1. 适配器模式(Adapter)
1.1. 单向适配器模式
1.1.1. 概述
l 场景:有两个类
n Class A{ public void funA(){/*A代码 */ }}
n Class B{ public void funB(){/*B代码 */ }}
l 这两个模块本来没有任何关系,但是应用中需要凡是以后A的对象调用了funA方法,一定要同时调用B的funB方法,怎么办?不能改变源代码
l 这个问题的类比:贵公司开发了一套图像应用软件,其中一个功能是可以把图像进行清晰化。但是这套软件运行一段时间之后,大家发现将图片清晰化之后然后进行一下去噪声可能会更好。于是贵公司又开发了一个专门去噪声的类,现在问题来了,怎么样把这个去噪声的类接到你原来的软件上?不改变源代码。
1.1.2. 解决办法:使用单向适配器模式
适配器模式分为单向适配器模式,如果将B接到A上用,这叫作单向适配器模式,A主动,B被动。单向适配器模式要点:继承方法。
代码如下:
class A{ public void funA(){ System.out.println("图像清晰化"); } } class B{ public void funB(){ System.out.println("图像去噪声老算法"); } } class SubA extends A{//将A进行扩展,以后将SubA当成A来用 public void funA(){ super.funA();//值得一提的是,这句代码可以去掉,如果去掉,就是功能替换 B b=new B(); b. funB(); } }
public class Adapter1{//客户端,最终使用方 public static void main(String args[]){ SubA subA = new SubA(); subA.funA(); } } |
在上面代码中,SubA 中继承了A,这样SubA就拥有A的所有功能,重写funA方法,把B对象的funB方法加进来,但在生产B对象时,直接使用new方法,耦合性太强,最好面对接口编程,是B对象的生成是由客户但传入进来的。修改代码如下:
class A{ public void funA(){ System.out.println("图像清晰化"); } }
interface IEM{//定义一个接口,管理所有的扩展模块(被动方模块) public abstract void extendFun(); }
class B implements IEM{ public void extendFun(){ System.out.println("图像去噪声老算法"); } }
class C implements IEM{ public void extendFun(){ System.out.println("图像去噪声新算法"); } }
class SubA extends A{//将A进行扩展,以后将SubA当成A来用 private IEM iem; //完全面向接口编程,可以将IEM的任何实现类对象接入(可以用Spring管理) public SubA(IEM iem){ this.iem = iem; } public void funA(){ super.funA();//值得一提的是,这句代码可以去掉,如果去掉,就是功能替换 iem.extendFun(); } }
public class Adapter1{//客户端,最终使用方 public static void main(String args[]){ C c = new C(); SubA subA = new SubA(c); subA.funA(); } } |
1.1.3. 单向适配器要点
l 注意:分清主动和被动方,主动方称为Adaptor,被动方称为Adaptee
l 定义一个类SubAdaptor,继承Adaptor类,重写funA方法
l 在这个类的构造函数可以传入Adaptee的引用
l 在SubAdaptor当成Adaptor来使用
l Adaptor a = new SubAdaptor(new Adaptee());
l 一种更好的方法:用接口,spring管理Adaptee
1.2. 双向适配器
1.2.1. 概述
l 代码:
Class A{ public void funA1(){ /* A1代码 */ } public void funA2(){/* A2代码 */} } Class B{ public void funB1(){ /* B1代码 */ } public void funB2(){/* B2代码 */} } |
l 应用需要凡是以后A的对象调用了funA1方法,一定要同时调用B的funB1方法;B的对象调用了funB2方法,一定要同时调用A的funA2方法;怎么办?不能改变源代码
1.2.2. 解决办法:使用双向适配器模式
双向适配模式是使用组合法将两个对象的方法进行组合。例如:
class A { public void funA1(){ System.out.println("A.funA1"); } public void funA2(){ System.out.println("A.funA2"); } } class B { public void funB1(){ System.out.println("B.funB1"); } public void funB2(){ System.out.println("B.funB2"); } }
class AB {//组合A和B,将AB当成A和B来使用 private A a; private B b; public void setA(A a){ this.a = a; } public void setB(B b){ this.b = b; } ///功能编写 public void funA1(){ a.funA1();//这句话如果去掉,就是功能替换 b.funB1(); } public void funB2(){ b.funB2(); a.funA2(); } }
public class Adapter2{//客户端,最终使用方 public static void main(String args[]){ AB ab = new AB(); ab.setA(new A()); ab.setB(new B()); ab.funA1(); ab.funB2(); } } |
以上代码没有使用面向接口编写,修改如下:
interface IA{ public abstract void funA1(); public abstract void funA2(); } class A implements IA{ public void funA1(){ System.out.println("A.funA1"); } public void funA2(){ System.out.println("A.funA2"); } }
interface IB{ public abstract void funB1(); public abstract void funB2(); } class B implements IB{ public void funB1(){ System.out.println("B.funB1"); } public void funB2(){ System.out.println("B.funB2"); } }
class AB implements IA,IB{//组合A和B,将AB当成A和B来使用 private IA ia; private IB ib; public void setIa(IA ia){//可以通过Spring框架来进行装配 this.ia = ia; } public void setIb(IB ib){ this.ib = ib; } ///功能编写 public void funA1(){ ia.funA1();//这句话如果去掉,就是功能替换 ib.funB1(); } public void funB2(){ ib.funB2(); ia.funA2(); } //重写其他功能,以免丢失 public void funA2(){ ia.funA2(); } public void funB1(){ ib.funB1(); } }
public class Adapter2{//客户端,最终使用方 public static void main(String args[]){ AB ab = new AB(); ab.setIa(new A()); ab.setIb(new B()); ab.funA1(); ab.funB2(); } } |
1.2.3. 双向适配器模式要点
l 因为不允许多重继承,用接口将两个类以及相关方法管理起来
l 编写第三个类,在构造函数传入前两个类的引用,并实现接口,重写函数
l 在函数里面进行调用
l 第三个类既可以当A,又可以当B来使用
1.2.4. 双向适配器练习
l 练习:有一个Frame1,上有一个按钮,点击按钮能让Frame1变红;另外有一个Frame2上有一个按钮,点击能将其标题改为China。
l 现在要求:这两个Frame在开发的时候并不知道将要调用外界的类,将这两个类合起来,达到:点击Frame1内的按钮,出现Frame2,能将Frame2的标题改为China,点击Frame2内的按钮,将Frame1变红。不改变Frame1,2源代码
1.3. 适配器小结
把一个类的接口变换成客户端所期待的另一种接口,从而是原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配器可以根据参数返还一个合适的实例给客户端。