结构型设计模式(适配器/桥接/过滤器/组合/装饰器/外观/享元/代理)

白昼怎懂夜的黑 提交于 2020-05-07 23:58:23

前言

结构型模式:这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。

  1. 适配器模式(Adapter Pattern)
  2. 桥接模式(Bridge Pattern)
  3. 过滤器模式(Filter、Criteria Pattern)
  4. 组合模式(Composite Pattern)
  5. 装饰器模式(Decorator Pattern)
  6. 外观模式(Facade Pattern)
  7. 享元模式(Flyweight Pattern)
  8. 代理模式(Proxy Pattern)

1. 适配器模式

将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。 我们用图来形象的解释这一概念:

这里的重点在于,老接口有特殊功能,我们不忍舍弃,比如只有欧洲的插头(老接口)能从插座里获得电,没有电我们过不下去。这个功能我们无法舍弃 为了继续享受这个特殊功能,我们使用适配器模式,让我们手中的美国的插头(新接口),拥有老接口的能力。

1.1 Demo

例如一个美国人说英语,一个中国人说中文,为了跟美国人做生意,说英文这个功能我们无法舍弃,但是我们是中国人,天生不会说英文。于是两者想要交流,就需要一个适配器,来充当沟通两者的工作。现在,我们希望让一个能说中国话的个体(实现说中文的接口的类),开口说英文。

1.1.1 角色

  • 目标接口(Target)(中文沟通能力的接口):定义了我们希望用来承载被适配类特殊功能的方法的接口,比如例子中,我们希望用中国人的说话方法,承载美国人的speak功能,说话方法就定义在该接口中。
  • 目标类(Target class)(中国人类):我们所期待的拥有特殊功能的具体类,也就是要说英文的类。
  • 需要适配的类(Adaptee)(美国人类):需要适配的类或适配者类,类中还有我们还不忍舍弃的功能,我们需要迂回实现他
  • 适配器(Adapter)(翻译类):通过包装一个需要适配的对象,把原接口转换成目标接口。

1.1.2 类适配器

适配器有两种主要的实现,我们先看第一种——类适配器

// 被适配类,已存在的、具有还有用处的特殊功能、但不符合我们既有的标准接口的类 
//——本例中即为一个会说f**k的美国人(你可以看作这个美国人实现了说英文的接口,不过这个无关紧要,省略),他说的话我们听不懂
class American{  
    public void speak() {  
        System.out.println("f**k");  
    }  
}  
// 目标接口,或称为标准接口  ——这里是一个说中文能力的接口,他定义了方法“说话”。
interface SpeakChinese {  
    public void shuoHua();  
} 
// 具体目标类,只提供普通功能 ——这里我们的具体实现是一个中国人类,他实现了说中国话的接口
class Chinese implements SpeakChinese {  
    public void shuoHua() {  
        System.out.println("敲里吗");  
    }  
}  
// 适配器类,继承了被适配类,同时实现标准接口  ——现在我们觉得,那个美国人说的四字真言好拽哦,我也要学会,于是我们定义了适配器
class Adapter extends American implements SpeakChinese {  
    public void shuoHua() {  
        super.speak();  
    }  
}  

开始测试 现在我们定义一个laoWang,老王是一个Chinese,他会说话这个方法。 然后再定义一个适配器,看看适配器能不能用中文的说话方法,说出英文来。

// 测试类public class Client {  
    public static void main(String[] args) {  
        // 使用普通功能类  
        SpeakChinese laoWang= new Chinese();  
        laoWang.shuoHua();  
          
        // 使用特殊功能类,即适配类  
        SpeakChinese adapter = new Adapter();  
        adapter.shuoHua();  
    }  
}  

测试结果:

敲里吗
f**k

很棒,现在用了适配器,适配器用说中文的方式,说出了这句著名的英文,现在我们迂回得到了说这四个字母的能力,可以去找外国友人交流感情了。

1.1.3 对象适配器

另外一种适配器模式是对象适配器,它不是使用多继承或继承再实现的方式,而是使用直接关联,或者称为委托的方式。

其他目标类和被适配类都一样,就是适配器类的定义方式有所不同:

// 适配器类,直接关联被适配类,同时实现标准接口  
class Adapter implements SpeakChinese {  
    // 直接关联被适配类  
    private American american;  
      
    // 可以通过构造函数传入具体需要适配的被适配类对象  
    public Adapter (American american) {  
        this.american = american;  
    }  
      
    public void shuoHua() {  
        // 这里是使用委托的方式完成特殊功能  
        this.american.speak();  
    }  
}  

开始测试 在这里,我们为了更灵活一点,定义了一个加州人,加州人和外面那些妖艳贱货不一样,他们比较优雅,一般喜欢说hello。

class Californian extends American{  
    public void speak() {  
        System.out.println("hello");  
    }  
} 
public class Client {  
    public static void main(String[] args) {  
        // 使用普通功能类  
        SpeakChinese laoWang = new Chinese();  
        laoWang.shuoHua();  
          
        // 使用特殊功能类,即适配类,  
        // 需要先创建一个被适配类的对象作为参数  
        American tom = new Californian(){}
        Target adapter = new Adapter(tom);  
        adapter.shuoHua();  
    }  
}  

测试结果

敲里吗
hello

同样的,我们用适配器获得了像加州人那样优雅的说英文的能力。对象适配器相对于类适配器来说比较灵活,我们可以复用这个适配器,通过传参的不同,得到德克萨斯人,弗吉尼亚人,佛罗里达人的说话方式。

1.2 总结

1.2.1 优劣

  • 优点

    • 通过适配器,客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单、更直接、更紧凑。
    • 复用了现存的类,解决了现存类和复用环境要求不一致的问题。
    • 将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
    • 一个对象适配器可以把多个不同的适配者类适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
  • 缺点

    • 过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!