今天这篇文章,我们来讲将设计模式中的“Adapter模式”,中文就是“适配器模式”。先说说一个生活中适配器模式的案例,有助于理解。现在有一个100伏特的交流电源,我现在想给笔记本充电,但是笔记本只能用12伏特的直流电,那我们是不是不能用这个电源进行充电了?直接充肯定不行,但是我们可以有一个充电器,这个充电器的功能是将100伏特的交流电转化成12伏特的直流电给电脑用。请注意,这句话很重要:“电脑并不知道充电器内部做了些什么,他只知道,可以用”。
现在运用到编码中来其主要特征就是:已经存在稳定功能的类的时候,新的需求需要去用这个类中所有或者部分功能,或者除此类的功能之外还得加新的功能,那Adapter模式就派上用场了。我们先看代码,然后再来说说细节:
现在我已经存在一个稳定的类Banner,并且已经测试稳定了,这个类不会再改动。这个类中有两个方法,showWithParen打印出一句话用括弧包起来,还有一个showWithAster打印一句话用星号"*"包起来。现在我有一个需求需要用到这两个方法,需要我去设计一个框架结构来完美的实现。
传统的编程来说很简单,直接new一个Banner对象,调用这里面的方法就行了。如果我需要更多的功能,那就增加一个类,在新的类中增加别的功能就行了。这种方式那就是面向实现编程了,类和类之间出现强耦合问题,一个类需要修改,相关的类都得改。如果代码出现问题,我们去维护的时候,就需要更改很多个类,并且还不好追踪问题所在。这就不好了,所以才会有了我们的设计模式登场嘛!我们理论的话不多说,往下看!
/**
* 本案例中,该类是一个早就已经功能稳定的类,现
* 在在新的版本或者需求中要用到这里面的方法
* Created by PICO-USER on 2017/3/9.
*/
public class Banner {
private String string;
public Banner(String string) {
this.string = string;
}
public void showWithParen() {
System.out.print("( " + string + " )\n");
}
public void showWithAster() {
System.out.print("* " + string + " *\n");
}
}
现在这个稳定的Banner类已经存在了,现在我需要定义一个接口Print,这个接口中有两个方法showWeak和showStrong,这两个方法只是表明了行为,显示两句话。具体是怎么实现,得看实现它的类,怎么去实现了。
/**
* Created by PICO-USER on 2017/3/9.
*/
public interface Print {
public abstract void showWeak();
public abstract void showStrong();
}
接着我定义一个类PointBanner,继承了Banner,并且实现了Print接口,很简单,调用了父类的构造方法,和showWithParen方法和showWithAster方法。
/**
* Created by PICO-USER on 2017/3/9.
*/
public class PrintBanner extends Banner implements Print {
public PrintBanner(String string) {
super(string);
}
@Override
public void showWeak() {
showWithParen();
}
@Override
public void showStrong() {
showWithAster();
}
}
现在来看AdapterRun类的调用代码,和实现功能。
public class AdapterRun {
public static void main(String[] args0) {
Print print = new PrintBanner("Hello World !");
print.showWeak();
print.showStrong();
}
}
最后的输出结果很简单:
( Hello World ! )
* Hello World ! *
在上面做了这么多事儿,最后就输出了两句话,有网友估计会说,无缘无故的你增加一个Print接口,你这不是更麻烦了吗?其实不然,代码上是多了一些,但是这就将面向实现编程转换成了面向接口编程。面向接口编程,代码量肯定会增加,但是代码的可读性,维护性,灵活性,可拓展性毫无疑问是大大增强的。具体的我们继续往下看。
细心的网友应该能发现,在AdapterRun类中,我们使用了Print类型来保存了PrintBanner类型的实例。为什么这样做呢?一般的写法是这样的PrintBanner print = new PrintBanner("Hello World !"),我们暂且成这种方式为方式2,示例代码中的方式我们称为方式1。方式1中是完全是使用Print接口调用showWeak方法和showStrong方法,因为是用print去调用的方法,那么你是不能调用showWithParena方法和showWithAster方法的。因为Print跟Banner是两个完全不相干的东西。如果不是按照方式1而是按照方式2,那就是用PrintBanner类去调用方法,这性质就完全变了,因为PrintBanner继承了Banner类,所以这种方式是可以直接调用showWithParen方法和showWithAster方法的。最重要的问题是:我们按着Ctrl点击该方法,方式1跳转到的是Print接口,方式2是跳转的PrintBanner类。问题这就出来了,方式1的使用和接口Print的使用完全将PrintBanner类和Banner类中的方法隐藏起来了,没有暴露出来。AdapterRun类完全不知道PrintBanner类和Banner中方法的实现,他也不需要关心,只需要用就行,这就弱化了PrintBanner类和AdapterRun类的耦合性,当需求更改的时候,我们可以在完全不需要更改AdapterRun类的情况下修改PrintBanner类来实现我们的新需求。
上面是实现接口的方式实现Adapter模式,其实我们也可以用委托的方式来实现Adapter模式。委托就是把这个功能让某人代替去执行,直接看代码:AdapterRun类和Banner类不需要动,只需要把Print接口改成类,然后稍微修改PrintBanner类即可。
/**
* Created by PICO-USER on 2017/3/9.
*/
public abstract class Print {
public abstract void showWeak();
public abstract void showStrong();
}
/**
* Created by PICO-USER on 2017/3/9.
*/
public class PrintBanner extends Print {
private Banner banner;
public PrintBanner(String string) {
this.banner = new Banner(string);
}
@Override
public void showWeak() {
banner.showWithParen();
}
@Override
public void showStrong() {
banner.showWithAster();
}
思想都是一样的,面向接口编程和面向抽象类编程其实都是面向抽象编程,都一样的。