java设计模式自我总结---适配器模式
上一篇博客说完了 java 23 中设计模式中的五种 创建性模式,由于篇幅过长,新开一贴今天开始学习结构型模式,
结构型模式包括以下七种:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
这里有也很详细的讲解:适配器模式
先学习适配器模式,适配器模式包括,类的适配器模式,对象的适配器模式,接口的适配器模式;其中对象的适配器模式是各种构造型模式的起源,见下图:
一:适配器模式
定义:将一个类的接口转换为用户希望的另一个接口,适配器模式让一些接口不兼容的类可以一起工作
适配器配器就是一种适配中间件,他存在于不匹配的二者之间,用于连接二者,将不匹配不匹配变得匹配,简单点理解就是平常缩减的转接头,转换器之类的等
适配器模式有如下三种:类适配器、对象适配器、接口适配器;
类适配器和对象适配器区别不大没作用一样,接口适配器差别较大。
1:类适配器模式
当我们在接口A中没有我们想要的方法,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器P来进行中转,这个适配器P要实现我们访问的接口A,这样我们就能继续访问当前接口A中的方法(虽然我们目前还不需要),然后在继承接口B的实现类BB,这样我们就可以在适配器P中访问接口B的方法了,这是我们在适配器P中的接口A方法中直接饮用BB中的方法,这样就完成了一个简单的类适配器。
不是自己的话还是不好理解,这里我又翻了几篇博客以加深理解,https://www.cnblogs.com/yueguanguanyun/p/7985411.html ;
类适配器大致是:如果需要客户端需要实现一个接口,but,这个接口中的方法并不能满足你的全部需求,你还需要另外一个接口或类中的方法,这是我们就可以使用一个适配器在实现原接口的基础上去继承所需要添加接口的实现类或直接继承某个类。我看到这里的时候有点蒙蔽,不就是一个类需要两个接口的方法吗,直接实现两个借口不就好了吗?为什么要那么麻烦呢?搜遍全网也没遇见解答,看来是我的脑回路太奇葩了,不过既然有问题就得去疏通它,于是...写了两个接口试验一下,事实证明实践出真知。下面看一下代码就明白了:
/** * 接口 1 */ public interface Interface1 { void method1(); } /** * 接口 2 */ public interface Interface2 { void method2(); } /** * 实现类 直接实现接口1 和接口2的方法 */ public class Impl1 implements Interface1,Interface2 { @Override public void method1() { System.out.println("method1"); } @Override public void method2() { System.out.println("method2"); } } /** * 测试类 */ public class Test { public static void main(String[] args) { Interface1 inter1 = new Impl1(); Interface2 inter2 = new Impl1(); inter1.method1(); inter2.method2(); } }
看到测试类估计很多有疑问的小伙伴们都要恍然大悟了(或许很多小伙伴们本来就没有疑问),对的,一个实现类实现两个接口之后,用该接口的实例只能去调用属于该接口的方法。我们需要的是用一个接口的实例去调用其它接口的方法,于是适配器模式之类适配器闪亮登场:我还是先滚去写代码。。。
我有两个接口,业务上需要实现Source源接口,但是还需要target接口的方法,客户端通过Source接口实例可以调用两个接口的方法
/** * 源接口 */ public interface Source { void sourceMethod1(); } /** * 目标接口 */ public interface Target { void targetMethod(); }
我们既然想扩展Target接口的方法,那么我们先实现该接口
1 public class TargetImpl implements Target{ 2 @Override 3 public void targetMethod() { 4 System.out.println("Target 目标接口的方法"); 5 } 6 }
怎么让两个接口实现适配呢,这时就可以创建适配器类,先继承要扩展的目标接口的实现类,然后再实现我们本来需要实现的接口,(简单说即先继承再实现),在方法中调用目标接口的方法,因为继承了Target的实现类所以可以直接调用。代码如下
1 /** 2 * 类适配器 3 */ 4 public class Adapter extends TargetImpl implements Source{ 5 @Override 6 public void sourceMethod1() { 7 //调用目标接口的方法 8 targetMethod(); 9 } 10 }
测试类
/** * 测试类 */ public class Test { public static void main(String[] args) { Source source = new Adapter(); source.sourceMethod1(); } }
结果输出:Target 目标接口的方法
如果想要同时调用Source接口和Target接口的方法,需要在Source中添加一个和Target中方法一直的抽象方法,如下:
1 /** 2 * 源接口 3 */ 4 public interface Source { 5 void sourceMethod1(); 6 7 /*新接口的方法*/ 8 void targetMethod(); 9 }
适配器只需要实现源接口的方法
/** * 类适配器 */ public class Adapter extends TargetImpl implements Source{ @Override public void sourceMethod1() { System.out.println("Source 源接口的方法"); } }
测试类
/** * 测试类 */ public class Test { public static void main(String[] args) { Source source = new Adapter(); source.sourceMethod1(); source.targetMethod(); } }
输出结果:
Source 源接口的方法
Target 目标接口的方法
这样我们就在Source 接口中使用Target接口的方法;
总结:类适配器模式即是通过继承来实现适配器的功能;
2:对象适配器模式
对象适配器是指通过合成复用来实现适配器功能;基本思路与类适配器相同:这里在理一下哈,有一个源接口Source和目标接口Target,客户端必须实现Source接口的时候如何使用Source接口的实例调用Target接口的方法呢,类适配器其中我们使用了继承的方法继承了Target接口的实现类,对象适配器的基本思想差不多,只不过是把继承改为了在适配器中创建Target的实例变量,并为该变量创建构造器用于为该变量赋值。就可以通过实例变量来调用Target的方法了。结合代码看一下(代码纯属手敲,望支持):
老规矩先上两个接口:Source(接口),和Target接口
1 /** 2 * 源接口 3 */ 4 public interface Source { 5 /**Source接口的方法*/ 6 void sourceMethod(); 7 } 8 9 /** 10 * 目标接口 11 */ 12 public interface Target { 13 /**target接口的方法*/ 14 void targetMethod(); 15 }
我们想另外调用target接口,所以先给它来个实现类吧:
1 /** 2 * Target接口实现类 3 */ 4 public class TargetImpl implements Target { 5 @Override 6 public void targetMethod() { 7 System.out.println("Target接口的方法"); 8 } 9 }
对象适配器类:
package com.shipeiqi.duixiangshipeiqi; /** * 对象适配器 */ public class Adapter implements Source{ /* 1:对象适配器在适配器中实现必须的接口 * 2:把需要引用的接口作为适配器类的成员变量, * 3:创建带参构造方法 */ private Target target; public Adapter(Target target) { this.target = target; } @Override public void sourceMethod() { System.out.println("Source接口的方法"); } /** * 在这里调用目标接口的方法 */ @Override public void targetMethod() { target.targetMethod(); } }
测试类:
1 public class Test { 2 public static void main(String[] args) { 3 /**传入目标接口的实现类,给适配器中的成员变量赋值*/ 4 Source source = new Adapter(new TargetImpl()); 5 source.sourceMethod(); 6 source.targetMethod(); 7 } 8 }
输出结果:
Source接口的方法
Target接口的方法
总结:对象适配器模式即是通过合成复用来实现适配器的功能;
3:接口适配器模式
接口适配器模式也很好理解,在大部分情况下我们写的一个接口中会有很多很多很多的抽象方法,当我们写接口的实现类时必须要实现该接口的所有抽象方法,,而我们可能只需要其中的一个方法,这样就会使该实现类变得十分臃肿,从而造成了资源的浪费;为了解决这个问题,我们可以引用适配器模式,借助于一个抽象类(即适配器类),该抽象类实现了所有的接口,我们不和原始的接口打交道,只和该抽象类取得联系,我们写一个类继承该抽象类,然后实现需要的方法即可;代码如下:
原始接口
1 /** 2 * 有很多方法的接口 3 */ 4 public interface Source { 5 void method(); 6 void method1(); 7 void method2(); 8 void method3(); 9 void method4(); 10 void method5(); 11 void method6(); 12 }
抽象类实现接口的方法
1 /** 2 * 接口适配器 3 * 抽象类实现接口,实现接口的方法,然后自定义业务类只需要继承该抽象类,重写所需的方法即可 4 */ 5 public abstract class Adapter implements Source{ 6 public void method(){} 7 public void method1(){} 8 public void method2(){} 9 public void method3(){} 10 public void method4(){} 11 public void method5(){} 12 public void method6(){} 13 }
写个类继承抽象类,只重写需要的方法即可
public class Service extends Adapter { @Override public void method(){ System.out.println("这个是我需要的方法"); } }
测试类:
public class Test { public static void main(String[] args) { Service service = new Service(); service.method(); } }
结果:
这个是我需要的方法
4:总结
总的来说适配器模式还是比较容易理解的,下面说一下三种适配器模式的使用场景和优缺点:
类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Adapter类,持有原类的一个实例,在Adapter类的方法中,调用实例的方法就行。
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter,实现所有方法,我们写别的类的时候,继承抽象类即可。
适配器模式的优缺点:
优点:
1:将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无需修改原有结构。
2:增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一适配者类可以在多个不同的系统中复用。
3:灵活性和扩展性都非常好,通过使用配置文件,可以很方便的更换适配器,也可以在不修改原有代码的基础上 增加新的适配器,完全复合开闭原则。
缺点:
1:一次最多只能适配一个适配者类,不能同时适配多个适配者。
2:适配者类不能为最终类,在C#中不能为sealed类
3:目标抽象类只能为接口,不能为类,其使用有一定的局限性。