1、什么是适配器模式?
适配器如同一个常见的变压器,也如同电脑的变压器和插线板之间的电源连接线,他们虽然都是3相的,但是电脑后面的插孔却不能直接插到插线板上。
如果想让额定工作电压是直流12伏特的笔记本电脑在交流100伏特”的AC电源下工作,应该怎么做呢?通常,我们会使用AC适配器,将家庭用的交流100伏特电压转换成我们所需要的直流12伏特电压。这就是适配器的工作,它位于实际情况与需求之间,填补两者之间的差异。适配器的英文是Adapter,意思是.....相互适合的东西”。前面说的AC适配器的作用就是让工作于直流12伏特环境的笔记本电脑适合于交流100伏特的环境(如下图)。
在程序世界中,经常会存在现有的程序无法直接使用,需要做适当的变换之后才能使用的情况。这种用于填补“现有的程序”和“所需的程序”之间差异的设计模式就是Adapter模式。Adapter模式也被称为Wrapper模式。Wrapper有 “包装器”的意思,就像用精美的包装纸将普通商品包装成礼物那样,替我们把某样东西包起来,使其能够用于其他用途的东西就被称为“包装器”或是“适配器”。
Adapter模式有以下两种。
●类适配器模式(使用继承的适配器) ●对象适配器模式(使用委托的适配器)
本章将依次学习这两种Adapter模式。
2、适配器样例
首先定义一个Banner类,比如就是我们实际的情况.
package cn.design.adapter; /**
* @author lin
* @version 1.0
* @date 2020-07-14 11:21
* @Description TODO
*/
public class Banner { private String string; public Banner(String string) {
this.string = string;
} public void showWithParen() {
System.out.println("(" + string + ")");
} public void showWithAster() {
System.out.println("*" + string + "*");
}
}
定义一个Print接口, 是需求的接口
package cn.design.adapter; /**
* @author lin
* @version 1.0
* @date 2020-07-14 11:24
* @Description TODO
*/
public interface Print {
public abstract void printWeak(); public abstract void printStrong(); }
2.1、类适配器模式
定义一个PrintBanner类,扮演适配器的角色 它继承Banner并且实现Print接口.
package cn.design.adapter; /**
* @author lin
* @version 1.0
* @date 2020-07-14 11:25
* @Description TODO
*/
public class PrintBanner extends Banner implements Print { public PrintBanner(String string) {
super(string);
} @Override
public void printWeak() {
showWithParen();
} @Override
public void printStrong() {
showWithAster();
}
}
Main测试类
package cn.design.adapter; /**
* @author lin
* @version 1.0
* @date 2020-07-14 11:26
* @Description TODO
*/
public class Main1 {
public static void main(String[] args) {
PrintBanner p1 = new PrintBanner("hello world");
p1.printWeak();
p1.printStrong();
}
}
运行结果如下:
(hello world)
*hello world*
2.2、对象适配器模式
定义一个BasePrint抽象类
package cn.design.adapter; /**
* @author lin
* @version 1.0
* @date 2020-07-14 11:24
* @Description TODO
*/
public abstract class BasePrint {
public abstract void printWeak(); public abstract void printStrong(); }
定义一个PrintBanner2类,它继承BasePrint
package cn.design.adapter; /**
* @author lin
* @version 1.0
* @date 2020/7/14 22:10
* @Description TODO
*/
public class PrintBanner2 extends BasePrint {
Banner banner; public PrintBanner2(String string) {
this.banner = new Banner(string);
} @Override
public void printWeak() {
banner.showWithParen();
} @Override
public void printStrong() {
banner.showWithAster();
}
}
定义Main2测试类
package cn.design.adapter; /**
* @author lin
* @version 1.0
* @date 2020/7/14 22:11
* @Description TODO
*/
public class Main2 {
public static void main(String[] args) {
PrintBanner2 p2 = new PrintBanner2(" main2 ");
p2.printWeak();
p2.printStrong();
}
}
运行结果如下:
( main2 )
* main2 *
3、适配器的登场角色
类适配器模式:
对象适配器模式
◆Target(对象)
该角色负责定义所需的方法。以本章开头的例子来说,即让笔记本电脑正常工作所需的直流12伏特电源。在示例程序中,由Print接口(使用继承时)和Print类(使用委托时)扮演此角色。
◆Client (请求者)
该角色负责使用Target 角色所定义的方法进行具体处理。以本章开头的例子来说,即直流12伏特电源所驱动的笔记本电脑。在示例程序中,由Main类扮演此角色。
◆Adaptee (被适配)
注意不是Adapt-er (适配)角色,而是Adapt-ee (被适配)角色。Adaptee是-一个持有既定方法的角色。以本章开头的例子来说,即交流100伏特电源。在示例程序中,由Banner类扮演此角色。如果Adaptee角色中的方法与Target角色的方法相同(也就是说家庭使用的电压就是12伏特直流电压),就不需要接下来的Adapter角色了。
◆Adapter (适配)
Adapter模式的主人公。使用Adaptee角色的方法来满足Target 角色的需求,这是Adapter 模式的目的,也是Adapter角色的作用。以本章开头的例子来说,Adapter 角色就是将交流100伏特电压转换为直流12伏特电压的适配器。在示例程序中,由PrintBanner类扮演这个角色。在类适配器模式中,Adapter角色通过继承来使用Adaptee角色,而在对象适配器模式中,Adapter角色通过委托来使用Adaptee角色。
4、什么时候使用适配器?
一定会有读者认为“如果某个方法就是我们所需要的方法,那么直接在程序中使用不就可以了吗?为什么还要考虑使用Adapter模式呢?”那么,究竟应当在什么时候使用Adapter模式呢?
很多时候,我们并非从零开始编程,经常会用到现有的类。特别是当现有的类已经被充分测试过了,Bug很少,而且已经被用于其他软件之中时,我们更愿意将这些类作为组件重复利用。
Adapter模式会对现有的类进行适配,生成新的类。通过该模式可以很方便地创建我们需要的方法群。当出现Bug时,由于我们很明确地知道Bug不在现有的类( Adaptee角色)中,所以只需调查扮演Adapter角色的类即可。这样一来, 代码问题的排查就会变得非常简单。
5、总结
个人经验
如何做到一个类不被实例化或者不被轻易实例化?
1.把一个类定义为抽象类; 2.把一个类的构造方法设置为:private类型的,这样在客户端就不能通过new ClassName()方法来轻易将一个类实例化,而要生成此类的实例就必须通过一个特殊的方法,这样在一个系统中,对此类的使用就能得到合理的控制(如:单例模式/多例模式/简单工厂方法等模式)。 3. 对于两个独立的系统,要满足ocp原则,则适配器模式会有一定的局限性。
发哥讲
如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~
● 扫码关注公众号