Java设计模式之结构型模式

结构型模式

适配器模式【Adapter】

【学习难度:★★☆☆☆,使用频率:★★★★☆】

定义:适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。

(1)类的适配(拓展、修改目标类的方法)

类的适配

通过继承和接口实现:继承原始类的方法,接口来实现新的方法。

(2)对象的适配(拓展、修改目标对象的使用方法)

对象的适配

持有原始类的对象引用和接口实现:原始对象的方法通过引用该对象调用来实现,接口实现新的方法。

(3)接口适配(解决接口定义方法过多,有些接口方法不需要实现的问题)

接口的适配

定义一个抽象类来实现目标接口的方法(空方法),再继承这个抽象类,要实现哪个方法,就重写哪个方法。

三种适配的使用场景:

类的适配器模式:

当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。

对象的适配器模式:

当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。

接口的适配器模式:

当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

注意:适配器模式不适合在详细设计阶段使用它,它是一种补偿模式,专用来在系统后期扩展、修改时所用。


装饰模式【Decorator】

【学习难度:★★★☆☆,使用频率:★★★☆☆】

定义:装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

装饰模式

ITarget类是被装饰的接口,Decorator类是一个装饰类,可以为Target类动态的添加一些功能。

从上面可知,装饰模式和对象的适配模式很像,都是为了拓展对象的方法。但是他们有显著的不同:

(1)持有对象不同:

装饰模式,装饰者持有的是被装饰类型的接口。

对象的适配模式,适配器持有的是具体需要适配的对象。

(2)拓展方式不同:

装饰模式,并不增加被装饰对象的方法数,只是增强被装饰对象的方法或者功能。

对象的适配模式,增加了适配对象的方法数。

(3)目的不同:

装饰模式,是在不改变接口的前提下,加入新的责任或功能方法,增强功能。

对象的适配模式,是增加接口来适配原先的方法【使用新接口代替原先的接口,本质上是接口替换】。


代理模式【Proxy】【

学习难度:★★★☆☆,使用频率:★★★★☆】

定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

(1)静态代理

静态代理

在图中,IProxy是抽象角色,Proxy是代理角色,NJProxy、BJProxy是真实角色。

静态代理和装饰模式很像,都可以对代理/装饰的对象的方法进行修改。但是他们有显著的不同:

(1)装饰模式,装饰者在构造的时候需要传入被装饰的接口。

代理模式,代理者在构造的时候并不需要传入代理的接口。

(2)装饰模式,主要的目的是增强被装饰者的方法或功能。

代理模式,主要的目的是对代理访问对象进行控制,被代理的对象方法和功能并没有增强。

【对代理访问对象进行控制的理解】

比如马云擅长卖货,那么他适合代理阿里巴巴的运转;马化腾擅长社交,那么他适合代理腾讯的运转。

如果是使用装饰模式的话,那么马云既可以运转阿里巴巴,也可以运转腾讯,那么就乱套了。。。

使用案例:网络请求代理

(2)动态代理

动态代理

和静态代理不同,动态代理不是实现具体的代理接口,而是要实现InvocationHandler接口,在接口里调用代理的接口实现动态代理。

当我们需要代理的接口数量过多,且不需要对方法进行具体的处理的话,就可以使用动态代理。

使用案例:网络请求代理,Retrofit等


外观模式【Facade】

【学习难度:★☆☆☆☆,使用频率:★★★★★】

定义:为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使得这一子系统更加容易使用。

(解决相关联类与类之间的依赖关系,将他们的依赖关系放到一个外观类中,可降低系统的复杂度)


外观模式

一幅图解外观模式:

外观模式

在这里,电脑Computer充当外观角色,它的各部分组件就充当子系统角色,而,User就充当了客户的角色。

Facade模式有下面一些优点:

1)对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易。通过引入外观模式,客户代码将变得很简单,与之关联的对象也很少。

2)实现了子系统与客户之间的松耦合关系,这使得子系统的组件变化不会影响到调用它的客户类,只需要调整外观类即可。

3)降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。

4)只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类。

Facade模式的缺点:

1) 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。

2) 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。拓展性比较差

由于外观模式提供了一个访问子系统的统一入口,常见的和单例模式组合使用比较多。

我们平时将一个非常复杂的方法分成多个私有子方法,再由一个统一的公用方法提供给外部,使用的就是外观模式。


桥接模式【Bridge】

【学习难度:★★★☆☆,使用频率:★★★☆☆】

定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。

桥接模式

桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。

桥接模式的本质是:分离抽象和实现。只有将两者分离,它们才能独立地变化,也只有两者可以相对独立地变化时,系统才会有更好的可扩展性和可维护性。桥接模式很好地遵循了开闭原则,也较好地体现了Favor Composition Over Inheritance(优先使用对象组合/聚合原则)。


组合模式【Composite】

【学习难度:★★★☆☆,使用频率:★★★★☆】

定义:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 "组合对象"的含义。

组合模式

图中Leaf叫做叶子对象(单个对象),Composite叫做容器对象(组合对象)。

组合模式又可分为透明模式和安全模式:

透明方式:在Component中声明所有来管理子对象的方法,其中包括Add,Remove等。这样实现Component接口的所有子类都具备了Add和Remove方法。这样做的好处是叶节点和枝节点对于外界没有区别,它们具备完全一致的接口。

安全方式:在Component中不去声明Add和Remove方法,那么子类的Leaf就不需要实现它,而是在Composit声明所有用来管理子类对象的方法。

适用场景

(1)当想表达对象的部分-整体的层次结构时

(2)希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象时。

举例说明:

Component就好比一家知名美容美发店,在它旗下有加盟店(Leaf),也有自己的总店和分店(Composite),但是对于顾客来说,如果拥有这家店的会员卡,按道理来说,我无论是在加盟店还是总店、分店,对于使用应该是没有任何差异的,这个时候就需要使用组合模式。

同样的,一家大的跨国公司也可以使用组合模式来表明其公司结构。其结构就类似二叉树结构。

使用案例:Android的View和ViewGroup


享元模式【Flyweight】

【学习难度:★★★★☆,使用频率:★☆☆☆☆】

定义:享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

享元模式

使用场景:

1、当我们发现某个类型的对象有大量的实例,且他们功能相似时。

2、我们发现通过使用享元模式后能够提高系统的性能和不会带来更多的复杂度时。

使用案例:数据库连接池、线程池、RxBus


项目源码

Github项目源码地址:https://github.com/xuexiangjys/architect-java/tree/master/src/designpattern/structure, 觉得有帮助的话记得给个star!~~

微信公众号

更多资讯内容,欢迎扫描关注我的个人微信公众号!

微信公众号

推荐阅读更多精彩内容