• 盘点GoF的23种设计模式


    前言

    设计模式最初并非出于软件设计中,而是用于建筑领域的设计中。1995年,四位作者将建筑设计的基本模式融合到软件开发中,合作出版了《设计模式:可复用的面向对象软件的基础》,一共收录了23个设计模式,这是设计模式领域里程碑的事件,导致了软件设计模式的突破。所以这四位作者在软件开发领域耦以四人帮(Gang Of Four)匿名著称,简称GoF。

    一、设计模式的分类

    设计模式按照目的来划分的话可以划分为三种类型,分别为创建型模式、结构型模式和行为型模式

    1.1、创建型模式

    用于描述“怎样创建对象”,主要特点是将对象的创建和使用进行分离。

    对象使用者不需要关心对象的创建细节,可以降低创建对象和使用对象之间的耦合度。

    主要有单例模式、原型模式、工厂方法模式、抽象工厂模式和建造者模式等五种设计模式

    1.2、结构型模式

    用于描述如何将类或对象按某种布局组成更大的结构

    主要有代理模式、适配器模式、桥接模式、装饰器模式、外观模式、享元模式和组合模式等七种设计模式

    1.3、行为型模式

    用于描述类或对象之间怎样协作共同完成单个对象都无法单独完成的任务以及如何分配各个对象的职责分配

    主要有模版方法模式、策略模式、命令模式、责任链模式、状态模式、观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式和解释器模式等十一中设计模式

    二、创建型设计模式

    2.1、单例模式(Singleton)

    定义:某个类只能生成一个实例,该类需要提供一个全局访问点供外部获取该类的全局唯一实例

    单例模式的类需要满足以下三个要求:

    1.单例类只有一个实例对象

    2.单例对象必须由单例类自行创建

    3.单例类需要对外提供一个获取单例对象的全局访问点

    优缺点:

    1、保证内存中仅有一个实例,减少内存开销

    2、不易扩展,需求发生改变需要修改单例类,会违背开闭原则

    应用场景:

    1、需要频繁创建和销毁某个类的实例时;

    2、某个类的实例具有唯一性时

    3、类的创建实例的过程非常消耗资源时

    4、对象需要被全局共享时

    可参考文章:Java常用设计模式详解1--单例模式

    2.2、原型模式(Prototype)

    定义:将一个对象作为原型,通过对其复制从而克隆出多个和原型类似的新的实例

    优缺点:

    1、采用clone的方式比new一个对象性能更好,因为是直接基于内存二进制流的复制

    2、深克隆时需要每一层的对象都需要实现cloneable接口

    原型模式在Java中的实现依赖于cloneable接口,原型实现cloneable接口,需要根据原型创建新对象时,直接调用原型对象的clone方法进行复制即可

    应用场景:

    1、对象之间相同或相似时,无需通过new创建

    2、创建对象成本大,比如比较消耗CPU或网络资源等

    3、系统中大量使用该类对象,且各个调用者都需要给它的属性重写赋值时,比如状态等属性

    案例如下:

    业务场景

    斗地主游戏有3人斗地主和4人斗地主,每种模式还分为新手房和高手房,此时可以抽象出游戏房间的类,并初始化各种类型的房间原型,每当有玩家进入对应模式的房间时就通过原型直接复制出一个房间即可。

     1 public class MainTest {
     2 
     3     static Map<String, GameRoom> prototypeRoomMap = new HashMap<>();
     4 
     5     /** 初始化游戏房间原型*/
     6     static {
     7         GameRoom room1 = new GameRoom();
     8         room1.setGameName("三人斗地主");
     9         room1.setRoomStatus(0);
    10         room1.setRoomType("新手房");
    11 
    12         GameRoom room2 = new GameRoom();
    13         room2.setGameName("四人斗地主");
    14         room2.setRoomStatus(0);
    15         room2.setRoomType("新手房");
    16 
    17         GameRoom room3 = new GameRoom();
    18         room3.setGameName("三人斗地主");
    19         room3.setRoomStatus(0);
    20         room3.setRoomType("高手房");
    21 
    22         GameRoom room4 = new GameRoom();
    23         room4.setGameName("四人斗地主");
    24         room4.setRoomStatus(0);
    25         room4.setRoomType("高手房");
    26 
    27         prototypeRoomMap.put("斗地主3人-新手房", room1);
    28         prototypeRoomMap.put("斗地主4人-新手房", room2);
    29         prototypeRoomMap.put("斗地主3人-高手房", room3);
    30         prototypeRoomMap.put("斗地主4人-高手房", room4);
    31     }
    32 
    33     public static void main(String[] args) throws CloneNotSupportedException {
    34         /** 获取指定类型房间的原型*/
    35         GameRoom prototype = prototypeRoomMap.get("斗地主4人-新手房");
    36         GameRoom room = null;
    37         for (int i=0;i<100;i++){
    38             if(i%4==0){
    39                 room = (GameRoom) prototype.clone();
    40                 System.out.println("玩家:"+i+"创建新房间");
    41             }
    42             room.addPlayer(new Player(i));
    43         }
    44     }
    45 }

    先初始化各种类型房间的原型对象,需要的时候获取原型对象,通过clone方法复制出一个新的对象,属性值和原型类的属性值完全一样。(原型类需要重写clone方法) 

    2.3、工厂方法模式(Factory Method)

    定义:提供一个创建产品的接口,由子类来决定生产什么产品

    工厂模式可以有三种实现模式,分为简单工厂模式,工厂方法模式和抽象工厂模式

    需要被创建的对象叫做产品,创建产品的对象叫做工厂,如果创建的产品类型已知且有限,那么只需要一个工厂类就可以完成,就算是简单工厂模式,简单工厂模式创建实例的方法通常为静态方法,可以创建多个不同的产品。 

    不过简单工厂模式不在GoF 23种设计模式之中。而工厂方法模式和抽象工厂模式则在GoF 23种设计模式之列。

    工厂方法模式是简单工厂模式的扩展,工厂不再是具体的实现类,而是需要定义一个抽象工厂,抽象工厂定义了创建产品的方法,而具体创建产品的工作交给抽象工厂的具体工厂去实现,这样当需要扩展新的产品时不需要修改抽象工厂,满足开闭原则

    可参考文章:Java常用设计模式2--工厂模式

    2.4、抽象工厂模式(Abstract Factory)

    定义:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品

    抽象工厂模式是工厂方法模式的扩展,当一个工厂不只生产一个产品时可以采用抽象工厂模式,抽象工厂定义多种产品的创建方法,具体的工厂实现抽象工厂,负责创建不同产品族中的各个产品。相比于工厂方法模式而言,每个工厂创建的产品会更丰富。

    比如定义汽车工厂抽象接口可以生产汽车和发动机,具体实现奔驰工厂可以生产奔驰的汽车和奔驰的发动机。

    当抽象工厂模式中的产品只有一种时,那么就退化到了工厂方法模式

    可参考文章:Java常用设计模式2--工厂模式

    2.5、建造者模式(Builder)

    定义:将一个复杂的对象分解成多个简单的部分,根据不同的需求进行创建,最后构造成复杂的对象

    建造者模式将一个复杂的对象分解成多个简单的对象,然后根据需求一步一步的构建,对象的所有组成部分是不可变的,但是每一个部分是灵活选择的。

    优缺点:

    1、封装性好,对象的构建和表示相分离

    2、扩展性好,对象的各个组成部分相互独立,各个部分之间互相解耦

    3、对象的构造和引用解耦,引用对象者无需关心对象构造的过程

    4、当对象的构造发生变化时,无需修改客户端,只需要修改建造者即可,满足开闭原则

    建造者模式和工厂模式相比更注重对象的构造过程,而工厂模式更注重对象的创建过程。以汽车为例,建造者模式关系如何将发动机、底盘、车轮和车身零件组装在一起,而工厂模式根据注重发动机、底盘等零件的生产过程

    可参考文章:JAVA常用设计模式3--建造者模式

    三、结构型设计模式

    3.1、代理模式(Proxy)

    定义:给某对象提供一个代理以控制对该对象的访问,访问对象不适合或无法直接访问目标对象,需要通过代理这种中介的身份进行访问

    代理模式的优缺点

    1、将目标对象和引用者之间进行隔离,启到了保护目标对象的作用,也使得目标对象和引用者之间进行解耦

    2、代理可以对目标对象进行功能扩展

    3、客户端和目标对象之间增加了代理对象,增加了系统设计的复杂度

    可参考文章:Java技术整理1---反射机制及动态代理详解

    3.2、适配器模式(Adapter)

    定义:将目标对象接口转换成客户端需要的接口,使得原本并不兼容客户端的接口可以被客户端使用

    适配器模式的优缺点

    1、客户端和目标接口之间解耦,调用透明,无需关心目标接口的具体定义

    2、提高了代码的复用性,一套接口可以适配不同的客户端业务需求

    3、满足了开闭原则,在不修改目标接口的情况下,通过扩展适配器的方式满足新业务的需求

    适配器模式和代理模式有点类似,都是客户端不会直接访问目标对象,而是有一个中间者,不同的是代理模式的目的是可以对目标对象进行扩展;而适配器模式的目的是为了可以正常使用目标对象。

    代理模式下去除代理也可以正常使用目标对象;而适配器模式去除适配器的情况下是无法正常使用目标对象的。一个是不想直接调用,一个是不能直接调用。虽然过程有点类似,但是目的和结果是完全不一样的。 

    3.3、桥接模式(Bridge)

    1、定义:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。

    2、桥接模式的优缺点:

    1、遵循了里氏替换原则和依赖倒置原则,满足了开闭原则

    2、系统复杂度增高

    3、桥接模式角色

    抽象化角色:定义抽象类,并包含一个对实现化对象的引用。

    扩展抽象化角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。

    实现化角色:定义实现化角色的接口,供扩展抽象化角色调用。

    具体实现化角色:给出实现化角色接口的具体实现。

    4、使用场景

    系统存在多个维度的分类,且可能会出现多个不同维度的变化时,通过抽象和实现之间的桥接,实现抽象和实现的解耦。如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系

    5、案例:

    奔驰汽车制造厂推出一款奔驰汽车,但是配置会有很多种,比如低配版、中配版、高配版、豪华版等四种,颜色又分为黑色、白色、灰色、蓝色四种,挂档又分为自动挡和手动挡两种,这样就会导致同一款轿车可以被具体细分出4 * 4 * 2 = 32种。

    此时需要将具体的汽车对象定义出来,就需要定义32个不同的类来表示不同的具体汽车实现类。很显然就会大大增加系统中类的个数。

    此时可以定义实现化角色Color(颜色)、Config(配置),并且分别给Color和Config定义具体的实现化角色,Black、White等颜色,Low、Middle、Top等配置

    定义抽象化角色Car表示汽车,并且包含Color和Config两个属性,定义两个扩展抽象化角色自动档汽车AutoCar和手动挡汽车HandCar,并且设置具体的Color和Config对象。

    这样相当于将Color和Config当作是属性注入到AutoCar和HandCar对象中,将原先的继承方式改成了关联方式,而不需要将各种情况的汽车对象一一定义出来。

    3.4、装饰器模式(Decorator)

    1、定义:在不改变对象现有结构的前提下,动态给该对象增加额外的功能

    2、装饰器模式优缺点:

    1、和继承类似,但是比继承更加灵活

    2、通过使用不同装饰类可以实现不同的效果

    3、遵循了开闭原则,即对修改关闭,对扩展开放

    4、装饰类较多增加了系统复杂度

    3、装饰器模式的角色

    抽象构件角色:定义一个抽象接口以规范准备接收附加职责的对象

    具体构件角色:实现抽象构件,通过装饰角色增加职责

    抽象装饰角色:继承抽象构建,并包含具体构件的实例,通过其子类扩展具体构件的功能

    具体装饰角色:实现抽象装饰的相关方法,并给具体构件对象添加附件的职责

    4、使用场景

    当需要给一个现有类动态添加职责而不希望通过子类的方式实现时;

    当需要通过对现有的一组基本功能进行排列组合而产生丰富的功能时,采用继承比较难以实现;

    当对象的功能可以动态的添加也可以动态的删除时;

    5、案例

    消费者购买刚刚出厂的奔驰汽车之后,往往需要对汽车进行贴膜,以及安装胎压监测等其他设备之后才会上路,而贴膜和胎压监测等装置并不属于汽车本身的设备,而是在不改变汽车本身结构的前提之下进行的装饰。实现如下:

    定义奔驰汽车抽象构件角色类BenzCar,定义具体构件角色类奔驰GLS款汽车对象BenzGLSCar,定义抽象装饰角色用户购买的汽车CusumerBenzCar实现了BenzCar,并且内部包含了BenzGLSCar的实例,对于不需要装饰的功能就直接使用BenzGLSCar的方法,

    对于需要装饰的模块比如贴膜和胎压监测,就可以定义附加的方法添加到内部的BenzGLSCar对象上,这样就可以在不改变BenzGLSCar的基础上进行扩展,同样如果用户购买的是其他车型,只需要将CusumerBenzCar内部的BenzGLSCar实例改成其他车型的实例即可

    3.5、外观模式(Facade)

    1、定义:外观模式又叫做门面模式,通过为多个复杂的子系统提供一个统一的接口供客户端使用,避免客户端需要访问每一个子系统,且不需要关心子系统的具体细节,降低程序复杂度和提高系统可扩展性和维护性

    2、外观模式优缺点

    1、降低客户端和各个子系统之间的耦合度,使得子系统的变化不会影响调用的客户端

    2、降低了系统的复杂度和提高了系统的可扩展性和可维护性

    3、遵循了迪米特法则

    4、增加子系统需要修改外观类,不符合开闭原则

    3、外观模式的角色

    外观角色:为多个子系统对外提供一个统一的接口

    子系统角色:各个子系统负责不同模块的功能,客户端通过外观角色来访问

    客户端角色:客户端不直接访问子系统角色,而是通过外观角色来访问子系统角色

    4、使用场景

    当系统的子系统较多,客户端需要访问多个子系统时,采用外观模式时客户端只需要访问外观类即可,不需要访问每一个子系统,大大降低了客户端和子系统之间的耦合度。

    5、案例

    消费者购买汽车到上路,需要经过买车、买保险、缴纳购置税、上牌等多个操作,并且每一项操作都需要到不同的部门去处理,对于客户来说成本比较高,此时4S店就相当于是外观角色,客户只需要在4S店办理手续即可,其他的和汽车厂家、保险公司、车管所的业务操作都由4S店去操作,客户不需要每一项都亲自去访问,很明显对于客户而言非常便利,降低了很大的购车上路的各项成本。实现如下:

    定义子系统模块,保险业务类InsuranceBusiness、税务业务类TaxBusiness、牌照业务类LicenseBusiness

    定义外观角色4S店面类 Car4SShop

    客户端购买车辆只需要调用4S店面类提供的购车、保险、购置税、上牌等方法即可,不需要关心具体的业务是如何完成的,也不需要每个部门都亲自去访问一遍

    3.6、享元模式(Flyweight)

    1、定义:采用共享技术来支持大量细粒度的对象的复用,共享已经存在的对象大幅度减少需要创建的对象数量,避免大量相似类的创建开销,提供系统资源的复用率

    2、享元模式优缺点

    1、提供系统资源的复用率

    2、需要将对象分成可共享和不可共享两部分,可共享部分共享,不可共享部分外部化,系统复杂性增加

    3、读取享元模式的外部状态会变慢

    3、享元模式的角色

    抽象享元角色:所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入

    具体享元角色:实现抽象享元角色规定的接口

    非享元角色:不可以共享的外部状态,以参数的形式注入具体享元的相关方法中

    享元工厂角色:负责创建和管理享元角色,当客户端请求享元对象时,享元工厂检查系统中是否存在符合要求的享元对象,如果存在则提供给客户端,如果不存在则创建新的对象

    4、使用场景

    当系统中多处需要同一组信息时,可以将信息封装到一个对象中,然后对对象进行缓存,这样一个对象就可以提供给多处需要使用的地方,避免大量同一对象的创建,降低系统内存空间的消耗。

    享元模式和工厂方法模式有点类似,只不过工厂模式都是实时创建新对象,而享元模式是在工厂模式下增加了一层缓存机制,将大量相同的数据进行缓存

    3.7、组合模式(Composite)

    1、定义:组合模式也可以叫做整体-部分模式,它是一种将对象组合成树状的层次结构的模式,用来表示整体-部分的关系,使用户对单个对象和组合对象具有一致的访问性

    组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面可以包含树枝节点和叶子节点。根节点和树枝节点本质上是同一种数据类型,可以作为容器使用;树枝节点和叶子节点在语义上不属于一种类型,但是在组合模式中,会把树枝节点和叶子节点当作同一种数据类型,这样就可以采用同一套接口来定义树枝节点和叶子节点。

    2、组合模式优缺点

    1、客户端可以通过一套接口访问单个对象和组合对象,不需要关心对象是属于单个对象还是组合对象,客户端开发更加简单

    2、组合对象加入新对象不会影响客户端逻辑,符合开闭原则

    3、单个对象和组合对象统一之后不容易扩展新功能,且不同对象之间的差异性不容易展示

    3、组合模式的角色

    抽象构件角色:为树枝和叶子构件声明公共接口,并实现默认行为,比如增删改查等

    树枝构件角色:组合中的分支节点对象,拥有子节点,用于继承或实现抽象构件,主要作用是存储和管理子节点

    叶子构件角色:组合中的叶子节点,没有子节点,用于继承或实现抽象构件

    4、使用场景

    当需要表示一个整体与部分的层次结构时;当需要对用户隐藏组合对象和单个对象的不同,通过采用统一的接口使用组合结构中的所有对象时

    四、行为型设计模式

    4.1、模版方法模式(Template Method)

    1、定义:定义一个操作中的算法框架,将算法的具体实现留给子类实现,使得子类可以不改变算法的具体执行步骤而可以改变算法的具体实现逻辑

    2、模版方法模式优缺点

    1、将业务逻辑的不可变的和可变的拆分开,不可变的规定在抽象类中,可变的留给子类实现,便于子类扩展且提高了代码的复用率

    2、符合开闭原则,子类可以通过扩展的方式增加相应功能的具体实现

    3、当父类增加新的抽象方法时,所有子类都需要同步修改

    3、模版方法模式的角色

    抽象模版角色:抽象模版中定义算法的架构,并且实现部分不变的逻辑并定义留给子类实现的方法定义

    具体实现角色:实现抽象模版定义的抽象方法

    4、使用场景

    当业务流程较长且整体步骤固定,仅仅个别步骤的具体逻辑可变时

    5、案例

    消费者购买奔驰汽车,核心步骤不变,分别包括签署合同、付款、缴纳购置税、买保险、上牌等几个步骤,每个步骤固定不变,但是每个步骤具体实现不同,保险有人买人保有人买太平洋,上牌有人办浙A有人办沪C,此时就可以通过模版方法来实现。

    定义抽象模版方法

     1 public abstract class AbstractBuyCar {
     2 
     3     public void buyCar(){
     4         sign();
     5         pay();
     6         tax();
     7         insurance();
     8         license();
     9     }
    10 
    11     /** 签署合同*/
    12     protected abstract void sign();
    13 
    14     /** 付款 */
    15     protected abstract void pay();
    16 
    17     /** 缴税 */
    18     protected abstract void tax();
    19 
    20     /** 买保险 */
    21     protected abstract void insurance();
    22 
    23     /** 上牌 */
    24     protected abstract void license();
    25 }

    抽象模版方法将购买汽车的整体逻辑定义在buyCar方法中,并且定义了各个步骤的抽象方法,子类实现AbstractBuyCar类的具体步骤的方法,这样就能保证买车的整体步骤不会改变,但是具体的实现会不同

    4.2、策略模式(Strategy)

    1、定义:策略模式时定义了一系列的算法,并将每个算法封装起来,互相可以替换且算法的改变不会影响使用算法的客户。通常是某个功能存在多种算法或者策略,根据不同场景选择不同的算法或者策略完成功能。

    2、策略模式的优缺点

    1、使用策略模式可以避免多重条件语句

    2、可以实现相同行为的不同实现,客户端根据不同环境选择不同实现

    3、会出现多种策略类,客户需要选择合适的策略类,系统复杂度增加

    3、策略模式的角色

    抽象策略类:定义一个公共接口,不同算法实现抽象策略的接口

    具体策略类:实现抽象策略定义的接口,提高具体策略的实现

    环境类:持有一个策略类的引用,最终给客户端使用

    4、使用场景

    实现同一个行为时可以有多种不同的实现方法时就可以采用策略模式,比如排序时可以采用不同的排序算法等场景

    5、案例

    消费者购买汽车时付款方式可以有很多中,比如现金支付、刷卡支付、贷款支付等,此时就可以采用策略模式来实现

    定义抽象策略类Pay,定义具体策略类CashPay、PostPay、LoanPay等实现Pay的方法,定义用户购买汽车的环境类BuyCar,内部持有抽象策略Pay的实例,此时不同的消费者会使用不同的支付方式进行支付,那么在支付的时候传入不同的具体策略即可,而环境类BuyCar中的支付逻辑不受影响。

    4.3、命令模式(Command)

    1、定义:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

    2、命令模式优缺点

    1、通过引入中间件(抽象接口)降低系统的耦合度。

    2、扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且满足“开闭原则”。

    3、可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。

    4、方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。

    5、可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。

    3、命令模式的角色

    抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。

    具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。

    实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。

    调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

    4.4、责任链模式(Chain Of Responsibility)

    1、定义:为了避免请求发送者与多个请求处理者耦合,可以将多个请求处理者通过前后排列成一条链,请求只需要发送到链路上,会自动沿着链路从第一个处理者依次传递到链路的最后一个处理者,直到处理完成为止。

    2、责任链模式优缺点

    1、请求发送者无需关心责任链上有哪些处理者也不需要关心每个处理者的具体实现,使得请求发送者和处理者之间解耦

    2、提高了扩展性,当有新的处理者时只需要在责任链上将新的处理者添加即可,而请求发送者是无需修改的,符合开闭原则

    3、责任链路上的每个处理者只处理本职工作,不需要处理的再传递给下一个处理者即可,符合单一职责原则

    4、当责任链上处理者较多时,会影响每一个请求的执行效率

    3、责任链模式的角色

    抽象处理者角色:定一个处理请求的接口,包含抽象处理方法和一个后续处理者的指针

    具体处理者角色:实现抽象处理者定义的方法,判断是否需要对请求进行处理,然后传递给下一个处理者

    客户端角色:创建责任链,并向链头的具体处理器发送请求,后续流程无需再关注

    4、使用场景

    同一个请求需要被多个不同的对象处理时,具体的处理可以在运行时动态确认;当可以动态增加新处理者时,不需要修改客户端逻辑,只需要往责任链上添加即可

    5、案例

    消费者购买汽车时需要将身份信息交给销售员签订合同、交给财务开发票、交给保险业务员办理保险、交给保安开具出入证明等;此时同样一份身份信息如果每个业务都需要消费者去主动处理,就需要准备四份身份证明,且需要消费者一个个的去办理,此时可以使用责任链模式实现。

    定义抽象处理者业务员Business且定义抽象方法doBusiness处理业务,定义具体处理者类销售员CarSalesMan、财务员Treasurer、保险业务员InsuranceSalesMan、保安SecurityMan实现抽象处理者Business的方法。各自实现处理具体业务方法doBusiness

    定义客户端对象定义责任链,依次创建不同业务员对象,并设置各自的下一个业务员对象引用,然后将用户身份信息传递给第一个业务员,第一个业务员执行doBusiness方法处理业务成功之后将用户身份信息传递给下一个业务员继续执行。

    这样用户只需要将身份信息交给责任链的第一个处理者即可,后续的责任链上有多少处理者,每个处理者具体的处理逻辑不需要用户关心,达到了解耦的效果

    4.5、状态模式(State)

    1、定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

    2、状态模式优缺点

    1、结构清晰,将不同状态对应的行为和状态绑定,且不同状态的行为拆分开,满足了单一职责原则

    2、可扩展性强,可以扩展新的状态和行为来扩展对象的功能,满足了开闭原则

    3、代码复杂度增加,且状态类数量较多

    3、状态模式的角色

    环境角色:环境上下文,定义客户端需要的接口,内部维护当前状态,且维护着对象状态的切换

    抽象状态角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。

    具体状态角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

    4、使用场景

    当一个对象的行为依赖于对象的状态时,且对象行为会随着对象的的状态会动态变化时,可以使用状态模式

    5、案例

    奔驰汽车在使用过程中汽车的行动随着变速箱的档位状态而改变,分为P档停车、N档空档、D档前进、R档倒车,车子的行驶行为完全由档位状态进行控制,此时就可以采用状态模式实现

    定义抽象状态角色类Gear,并定义handle方法处理具体的行为

    定义不同档位的具体状态类GearP,GearN、GearD、GearR分别实现Gear的handle方法,分别实现停车、空档、前进、倒车等行为功能

    定义环境类开车类DriveContext,内部维护一个当前状态Gear对象,并实现档位的切换功能

    6、状态模式和策略模式的区别

    状态模式和策略模式都是在一种状态对应一种行为,区别是策略模式各个状态和行为之间是相互独立的,而状态模式下不同的状态之间是可以动态切换的

    4.6、观察者模式(Observer)

    1、定义:多个对象间存在一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象接收到通知自动更新,相当于发布-订阅模式

    2、观察者模式优缺点

    1、降低了目标和观察者之间的耦合度

    2、当新增观察者时只需要订阅目标即可,其他观察者不会受到影响,可扩展性强

    3、当观察者数量较多时,通知的效率会受到影响

    3、观察者模式的角色

    抽象主题角色:抽象目标角色,提供管理观察者的方法以及通知观察者的方法

    具体主题角色:具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象

    抽象观察者角色:包含抽象的更新方法,当观察的目标更新时被调用

    具体观察者角色:实现抽象观察者的方法,在接收到目标更新之后自动更新

    4、使用场景

    当对象之间存在一对多的关系,一个对象状态的变化会影响多个对象的状态或行为发生变化时

    5、案例

    奔驰汽车熄火之后除了会关闭发动机之后,还会额外关闭其他设备的运作,比如关闭电子仪表盘、 关闭行车记录仪、释放门窗锁等功能,这些功能都会随着发动机的启动而启动,关闭而关闭,所以此时就可以使用观察者模式。

    定义抽象主题角色发动机对象Engine,定义抽象观察者角色电子设备对象Device,并且在Device中定义接收发动机状态变化的方法invoke方法,发动机对象内部定义添加设备和删除设备的方法,并且维护观察的设备列表,另外定义通知所有设备发动机状态的方法invokeEngineState方法

    定义具体主题角色奔驰发动机对象BenzEngine实现Engine的方法,当发动机熄火之后执行invokeEngineState方法,遍历内部所有的设备,依次执行invoke方法通知设备对象。

    定义具体观察者角色仪表盘对象BashBoardDevice、行车记录仪对象DriveRecorderDevice、门窗锁对象DoorLockDevice分别实现Device的方法,各自实现invoke处理不同的逻辑

    4.7、中介者模式(Mediator)

    1、定义:当多个对象之间存在存在复杂的网址结构时,定义一个中介对象来封装一系列对象之间的交互,可以使得对象之间解耦,满足迪米特法则

    2、中介者模式优缺点

    1、对象之间互相解耦,只和中介者交互,符合迪米特法则

    2、将对象之间一对多的关系变成了一对一的关系

    3、当对象数量变多时,中介者逻辑比较难以维护

    3、中介者模式的角色

    抽象中介角色:定义同事对象注册和同事对象之间转发信息的抽象方法

    具体中介角色:实现抽象中介角色的接口,协调各个同事角色之间的交互

    抽象同事角色:保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能

    具体同事角色:抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互

    4、使用场景

    当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时

    5、案例

    有5个奔驰车主想卖车,有5个消费者想买二手车,此时消费者想买到心仪的汽车只能和每个奔驰车主进行交流,同样车主卖出也需要和不同的消费者进行交流才能谈出合适的价格,于是此时就产生了中介者,也就是二手车交易平台。

    此时5个买家和5个卖家不需要再互相交流,都只需要和二手车平台一对一交互即可,大大降低了买家和卖家之间的耦合度。

    4.8、迭代器模式(Iterator)

    1、定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示

    2、迭代器模式优缺点

    1、访问一个聚合对象的内容而无须暴露它的内部表示。

    2、遍历任务交由迭代器完成,这简化了聚合类。

    3、它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。

    4、增加新的聚合类和迭代器类都很方便,无须修改原有代码。

    5、封装性良好,为遍历不同的聚合结构提供一个统一的接口。

    3、迭代器模式的角色

    抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。

    具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。

    抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。

    具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

    4.9、访问者模式(Vistor)

    1、定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离

    2、访问者模式优缺点

    1、扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。

    2、复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。

    3、灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。

    4、符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。

    5、增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。

    6、破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。

    7、违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。

    3、访问者模式角色

    抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。

    具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。

    抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。

    具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。

    对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

    4.10、备忘录模式(Memento)

    1、定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。

    2、备忘录模式优缺点

    1、提供了快照机制,可以使某个对象回滚到历史某个状态

    2、如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源

    3、备忘录模式的角色

    发起人角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息

    备忘录角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人

    管理者角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改

    4、使用场景

    需要保存与恢复数据的场景,方便回滚到历史某个状态时可以使用

    4.11、解释器模式(Interpreter)

    1、定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文

    2、解释器模式优缺点

    1、扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。

    2、容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。

    3、执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。

    4、会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。

    5、可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。

    3、解释器模式角色

    抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。

    终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。

    非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。

    环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。

    客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

  • 相关阅读:
    Golang的跨平台编译程序
    PySide教程:Clo“.NET研究”se 狼人:
    PyS“.NET研究”ide QtCore.Signal帮助手册 狼人:
    PySide教程:“.NET研究”第一个PySide应用 狼人:
    关“.NET研究”于Android的一些设计 狼人:
    资深设计师Tony Ventrice解析手机游戏开“.NET研究”发的四个层次 狼人:
    移动开发多平台代码共享“.NET研究” 狼人:
    关于做Andr“.NET研究”oid+J2ee系统集成开发的一点心得 狼人:
    “.NET研究”【Android开发教程】一、基础概念 狼人:
    “.NET研究”如何发布你的Android应用程序 狼人:
  • 原文地址:https://www.cnblogs.com/jackion5/p/14326857.html
Copyright © 2020-2023  润新知