“朋友你好!”

“嗨,比拉博!”

“还有一些时间,我再告诉你三种模式。”

“还有三个?一共有多少个?”

“目前流行的模式有几十种,但《成功解法》的数量是无限的。”

“原来如此,所以我要学几十种纹路?”

“除非你有真正的编程经验,否则他们不会给你太多。”

“你最好多积累一点经验,然后,一年后,回到这个话题,试着更深入地理解它们。至少有几十种最流行的设计模式。”

“不利用别人的经验而是第 110 次发明某样东西是一种罪过。”

“我同意。”

“那我们开始吧。”

适配器(或包装器)模式

模式:适配器、代理、桥 - 1

“想象一下,你来到中国,发现电源插座遵循不同的标准。孔不是圆的,而是扁平的。在这种情况下,你需要一个适配器。”

“在编程中也会发生类似的事情。类在相似但不同的接口上运行。因此我们需要在它们之间建立一个适配器。”

“这是它的样子:”

例子
interface Time
{
 int getSeconds();
 int getMinutes();
 int getHours();
}

interface TotalTime
{
 int getTotalSeconds();
}

“假设我们有两个接口:Time 和 TotalTime。”

“Time 接口允许您使用getSeconds ()、  getMinutes () 和 getHours () 方法获取当前时间。”

TotalTime接口可让您获取从午夜到当前时刻经过的秒数。”

“如果我们有一个TotalTime对象,但我们需要一个Time对象,我们应该怎么做,反之亦然?”

“我们可以为此编写适配器类。例如:”

例子
class TotalTimeAdapter implements Time
{
 private TotalTime totalTime;
 public TotalTimeAdapter(TotalTime totalTime)
 {
  this.totalTime = totalTime;
 }

 int getSeconds()
 {
  return totalTime.getTotalSeconds() % 60; // seconds
 }

 int getMinutes()
 {
  return totalTime.getTotalSeconds() / 60; // minutes
 }

 int getHours()
 {
  return totalTime.getTotalSeconds() / (60 * 60); // hours
 }
}
用法
TotalTime totalTime = TimeManager.getCurrentTime();
Time time = new TotalTimeAdapter(totalTime);
System.out.println(time.getHours() + " : " + time.getMinutes () + " : " +time.getSeconds());

“还有另一个方向的适配器:”

例子
class TimeAdapter implements TotalTime
{
 private Time time;
 public TimeAdapter(Time time)
 {
  this.time = time;
 }

 int getTotalSeconds()
 {
  return time.getHours() * 60 * 60 + time.getMinutes() * 60 + time.getSeconds();
 }
}
用法
Time time = new Time();
TotalTime totalTime = new TimeAdapter(time);
System.out.println(time.getTotalSeconds());

“啊。我喜欢。但是有没有例子?”

“当然!例如,InputStreamReader 是一个经典的适配器。它将 InputStream 转换为 Reader。”

“有时这种模式也称为包装器,因为新类‘包装’了另一个对象。”

“你可以在这里读到一些其他有趣的东西。”

代理模式

"代理模式有点类似于包装模式。但它的目的不是转换接口,而是控制对代理类内部存储的原始对象的访问。而且,原始类和代理通常都具有相同的接口,这使得用代理对象替换原始类的对象变得更容易。”

“例如:”

真实类的接口
interface Bank
{
 public void setUserMoney(User user, double money);
 public int getUserMoney(User user);
}
原始类的实现
class CitiBank implements Bank
{
 public void setUserMoney(User user, double money)
 {
  UserDAO.updateMoney(user, money);
 }

 public int getUserMoney(User user)
 {
  return UserDAO.getMoney(user);
 }
}
代理类的实现
class BankSecurityProxy implements Bank
{
 private Bank bank;
 public BankSecurityProxy(Bank bank)
 {
  this.bank = bank;
 }
 public void setUserMoney(User user, double money)
 {
  if (!SecurityManager.authorize(user, BankAccounts.Manager))
  throw new SecurityException("User can’t change money value");

  bank.setUserMoney(user, money);
 }

 public int getUserMoney(User user)
 {
  if (!SecurityManager.authorize(user, BankAccounts.Manager))
  throw new SecurityException("User can’t get money value");

  return bank.getUserMoney(user);
 }
}

“在上面的示例中,我们描述了Bank接口和CitiBank类,它是该接口的一个实现。”

“该界面可让您获取或更改用户的帐户余额。”

然后我们创建了BankSecurityProxy,它也实现了Bank接口并存储了对不同 Bank 接口的引用。此类的方法检查用户是帐户所有者还是银行经理。如果不是,则抛出 SecurityException。”

“这是它在实践中的运作方式:”

没有安全检查的代码:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank.setUserMoney(user, 1000000);
带有安全检查的代码:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank = new BankSecurityProxy(bank);
bank.setUserMoney(user, 1000000);

“在第一个示例中,我们创建了一个银行对象并调用了它的setUserMoney方法。

“在第二个示例中,我们将原始银行对象包装在BankSecurityProxy对象中。它们具有相同的接口,因此后续代码继续按原样工作。但现在每次调用方法时都会执行安全检查。”

“凉爽的!”

“是的。你可以有很多这样的代理。例如,你可以添加另一个代理来检查帐户余额是否过大。银行经理可能会决定将大量资金存入自己的帐户并携带资金潜逃到古巴”

“更重要的是......所有这些对象链的创建都可以放入BankFactory类中,您可以在其中启用/禁用所需的对象。”

BufferedReader使用类似的原理工作。它是一个Reader,但它做了额外的工作。”

“这种方法可以让你‘组装’一个对象,它具有来自各种‘片段’的所需功能。”

“哦,我差点忘了。代理的使用比我刚才给你看的要广泛得多。你可以在这里阅读其他用途。”

桥梁模式

模式:适配器、代理、桥 - 2

“有时,当程序运行时,有必要显着改变对象的功能。例如,假设你有一个游戏,里面有一个驴角色,后来被一个法师变成了一条龙。龙有完全不同的行为和属性,但它是同一个对象!”

“我们不能只创建一个新对象并完成它吗?”

“不一定。假设你的驴子是一群角色的朋友,或者它受到了几个法术的影响,或者它参与了某些任务。换句话说,这个物体可能已经在很多地方被使用了——并链接到许多其他对象。因此在这种情况下,不能简单地创建一个新对象。”

“嗯,那有什么办法呢?”

“桥接模式是最成功的解决方案之一。”

“这种模式需要将一个对象分成两个对象:一个«接口对象»和一个«实现对象»。

“接口和实现它的类有什么区别?”

“有了一个接口和一个类,我们最终得到一个对象。但是在这里——我们有两个。看这个例子:”

例子
class User
{
 private UserImpl realUser;

 public User(UserImpl impl)
 {
  realUser = impl;
 }

 public void run() //Run
 {
  realUser.run();
 }

 public void fly() //Fly
 {
  realUser.fly();
 }
}

class UserImpl
{
 public void run()
 {
 }

 public void fly()
 {
 }
}

“然后你可以声明 UserImpl 的几个子类,例如UserDonkey(驴)和UserDragon(龙)。”

“尽管如此,我真的不明白这将如何运作。”

“好吧,像这样:”

例子
class User
{
 private UserImpl realUser;

 public User(UserImpl impl)
 {
  realUser = impl;
 }

 public void transformToDonkey()
 {
  realUser = new UserDonkeyImpl();
 }

 public void transformToDragon()
 {
  realUser = new UserDragonImpl();
 }
}
怎么运行的
User user = new User(new UserDonkey()); // Internally, we're a donkey
user.transformToDragon(); // Now we're a dragon internally

“所以它有点像代理。”

“是的,但在代理中,主要对象可以单独存储在某个地方,代码与代理一起工作。这里我们说每个人都使用主要对象,但它的部分在内部发生变化。”

“啊。谢谢。你能给我一个链接来阅读更多吗?”

“当然,阿米戈,我的朋友。给你:桥梁模式。”