zoukankan      html  css  js  c++  java
  • 设计模式 —— 结构型模式

    结构型模式(Structural Pattern)关注如何将现有类或对象组织在一起形成更加强大的结构
    可分为两种:

    1. 类结构型模式:关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系
    2. 对象结构型模式:关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。更符合“合成复用原则”

    1. 适配器模式(Adapter pattern)

    1.1 定义

    "Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces."
    将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作

    又称包装器(Wrapper),既可以作为类结构型模式,也可以作为对象结构型模式。

    使用前提或场景:解决两个已有接口间不兼容问题。Client面向接口编程,而该面向的接口又与第三方接口不兼容,两者又不便于修改,则可用Adapter协调工作

    个人理解:不能说是"转换",而是协调不兼容接口间工作。比如客户端期望调用的方法传参是一个样子(面向接口编程,该方法可上升为目标抽象类,下述角色中有提到),而现有第三方实现的满足业务功能的接口可用,但规定的参数格式不一样。但两者(目标抽象类与第三方接口)都不愿或者不能修改,因此,则需要一个适配器来协调两者工作。

    1.2 模式结构

    示例代码参考

    包含如下角色:

    1. Target(目标抽象类)
      客户期望的业务接口,可以是具体类,也可以是接口。

    2. Adapter(适配器类)
      适配器类可调用Adaptee接口,以对 Adaptee 和 Target 进行适配,使其协调工作。

    3. Adaptee(适配者类)
      被适配的角色,定义了可工作、已存在、待适配的接口,些情况下甚至没有源代码。

    4. Client(客户类)
      客户类面对目标抽象类进行编程

    可细分为两种模式:

    1. 类适配器模式:适配器类与适配者类是继承关系,因为Java不支持多重继承,因此该模式下目标抽象类只能是接口。
    2. 对象适配器:适配器类与适配者类是关联关系(也可以称为委派关系),即含有适配者类的成员变量

    1.3 模式扩展

    1. 缺省适配器模式(Default Adapter Pattern):当不需要实现接口提供的全部方法时,可先设计一个抽象类(缺省适配器)来实现该接口,并为每个方法提供一个默认实现(通常是空实现,也称钩子方法[Hook Method]),那么该抽象类的子类(具体业务类)可有选择的只覆盖父类中某些方法来实现需求。
    interface ServiceInterface{
    	void M1();
    	void M2();
    	void M3();
    }
    abstract class AbstractServiceClass implements ServiceInterface{
    	public void M1(){}
    	public void M2(){}
    	public void M3(){}
    }
    class ConcreteServiceClass extends AbstractServiceClass{
    	public void M2(){
    		System.out.println("具体业务方法");
    	}
    }
    
    1. 双向适配器:适配器中同时包含对目标类和适配者类的引用。

    1.4 优缺点

    也是其使用场景,可以在不修改客户、适配者、目标抽象类前提下,让几者兼容工作。同时适配器也能很方便的替换,符合开闭原则。

    2. 桥接模式(Bridge Pattern)

    2.1 定义

    "Decouple an abstraction from its implementation so that the two can vary independently."
    将抽象部分与它的实现部分解耦,使它们都能独立地变化

    又称柄体(Handle and Body)模式,接口(Interface)模式,属于对象结构型模式。

    使用场景:当一个类存在两个独立变化的维度时,为了减少因继承结构带来的具体类数量,可将变化的维度进行抽象化,再用关联方式将其联系起来。

    个人理解:如上面所讲,多变化维度如果用继承将会大大增加类的数量。比如下面例子中的"跨平台多格式播放器",若将其中一个维度用继承关系表达,再在其中定义另一个维度接口的成员变量,以此达到灵活组合的目的。

    2.2 模式结构

    1. Abstraction(抽象类)
      其中一个变化维度的继承关系结构中的抽象父类,一般来说 Implementor 接口仅提供基本操作,而 Abstraction 则可能会做更多更复杂的操作。其中定义了一个 Implementor 类型对象,并维护该对象,达成关联关系.

    2. RefinedAbstraction(扩充抽象类)

    3. Implementor(实现类接口)
      另一变化维度所抽象出来的接口

    4. ConcreteImplementor(具体实现类)

    2.3 优点

    1. 通过将另一维度抽象,并使用关联关系,一定程度上进行了解耦,也 满足了"合成复用原则",大大减少了因静态抽象继承结构可能带来的类的数量。

    2. 因为抽象,客户端面向两个维度抽象层编程,加上新增扩充抽象类或具体实现类均不需要修改其他任何代码,因此很好的符合了"依赖倒转原则"与"开闭原则"。

    3. 组合模式(Composite Pattern)

    3.1 定义

    "Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly."
    组合多个对象形成树形结构以表示"部分——整体"的层次结构。组合模式使客户能统一对待单个对象(即叶子对象)和组合对象(即容器对象)

    又称"部分-整体"(Part-Whole)模式,属于对象的结构模式

    使用场景:在具有整体(容器)和部分(叶子)的类层次结构中,希望能忽略两者的差异,使客户能一致对待它们。

    个人理解:通过定义一个抽象构件类,既能代表叶子也能代表容器构件,其中声明了构件的统一业务方法,该方法叶子与容器有不同的实现,但客户面向该抽象构件编程,因此可以统一处理而无须关心两者间差异。

    3.2 模式结构

    1. Component(抽象构件)
      可以是接口或者抽象类,声明叶子构件与容器构件共有业务方法

    2. Leaf(叶子构件)

    3. Composite(容器构件)
      在容器构件中,包含了一个集合用于储存子结点,该子结点可以是叶子、也可以是容器对象。且额外提供了管理子结点的方法,如addLeaf(Component com)等等

    4. Client(客户类)

    :该模式又可细分为:透明组合模式、安全组合模式

    • 透明组合模式:即将容器类中的额外方法(如addLeaf),提到抽象构件类中,使得所有构建类都有相同的接口,客户可透明的完全一致地对待所有对象。
      缺点很大:因为叶子对象和容器对象本质上是有区别,叶子对象不可能有成员对象。因此add等方法对叶子类是没有意义的,在运行时调用会出错。

    • 安全组合模式(推荐):即没有在抽象构件类中声明本该容器具有的方法,这是安全的。但这会造成客户不能完全针对抽象编程(因为add等方法是定义在容器类中的,只有声明为容器类型才能调用)无法一致使用叶子与容器构件。

    4. 装饰模式(Decorator Pattern)

    4.1 定义

    "Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality."
    动态地给一个对象添加职责。相较于继承,装饰模式则提供了一种更为灵活的方式来扩展功能。

    又称"油漆工模式"、"包装器(Wrapper)"(与适配器模式别名相同,但含义不同),是对象结构型模式。

    使用场景:无须使用继承,通过关联机制来动态、透明的为对象增加职责。

    个人理解:定义抽象构件(类或接口)声明业务方法,具体构件与装饰类均实现该接口。在装饰类中定义了构件类型的成员变量,在接口方法中调用该对象的方法并添加额外实现。而对客户来说是一直的,因为其面向抽象编程。
    该模式也能达到循环装饰,来添加功能。

    4.2 模式结构

    1. Component(抽象构件)
      声明了业务方法,客户端面向该抽象编程

    2. ConcreteComponent(具体构件)
      实现了抽象构件

    3. Decorator(抽象装饰类)
      (可选)实现抽象构件。当有多个装饰类时,可声明抽象装饰类,用以定义所有装饰类的统一行为,如维护一个抽象构件类型的引用。假如只有一个装饰类时,则可省略

    4. ConcreteDecorator(具体装饰类)
      内部调用被装饰对象方法,并额外增添新的功能。

    :该模式可细分为透明装饰模式与半透明装饰模式。

    • 透明装饰模式:即装饰类中未额外添加public方法,客户端可完全一致的使用具体构建类与装饰类。
    • 半透明装饰模式:装饰类中增添了自己的方法,意味着客户端必须声明为装饰类型才可调用,因此无法一致对待。

    4.3 优点

      一定程度上降低了静态继承带来的耦合度,符合”合成复用原则“,同时抽象层的定义也符合”开闭原则“,比如新增具体装饰类无须修改其他代码。

    5. 外观模式(Facade Pattern)

    5.1 定义

    "Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use."
    为子系统的一组接口提供一个统一的入口。外观模式定义了一个高层接口使得能更方便地使用子系统。

    又称门面模式,是对象结构型模式

    使用场景:若想简化客户端与复杂子系统间的操作,则可引入外观角色来为复杂子系统提供简化的入口

    个人理解:如上所述,客户端只需与外观角色交互,而由外观角色来整合调用子系统中的复杂接口

    5.2 模式结构

    1. SubSystem(子系统角色)
      每个子系统都可由客户端直接调用,也可以被外观角色调用。子系统并不知道外观角色的存在,对子系统而言,外观角色仅仅是另一个客户端而已

    2. Facade(外观角色)
      它将从客户端发来的请求委派到相应子系统

    5.3 优点

      如同使用情景一样,使用外观模式可为客户端提供很大方便,将客户端与子系统的内部复杂性分隔开,只需与外观角色交互即可。降低了客户与子系统耦合度,符合"迪米特法则"。

    6.享元模式

    TODO

    7. 代理模式(Proxy Pattern / Surrogate Pattern)

    7.1 定义

    "Provide a surrogate or placeholder for another object to control access to it."
    为某一对象提供一个代理,由代理对象来控制对原对象的引用

    属于对象结构型模式

    使用场景及个人理解:为现有对象的使用提供额外的控制,而无须修改现有类与客户调用,对客户端而言是透明的,无须关心具体实现,只需面向接口使用即可。Java中官方自带有对动态代理的支持。

    和“装饰模式”的不同:(1)代理中的引用是对真实主题角色的引用,而装饰模式中是对抽象主题角色的引用。(2)装饰模式的被装饰对象是由客户传入的,想额外增加修饰功能。而代理模式是内部创建被代理对象,且完全控制其行为。

    7.2 模式结构

    1. Subject(抽象主题角色)
      定义了业务方法,是真实主题与代理主题的共同接口

    2. Proxy(代理主题角色)
      内部包含对真实主题角色的引用,可完全控制真实主题的使用逻辑

    3. RealSubject(真实主题角色)

    7.3 扩展

    常见应用:

    1. 远程代理:远程代理可以将网络细节隐藏起来,使客户端不必考虑网络的存在。比如Java的RMI(Remote Method Invocation,远程方法调用),客户对象在客户端运行,想服务器端运行的远程对象发起请求。
    2. 虚拟代理:一种时间换空间的内存节省技术,将占用大量内存或处理复杂的对象推迟到使用它时才创建。比如Hibernate的懒加载

    Java中的动态代理,以及优缺点

    TODO:一个链接

  • 相关阅读:
    springboot + websocket + qpid问题记录
    学习开源项目guns遇到的问题记录
    CSS选择器和jQuery选择器学习总结
    (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
    (深入.Net平台和C#编程)第九章.上机练习.20170417
    (深入.Net平台和C#编程)第八章.上机练习(网络电视精灵).20170415
    (深入.Net平台和C#编程)第十章.课程总复习.20170413
    (深入.Net平台和C#编程)第七章.上机练习.20170412
    (深入.Net平台和C#编程)第六章.简答题5.20170410
    (深入.Net平台和C#编程)第六章.简答题3.20170410
  • 原文地址:https://www.cnblogs.com/simpleito/p/10878865.html
Copyright © 2011-2022 走看看