设计模式概述

个人框架中的应用

系统架构上

  • 状态模式:FSM有限状态机;模块场景之间的切换;AI控制

  • 单例模式:游戏中的唯一管理者

  • 外观模式:GamePlay中的各个系统;封装的工具;不同的模块

  • 中介者模式:事件系统;MVC中的C层就是M和V的中介

角色设计上

  • 桥接系统:数据配置如技能配置桥接引用Buff和输入的实现;原版ECS实体和组件
  • 策略模式:Buff系统
  • 模板模式:ECS框架底层关键逻辑,同时也是ECS的短板;UIPanel面板逻辑公用;技能系统流程公用

角色创建

  • 工厂方法:角色、技能、buff的创建;ECS逻辑分发的概念

  • 建造者模式:ET框架组件式编程;游戏中的特效组织;UI界面的拼凑

  • @享元模式:对象池;ET数值系统?

战斗开始

  • 组合模式:UI框架中的V层;ECS系统的生命周期管理
  • 适配器模式:ECS无法继承只能通过适配接口来扩展

辅助系统

  • 观察者模式:事件系统;MVC中的M层;成就系统
  • 代理模式:游戏中的资源加载和服务器缓冲

    七大原则

    单一职责原则SRP

​ 从软件变化的角度来看,就一个类而言,应该仅有一个让它变化的原因;通俗地说,即一个类只负责一项职责;

接口隔离原则ISP

接口隔离原则,其 “隔离” 并不是准备的翻译,真正的意图是 “分离” 接口的功能
1.接口隔离原则强调:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上

里氏替换原则LSP

​ 当使用继承时候,类 B 继承类 A 时,除添加新的方法完成新增功能 P2,尽量不要修改父类方法预期的行为;里氏替换原则的重点在不影响原功能,而不是不覆盖原方法;

合成复用原则CRP

​ 组合/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分; 新的对象通过向这些对象的委派达到复用已有功能的目的

开闭原则OCP

​ 应该是可以被扩展的,但是不可被修改;

1.如果一个软件能够满足 OCP 原则,那么它将有两项优点:

  • 能够扩展已存在的系统,能够提供新的功能满足新的需求,因此该软件有着很强的适应性和灵活性。
  • 已存在的模块,特别是那些重要的抽象模块,不需要被修改,那么该软件就有很强的稳定性和持久性。

依赖倒置原则DIP

​ 高层模块不应该依赖低层模块,二者都应该于抽象。进一步说,抽象不应该依赖于细节,细节应该依赖于抽象

迪米特法则LOD

​ 对于被依赖的类来说,无论逻辑多么复杂,都尽量的将逻辑封装在类的内部,对外提供 public 方法,不对泄漏任何信息;

创建型模式(5种)

创建型模式,就是创建对象的模式,抽象了实例化的过程。
它帮助一个系统独立于如何创建、组合和表示它的那些对象。
关注的是对象的创建,创建型模式将创建对象的过程进行了抽象,也可以理解为将创建对象的过程进行了封装,作为客户程序仅仅需要去使用对象,而不再关心创建对象过程中的逻辑

单例模式*

单例模式概念

​ 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

1.详细说明:

  • 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点

  • 主要解决:一个全局使用的类频繁地创建与销毁

  • 何时使用:当您想控制实例数目,节省系统资源的时候

  • 如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建

  • 关键代码:构造函数是私有的

2.优点

  • 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存);
  • 避免对资源的多重占用(比如写文件操作);

3.缺点:

  • 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

4.使用场景:

  • 要求生产唯一序列号。
  • WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  • 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

5.注意事项:

  • 单例类只能有一个实例;
  • 单例类必须自己创建自己的唯一实例;
  • 单例类必须给所有其他对象提供这一实例;

多线程下的单例模式

public class Singleton {  
    private volatile static Singleton singleton;  
    //设置为私有构造方法,外部无法直接实例化
    private Singleton (){}  
    public static Singleton getSingleton() {  
    //第一个判断用来检测实例是否存在
    if (singleton == null) {  
        //加锁
        lock (singleton) {  
            //第二个判断用来避免多线程下多次创建
            if (singleton == null) {  
                singleton = new Singleton();  
            }  
        }  
    }  
    return singleton;  
    }  
}

静态初始化(饿汉、懒汉)

1.懒汉模式:懒汉模式是在第一次引用的才将自己实例化,如上面的代码,懒汉模式还有一个优点就是在多个管理者单例面前之前可能会互相调用,那么这时候如果采用如下的饿汉模式,可能一部分管理者还没有实例化

单例模式的应用

  • 要求生产唯一序列号;
  • Manager of Managers种的管理者就是唯一单例
  • 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等;

工厂方法模式

工厂方法的概念

​ (依赖倒置、迪米特法则、开闭原则、里氏替换)这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式;在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

1.详细说明:

  • 意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
  • 主要解决:主要解决接口选择的问题。
  • 何时使用:我们明确地计划不同条件下创建不同实例时。
  • 如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
  • 关键代码:创建过程在其子类执行。

2.优点:

  • 一个调用者想创建一个对象,只要知道其名称就可以了。

  • 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。

  • 屏蔽产品的具体实现,调用者只关心产品的接口。

3.缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

4.使用场景:

  • 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
  • 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
  • 设计一个连接服务器的框架,需要三个协议,”POP3”、”IMAP”、”HTTP”,可以把这三个作为产品类,共同实现一个接口。

5.注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

6.和简单工厂区别:

  • 简单工厂只是简单的用条件判断去封装了生产过程,集中在一个类,耦合性很大
  • 而工厂方法具体的生产过程还是在子类,每个子类去生产不同的产品,但是耦合性更大

工厂方法的应用

  • 您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
  • 角色、Buff、技能的创建都可以用工厂方法,甚至可以和对象池一起封装
  • Hibernate 换数据库只需换方言和驱动就可以。

抽象工厂模式

建造者模式*

建造者模式概念

(合成复用、单一职责)相同的流程(分析流程),不同的功能产生不同的对象行为表现(功能分开)。使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的

1.详细说明

  • 意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

  • 主要解决:主要解决在软件系统中,有时候面临着”一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

  • 何时使用:一些基本部件不会变,而其组合经常变化的时候。

  • 如何解决:将变与不变分离开。

  • 关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。

2.优点:

  • 建造者独立,易扩展。

  • 便于控制细节风险。

3.缺点:

  • 产品必须有共同点,范围有限制。

  • 如内部变化复杂,会有很多的建造类。

4.使用场景:

  • 需要生成的对象具有复杂的内部结构。

  • 需要生成的对象内部属性本身相互依赖。

5.注意事项:

  • 与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

建造者模式的应用

  • 搭建框架的基本功底
  • ET框架组件式编程
  • 去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的”套餐”。
  • JAVA 中的 StringBuilder。

原型模式

结构型模式(解耦、封装 7种)

结构型模式是为解决怎样组装现有的类,设计他们的交互方式,从而达到实现一定的功能。

结构型模式侧重于接口的使用,它做的一切工作都是对象或是类之间的交互,提供一个门,成就一个你来我往,协同合作的地球村;

适配器模式

适配器模式概念

​ (开闭原则、合成复用原则、接口隔离原则、单一职责)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。;这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡

1.详细说明:

  • 意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
  • 主要解决:主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。
  • 何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
  • 如何解决:继承或依赖(推荐)。
  • 关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。

2.优点:

  • 可以让任何两个没有关联的类一起运行。
  • 提高了类的复用。
  • 增加了类的透明度。
  • 灵活性好。

3.缺点:

  • 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
  • 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

4.使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。

5.注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。

适配器模式的应用

  • ECS的接口扩展

  • 1、美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。

  • JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。 3、在 LINUX 上运行 WINDOWS 程序。

  • JAVA 中的 jdbc。

装饰器模式

代理模式*

代理模式概念

​ (开闭原则、迪米特法则)一个类代表另一个类的功能。这种类型的设计模式属于结构型模式;在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口

1.详细说明

  • 意图:为其他对象提供一种代理以控制对这个对象的访问。
  • 主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
  • 何时使用:想在访问一个类时做一些控制。
  • 如何解决:增加中间层。
  • 关键代码:实现与被代理类组合。

2.优点:

  • 职责清晰。
  • 高扩展性。
  • 智能化。

3.缺点:

  • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
  • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

4.使用场景:按职责来划分,通常有以下使用场景:

  • 远程代理。

  • 虚拟代理。

  • Copy-on-Write 代理。

  • 保护(Protect or Access)代理。

  • Cache代理。

  • 防火墙(Firewall)代理。

  • 同步化(Synchronization)代理。

  • 智能引用(Smart Reference)代理。

5.注意事项:

  • 和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
  • 和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

###代理模式的应用
应用实例:

  • 游戏中的资源加载和服务器缓冲
  • Windows 里面的快捷方式。
  • 猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。
  • 买火车票不一定在火车站买,也可以去代售点。
  • 一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。
  • spring aop。

外观模式*

外观模式概念

​ (单一职责、迪米特法则、合成复用原则)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性;这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。

1.详细说明:

  • 意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

  • 主要解决:降低访问复杂系统的内部子系统时的复杂度,简化客户端之间的接口。

  • 何时使用:

    • 客户端不需要知道系统内部的复杂联系,整个系统只需提供一个”接待员”即可
    • 定义系统的入口
  • 如何解决:客户端不与系统耦合,外观类与系统耦合。

  • 关键代码:在客户端和复杂系统之间再加一层,这一层将调用顺序、依赖关系等处理好。

2.优点:

  • 减少系统相互依赖。

  • 提高灵活性。

  • 提高了安全性。

3.缺点:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。

4.使用场景:

  • 为复杂的模块或子系统提供外界访问的模块。
  • 子系统相对独立。 3、预防低水平人员带来的风险。

5.注意事项:在层次化结构中,可以使用外观模式定义系统中每一层的入口。

外观模式的应用

  • 编写工具类,只提供一个对外接口;
  • 游戏中的一些系统,例如GamePlay中的技能系统、Buff系统、关卡系统;
  • JAVA 的三层开发模式;

    桥接模式*

桥接模式概念

​ (合成复用法则、里氏替换原则、依赖倒置原则)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦,这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。

1.详细说明:
意图:将抽象部分与实现部分分离,使它们都可以独立的变化。
主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
何时使用:实现系统可能有多个角度分类,每一种角度都可能变化。
如何解决:把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。
关键代码:抽象类依赖实现类。

优点: 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。

缺点:桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。

使用场景: 1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

注意事项:对于两个独立变化的维度,使用桥接模式再适合不过了。

桥接模式的引用

  • 游戏中的角色系统,不仅仅要考虑角色这一个东西,还需要考虑武器,可以把角色和武器桥接起来
  • 游戏中的配置数据,例如技能配置,就可以桥接Buff和输入

##组合模式*

享元模式

行为型模式(11种)

行为型模式是对在不同的对象之间划分责任和算法的抽象化,行为型模式不仅仅关注类和对象的结构,而且重点关注他们之间的相互作用,通过行为型模式,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象之间的交互。

策略模式*

策略模式概念

​ (除了单一职责、合成复用)一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式;在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法;

1.详细说明:

  • 意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
  • 主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
  • 何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
  • 如何解决:将这些算法封装成一个一个的类,任意地替换。
  • 关键代码:实现同一个接口。

2.优点:

  • 算法可以自由切换。

  • 避免使用多重条件判断。

  • 扩展性良好。

3.缺点:

  • 策略类会增多。

  • 所有策略类都需要对外暴露。

4.使用场景:

  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

  • 一个系统需要动态地在几种算法中选择一种。

  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

5.注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

策略模式的应用

  • 游戏种的Buff系统就是最好的解释,但是例如一个伤害类Buff应该是一个伤害类算法群,分不同接口处理,但是都可以交给一个Context;
  • 旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。
  • JAVA AWT 中的 LayoutManager。

模板方法模式*

模板方法模式概念

​ (依赖倒置原则,里氏替换原则)一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式

1.详细说明:

  • 意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
  • 主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
  • 何时使用:有一些通用的方法。
  • 如何解决:将这些通用算法抽象出来。
  • 关键代码:在抽象类实现,其他步骤在子类实现。

2.优点:

  • 封装不变部分,扩展可变部分。
  • 提取公共代码,便于维护。
  • 行为由父类控制,子类实现。

3.缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

4.使用场景:

  • 有多个子类共有的方法,且逻辑相同。

  • 重要的、复杂的方法,可以考虑作为模板方法。

5.注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。

模板模式应用

  • 应用场景太丰富了,是面向对象继承、多态的基础
  • 如角色系统,一些不管是怪物和玩家角色都可以公用的逻辑就写父类中
  • 游戏或者程序中的步骤流程可以套用模板,而具体干什么就具体实现,如不同法术的释放
  • 在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。
  • spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。

观察者模式*

观察者模式概念

​ 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

1.详细说明:

  • 意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
  • 主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
  • 何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
  • 如何解决:使用面向对象技术,可以将这种依赖关系弱化。
  • 关键代码:在抽象类里有一个 ArrayList 存放观察者们。
  • 应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。

2.优点:

  • 观察者和被观察者是抽象耦合的。
  • 建立一套触发机制。

3.缺点:

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

4.注意事项:

  • JAVA 中已经有了对观察者模式的支持类。
  • 避免循环引用。
  • 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

观察者模式应用

  • 事件系统

  • MVC中的M层

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。

  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。

  • 一个对象必须通知其他对象,而并不知道这些对象是谁。

  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

迭代子模式

责任链模式

命令模式*

备忘录模式*

状态模式*

状态模式概念

​ (开闭原则、合成复用原则、里氏替换)类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式,在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象

1.详细说明:

  • 意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
  • 主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
  • 何时使用:代码中包含大量与对象状态有关的条件语句。
  • 如何解决:将各种具体的状态类抽象出来。
  • 关键代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if…else 等条件选择语句。

2.优点:

  • 封装了转换规则;
  • 枚举可能的状态,在枚举状态之前需要确定状态种类;
  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为;
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块;
  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数;

3.缺点:

  • 状态模式的使用必然会增加系统类和对象的个数;
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱;
  • 状态模式对”开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码;

4.使用场景:

  • 行为随状态改变而改变的场景;
  • 条件、分支语句的代替者;

5.注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个;

状态模式的应用

  • 有限状态机
  • 模块之间的切换

访问者模式

中介者模式*

中介者模式概念

​ (迪米特法则)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式

1.详细说明:

  • 意图:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

  • 主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。

  • 何时使用:多个类相互耦合,形成了网状结构。

  • 如何解决:将上述网状结构分离为星型结构。

  • 关键代码:对象 Colleague 之间的通信封装到一个类中单独处理。

2.优点:

  • 降低了类的复杂度,将一对多转化成了一对一。

  • 各个类之间的解耦。 3、符合迪米特原则。

3.缺点:中介者会庞大,变得复杂难以维护。

4.使用场景:

  • 系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。
  • 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

5.注意事项:不应当在职责混乱的时候使用。

中介者模式的应用:

  • 事件系统是典型的中介者模式;
  • 我们生活中的物流和物流中心也是的;
  • MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者;

解释器模式