设计模式(一)面向可维护性的,基于委派和继承的设计模式

1.    概述

(1)Creational patterns(创建型模式)

    a.    Factory method pattern(工厂方法模式)

    b.    Abstract factory pattern(抽象工厂模式)

    c.    Builder pattern(构造器模式)

(2)Structural patterns(结构型模式)

    a.    Bridge pattern(桥接模式)

    b.    Proxy pattern(代理模式)

    c.    Composite pattern(组合模式)

(3)Behavioral patterns(行为类模式)

    a.    Observer pattern

    b.    Visitor pattern

    c.    Mediator pattern

    d.    Command pattern

    e.    Chain of Responsibility(职责链模式)

    黑体部分将在以下详细介绍

2.    Creational patterns(创建型模式)

    (1)Factory method pattern(工厂方法模式)

        也被称为“Virtual Constructor”虚拟构造器

        当client不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体创建

        的实例时,用工厂方法。

        定义一个用于创建对象的接口,令其子类来决定实例化哪个类,从而使一个类的实例化

        延迟到其子类。

        设计模式(一)面向可维护性的,基于委派和继承的设计模式_第1张图片

        例如:trace类:

        使用工厂方法模式之前:

        abstract product:

        设计模式(一)面向可维护性的,基于委派和继承的设计模式_第2张图片

        Concrete product 1:

              设计模式(一)面向可维护性的,基于委派和继承的设计模式_第3张图片

        Concrete product 2:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第4张图片

        不采用方法直接创建:

        设计模式(一)面向可维护性的,基于委派和继承的设计模式_第5张图片

        后果是:客户端代码与产生product的代码聚合度较高

        我们使用工厂方法:定义工厂接口TraceFactory:

        设计模式(一)面向可维护性的,基于委派和继承的设计模式_第6张图片

        除此之外,还可以使用静态工厂方法:

        设计模式(一)面向可维护性的,基于委派和继承的设计模式_第7张图片

        优点:

        消除了应用程序与具体实现的捆绑。

        代码只处理Product接口,所以代码也可处理用户定义的ConcreteProduct

        潜在的缺点:

        用户端可能不得不新建一个Creator的子类,来产生ConcreteProduct

        客户端要定义的类有可能需要处理新的变化,而产生它的类不能继承Creator子类

    

    (2)Abstract factory pattern(抽象工厂模式)

        应用场景:

        a.    一个UI, 包含多个窗口控件,这些控件在不同的OS中实现不同 

        b.    一个仓库类,要控制多个设备,这些设备的制造商各有不同,控制接口有差异 

        抽象工厂模式:提供接口以创建一组相关/相互依赖的对象, 但不需要指明其具体类。   

例如: 

        设计模式(一)面向可维护性的,基于委派和继承的设计模式_第8张图片

其实现代码:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第9张图片     

另需要一个辅助类,其内方法以抽象工厂类的对象为参数,进行delegate:

并在客户端,根据要创建的“组合产品”的类型,构建某一具体工厂类,进而生产出整个产品族。

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第10张图片

注意:

Abstract Factory 创建的不是一个完整产品,而是“产品族”(遵循固定搭配规则的多类产品的实例),得到的结果是:多个不同产品的 object,各产品创建过程对client可见,但“搭配”不能改变。

本质上,Abstract Factory是把多类产品的factory method组合在一起

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第11张图片

直接用多个factory method,也能达到目的,但要详细表述各个组件的种类,更麻烦。

    对比:Abstract Factory vs Factory Method

    a.    Factory method 创建一个类型对象,而Abstract Factory创建多个类型的对象

    b.    Factory method 有一个factory方法,而Abstract Factory有多个factory方法

    c.    Abstract Factory 使用委派/组合,而Factory method使用继承/子类型


    (3)Builder pattern(构造器模式)

    用于创建包含多个组成部分的复杂对象。

    例如:你在麦当劳订套餐时:

    设计模式(一)面向可维护性的,基于委派和继承的设计模式_第12张图片

    client要的不是一堆零散的objects,而是一个完整的产品,client不关心细节组成与创建

    示意图如下:

    设计模式(一)面向可维护性的,基于委派和继承的设计模式_第13张图片

    使用一个director,在其construct方法中,调用Builder的各个buildXXX(),完成各部分构建,并组装成完整的商品。Builder中的getResult()方法,可获得商品实例。

    Builder为一抽象类,需要具体实现ConcreteBuilder。

    相当于使用委派的方式,与模板模式只差在返回组合产品,在Director完成赋值这一步。

    例如:要做一个Pizza,我们不需要考虑其面团(dough),酱汁(sauce)和佐料(topping)是怎样组成的,每一种pizza有一种特定的做法。

     先构建pizza类,即为product类,其part是以三个属性的形式体现的,其builder就相当于给三个属性赋值:

    设计模式(一)面向可维护性的,基于委派和继承的设计模式_第14张图片

    先创建一个PizzaBuilder(抽象类,其getResult实现,而三个buildXXX为抽象方法)

    注意:pizza是一个pizzaBuilder的属性

    设计模式(一)面向可维护性的,基于委派和继承的设计模式_第15张图片

    继承该抽象类的子类,需要重写三个抽象方法,表示构建pizza的三个parts

    设计模式(一)面向可维护性的,基于委派和继承的设计模式_第16张图片

然后再建立一个director类:waiter    

将操作委派给传进来的pizzabuilder:

在其ConstructPizza方法中,分别调用pizzabuilder的createNewPizza,buildXXX方法

进行委派。

在其getPizza方法中,调用pizzabuilder的getPizza方法,返回pizzabuilder内部已构造好的属性。

然后再client中:使用不同的builder子类构建不同的产品,建立waiter与builder的委派关系,实现构建细节的完全隐藏。

如下:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第17张图片

对比1:Abstract Factory vs Builder

Abstract Factory创建的不是一个完整的产品,而是“产品族”(遵循固定搭配规则的多类产品实例),各类产品的创建过程对client可见,但搭配不能改变。

Builder创建的是一个完整的产品,至于有多少部分组成,client不需要了解每个部分是怎样创建,各个部分是怎样组合,最终得到一个完成的产品object。

对比2:Template Method vs Builder

Template Method:一个behavioral pattern(行为类模式),目标是为了复用算法的公共次序:

-    定义了一个操作中算法的骨架(steps),而将具体步骤的实现延迟到子类中, 从而复用算法的结构并可重新定义算法某些特定步骤的实现逻辑。 

-    复用算法骨架,强调步骤的次序 

-    子类override算法步骤 

Builder: 一个creational pattern(创建型模式),目标是创建复杂对象,可灵活扩展

-    将一个复杂对象的构造方法与对象内部的具体表示分离出来,同样的构造方 法可以建立不同的表现。 

-    不强调复杂对象内部各部分的“次序” 

-    子类override复杂对象内部各部分的“创建”

-    适应变化:通过派生新的builder来构造新的对象(即新的内部表示)

    满足OCP原则。

3.    Structural patterns(结构型模式)

(1)Bridge pattern(桥接模式)  

    Bridge是OOP最基本的structural pattern,通过delegation+inheritance 建立两个具体类之间的关系(满足DIP依赖转置原则,抽象依赖于抽象)。

    为了动态建立起delegation link,创造客户端使用的各个delegate类的接口,客户端通过使用接口,来动态地delegation,便于方法的维护和拓展。

    设计模式(一)面向可维护性的,基于委派和继承的设计模式_第18张图片

例如:我们有各种Shape类,希望具备绘图功能,delegate到专门的绘图类。

先建立绘图类的抽象接口:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第19张图片

再完成绘图类的多种实现:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第20张图片

在抽象类class中永久保存delegation,构造中动态传入DrawAPI,进而建立动态的delegation link。

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第21张图片

在抽象类Shape的具体实现中:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第22张图片

将draw的责任delegate到drawAPI接口中进行具体执行。

相当于delegation中的aggregation

客户端使用:



对比:Bridge vs. Strategy

Bridge: 

structural pattern (结构型模式)强调双方的run time delegation linking,即强调结构关系的动态绑定。

关注的是:已知一个类A的对象中有其他类B的对象作为其组成部分,但A的对象具体绑定到B的哪个具体子类的实现。在运行时通过delegation加以组合,永久保存这种delegation关系(一般在creator中进行组合)。

Strategy: 

behavioral pattern (行为类模式)强调一方run-time使用另一方的“算法”,即强调算法的功能调用。

“算法”通常实现为“类的某个方法”的形式,strategy的目的并非在“调用算法的类”与“被调用算法所在的类”之间建 立起永久联系,而只是帮助前者临时使用后者中的“算法”,前者无需永久保存后者的实例。

(只是在算法中进行delegation调用,而非将delegation类视为一种组成部分,是临时的)


(2)Proxy pattern(代理模式)

某个对象比较“敏感”/“私密”/“贵重”,不希望被client直接访问 到,故设置proxy,在二者之间建立防火墙。

模式图:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第23张图片

Subject是为了便于改变的抽象接口,客户端访问抽象接口。

Proxy,RealSubject均是Subject的子类,进行具体实现。但client无法直接访问RealSubject,只能访问Proxy。但Proxy的各项功能均通过delegation到RealSubject来实现,进而client在没有直接接触到RealSubject的情况下使用到了其功能。

例子:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第24张图片

代码:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第25张图片

而使用的代理的类:不需要再构造的时候从文件装载,当display防线没有装载时,再delegation,delegate到原来的类来完成具体装载(相当于把装载过程延迟到了代理过程)

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第26张图片

相当于delegation中的Composition。

对比:Proxy vs. Adaptor

Adapter:是一种structural pattern(结构型模式),目的是:消除不兼容,令新的类以客户端期望的统一的方式,和delegate的类建立起联系。

Proxy:是一种behavioral pattern(行为类模式),目的是:隔离对复杂对象的访问,降低难度/代价,定位在“访问/使用行为”


(3)Composite pattern(组合模式)

应用场景:应用需要处理原始的和组合的分级对象集。

    加工一个原始的对象需要一种方法,而处理加工一个组合的对象则需要不同的方法

    不喜欢在尝试处理前不得不查询每个对象的类别。

应对策略:将对象组织成树结构,来代表整块的层次性。

    使用户端以统一的方式对待单一对象和对象组合。

    递归地组合。

Composite pattern:

    目录类含有目录项,而每个目录项均可以是一个目录。    

    容器内含有元素,每个元素均可是一个容器。(如此形成树结构)

    设计模式(一)面向可维护性的,基于委派和继承的设计模式_第27张图片

一个例子。

一个公司的组成层次:基本元素员工:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第28张图片

这样就可以在每个节点加入另外的节点,表示其下级,相当于根节点上的叶节点。

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第29张图片

对比:Composite vs Decorator

Composite:一个结构型模式,目的是在同类型的对象之间建立起树型层次结构,一个上级对象可包括多个下级对象。

Decorator:也是一个结构型模式,强调的是同类型对象之间“特性增加”问题,它们之间是平等的,区别在于“拥有特性”的多少,每次decoration只能作用于一个object。


4.    Behavioral patterns(行为类模式)


(1)Observer pattern:

应用场景:“粉丝”对“偶像”感兴趣,希望随时得知偶像的一举一动 

粉丝到偶像那里注册,偶像一旦有新闻发生,就推送给已注册的粉丝 (回调callback粉丝的特定功能)

解决方式:

定义以下多种对象:

a.    Abstract subject:维护list of dependents(粉丝),在master(偶像)变化时通知

    它们。

b.    Abstract observer:定义被提供更新信息的粉丝的协议

c.    Concrete subject:管理粉丝的数据,在master(偶像)变化时通知它们。

d.    Concrete observers:获得最新的subject状态,接受更新信息。

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第30张图片

设计代码:

Subject类:用一个list维持一组“observer”对象

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第31张图片

通知粉丝的方法:调用粉丝的update操作,让粉丝update自己的变化。

attach方法:向list中增加粉丝。

粉丝对象:Observer

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第32张图片

subject同样作为Observer的一个属性,即二者建立的是双向的关系。

创建observer时,即将subject放置到自身属性中,又调用subject方法,将自己加入粉丝队列。

update方法:是被“偶像”更新信息时回调的方法,当偶像状态变化时,调用subject.getState获得最新信息。不同子类的“粉丝”Observer,其update方法可重新,因此其行为可定制。

客户端实例:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第33张图片

并没有直接调用粉丝行为的代码,而是通过委托的方式实现。这样就可以自动地向多个observer对象返回信息。

模拟了1对多的依赖。

Java中已经提供现成的Observer类与Observable类,对应粉丝与偶像:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第34张图片


(2)Visitor pattern:

模式描述:

对特定类型的object的特定操作(visit),在运行时将二者动态绑定到一起,该操作可以灵活更改,无需更改被visit的类 ,本质上:将数据和作用于数据上的某种/些特定操作分离开来。

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第35张图片

visitorClient:操作访问端

objectStructure:数据端

实现代码:

买书问题:

数据元素类型:Book,其有一个属性:价格

accept方法:调用外部传入的visitor,将处理数据的功能delegate到visitor中来。

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第36张图片

visitor:需要计算书的价钱,其功能完全可以在Book类中实现,但为了将数据和操作进行分离,写在visitor的方法中,book需要通过委派的方式进行调用。

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第37张图片

客户端:

设计模式(一)面向可维护性的,基于委派和继承的设计模式_第38张图片

若方式变化,只需改变visitor的具体实现,而不需要改变各个物品类中的代码。

对比:

Visitor vs Iterator(同为行为类模式)

Visitor:在特定ADT上执行某种特定操作,但该操作不在ADT内部实现,而是delegate到独立的visitor对象,客户端可灵活扩展/改变visitor的操作算法,而不影响ADT

Iterator:以遍历的方式访问集合数据而无需暴露其内部表 示,将“遍历”这项功能delegate到外部的iterator对象。

Visitor vs Strategy(同为行为类模式)

二者都是通过delegation建立两个对象的动态联系。

区别是:

visitor 强调的是外部定义某种对ADT的操作,该操作对于ADT自身关系不大(只是进行访问),故ADT内部只需要开放accept(visitor)即可,client通过它设定visitor操作,并在外部调用。

Strategy强调的是对ADT内部某些要实现的功能的相应算法的灵活替换,而这些算法是ADT功能的重要组成部分,只不过是delegate到外部strategy类而已。

visitor是站在外部client的角度,灵活增加对ADT的各种不同操作(哪怕ADT没实现该操作),strategy则是站在内部ADT的角度, 灵活变化对其内部功能的不同配置。












你可能感兴趣的:(java,设计模式,面向可维护性,继承与委派)