# DesignPattern **Repository Path**: magicGuGu/design-pattern ## Basic Information - **Project Name**: DesignPattern - **Description**: No description available - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 12 - **Forks**: 0 - **Created**: 2022-02-01 - **Last Updated**: 2022-06-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 设计模式七大原则: 1. 单一职责原则(Single Responsibility Principle, SRP) : 一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中 另一种定义方式:就一个类而言,应该仅有一个引起它变化的原因 简单而言,就是一个类如果职责越多,那么它被复用的可能性就越低即这个类中一个职责变化,可能会影响到其他的职责的运作 因此,单一职责原则就是实现高内聚,低耦合,将一个类的职责降低到最小,即类的数目很多,类中职责很少,因而类被复用的可能性被提高 2. 开闭原则(Open-Closed Principle,OCP) : 软件实体应当对扩展开放,对修改关闭 开闭原则是复用设计的第一块基石在软件实体中应在尽量不修改原有代码的情况下进行扩展 3. 里氏代换原则(Liskov Substitution Principle,LSP) : 所有引用基类的地方必须能透明地使用其子类的对象 在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何的错误和异常,反之不成立 例如:我喜欢动物,那么我一定喜欢狗,因为狗是动物的子类,反之不成立 4. 依赖倒转原则(Dependence Inversion Principle,DIP) : 高层模块不应该依赖低层模块,它们应该依赖抽象抽象不应该依赖于细节,细节应该依赖于抽象 要针对接口编程,不针对实现编程一个具体类应当只实现接口或抽象类中声明过的方法,而不给出多余的方法,否在无调用到在子类中增加的新方法 5. 接口隔离原则(Interface Segregation Principle,ISP) : 客户端不应该依赖那些它不需要的接口 当一个接口太大时,将它分割成一些更细小的接口,使用该接口的客户端只要知道与之相关的方法即可 6. 合成复用原则(Composite Reuse Principle,CRP) :优先使用对象组合,而不是继承来达到复用的目的 在一个新的对象里通过关联关系(包括组合和聚合关系)来使用一些已有的对象,使之成为新对象的一部分,新对象通过委派调用已有对象的方法达到复用功能的目的 7. 迪米特法则(Law of Demeter,LoD) : 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位 又称最少知识原则,一个软件实体应尽可能少地与其他类发生相互作用 设计模式分类: 创建型模式(关注对象的创建过程,对类的实例化过程进行抽象,描述如何将对象的创建和使用分离) 抽象工厂模式(Abstract Factory) 主要解决:主要解决接口选择的问题 何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品 建造者模式(Builder) 主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成 由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定 何时使用:一些基本部件不会变,而其组合经常变化的时候 工厂模式(Factory Method) (工厂方法 、 简单工厂模式) 主要解决:主要解决接口选择的问题 何时使用:我们明确地计划不同条件下创建不同实例时 原型模式(Prototype) 主要解决:在运行期建立和删除 原型 何时使用: 1、当一个系统应该独立于它的产品创建,构成和表示时 2、当要实例化的类是在运行时刻指定时,例如,通过动态装载 3、为了避免创建一个与产品类层次平行的工厂类层次时 4、当一个类的实例只能有几个不同状态组合中的一种时 建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些 单例模式(Singleton) 主要解决:一个全局使用的类频繁地创建与销毁 何时使用:当您想控制实例数目,节省系统资源的时候 结构型模式(关注如何将现有类或对象组织在一起形成更加强大的结构) 适配器模式(Adapter) 主要解决: 主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的 何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口 3、通过接口转换,将一个类插入另一个类系中(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器, 在里面包容一个虎对象,实现飞的接口) 分类: 1.类结构模式: 耦合度较高,且要求使用者了解现有组件库中的相关组件的内部结构 2.对象结构模式 优点: 1、客户端通过适配器可以透明地调用目标接口 2、复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类 3、将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题 4、在很多业务场景中符合开闭原则 缺点: 1、适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性 2、增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱 桥接模式(Bridge) 主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活 何时使用:实现系统可能有多个角度分类,每一种角度都可能变化 优点: 1、抽象和实现的分离 2、优秀的扩展能力 3、实现细节对客户透明 缺点: 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程 使用场景: 1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系, 通过桥接模式可以使它们在抽象层建立一个关联关系 2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用 3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展 组合模式(Composite) 主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素, 从而使得客户程序与复杂元素的内部结构解耦 何时使用: 1、想表示对象的部分-整体层次结构(树形结构) 2、希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象 优点: 1、高层模块调用简单 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码 2、节点自由增加 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则” 缺点: 1、在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则 2、设计较复杂,客户端需要花更多时间理清类之间的层次关系 3、不容易限制容器中的构件 4、不容易用继承的方法来增加构件的新功能 使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理 装饰模式(Decorator) 主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀 何时使用:在不想增加很多子类的情况下扩展类 优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能 缺点:多层装饰比较复杂 使用场景: 1、扩展一个类的功能 2、动态增加功能,动态撤销 外观模式(Facade) 主要解决:降低访问复杂系统的内部子系统时的复杂度,简化客户端之间的接口。 何时使用: 1、客户端不需要知道系统内部的复杂联系,整个系统只需提供一个"接待员"即可。 2、定义系统的入口。 优点: 1、减少系统相互依赖。 2、提高灵活性。 3、提高了安全性。 缺点: 不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。 使用场景: 1、为复杂的模块或子系统提供外界访问的模块。 2、子系统相对独立。 3、预防低水平人员带来的风险。 享元模式(Flyweight) 主要解决: 在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。 何时使用: 1、系统中有大量对象。 2、这些对象消耗大量内存。 3、这些对象的状态大部分可以外部化。 4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。 5、系统不依赖于这些对象身份,这些对象是不可分辨的。 优点: 大大减少对象的创建,降低系统的内存,使效率提高。 缺点: 提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化, 否则会造成系统的混乱。 使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景。 代理模式(Proxy) 主要解决: 在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上 在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问), 直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层 何时使用: 想在访问一个类时做一些控制 eg: crud后插入日志 优点: 1、使职责更加纯粹,不需要去关注一些公共业务 2、高扩展性公共业务交给了代理角色,实现了业务分工 3、智能化业务发生拓展时方便集中管理 缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢 2、实现代理模式需要额外的工作(一个真实角色就会产生一个代理角色,工作量会翻倍),有些代理模式的实现非常复杂(动态代理可以解决这 个问题) 使用场景:按职责来划分,通常有以下使用场景: 1、远程代理 2、虚拟代理 3、Copy-on-Write 代理 4、保护(Protect or Access)代理 5、Cache代理 6、防火墙(Firewall)代理 7、同步化(Synchronization)代理 8、智能引用(Smart Reference)代理 行为型模式(关注系统中对象间的交互,研究系统在运行时对象之间的相互通信与协作进一步明确对象的职责) 职责链模式(责任链模式)(Chain of Responsibility) 主要解决: 职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和 请求的处理者解耦了。 何时使用:在处理消息的时候以过滤很多道。 优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。 缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。 使用场景: 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。 命令模式(Command) 主要解决: 在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法 抵御变化的紧耦合的设计就不太合适。 何时使用: 在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求 者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。 优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。 缺点: 使用命令模式可能会导致某些系统有过多的具体命令类。 使用场景: 认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。 解释器模式(Interpreter) 主要解决: 对于一些固定文法构建一个解释句子的解释器。 何时使用: 如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释 器,该解释器通过解释这些句子来解决该问题。 优点: 1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。 缺点: 1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。 使用场景: 1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 2、一些重复出现的问题可以用一种简单的语言来进行表达。 3、一个简单语法需要解释的场景。 迭代器模式(Iterator) 主要解决:不同的方式来遍历整个整合对象 何时使用:遍历一个聚合对象 优点: 1、它支持以不同的方式遍历一个聚合对象 2、迭代器简化了聚合类 3、在同一个聚合上可以有多个遍历 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码 缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性 使用场景: 1、访问一个聚合对象的内容而无须暴露它的内部表示 2、需要为聚合对象提供多种遍历方式 3、为遍历不同的聚合结构提供一个统一的接口 中介者模式(Mediator) 主要解决: 对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象, 同时做出相应的处理。 何时使用: 多个类相互耦合,形成了网状结构。 优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。 缺点: 中介者会庞大,变得复杂难以维护。 应用实例: 1、中国加入 WTO 之前是各个国家相互贸易,结构复杂,现在是各个国家通过 WTO 来互相贸易。 2、机场调度系统。 3、MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。 使用场景: 1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。 备忘录模式(Memento) 主要解决: 所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。 何时使用: 很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态, 使得他有"后悔药"可吃。 优点: 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节。 缺点: 消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。 使用场景: 1、需要保存/恢复数据的相关状态场景。 2、提供一个可回滚的操作。 观察者模式(Observer) 主要解决: 一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。 何时使用: 一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。 优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。 缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。 使用场景: 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。 一个对象必须通知其他对象,而并不知道这些对象是谁。 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。 状态模式(State) 主要解决: 对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。 何时使用: 代码中包含大量与对象状态有关的条件语句。 优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。 缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码, 否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。 使用场景: 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。 策略模式(Strategy) 主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护 何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为 优点: 1、算法可以自由切换 2、避免使用多重条件判断 3、扩展性良好 缺点: 1、策略类会增多 2、所有策略类都需要对外暴露 使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为 2、一个系统需要动态地在几种算法中选择一种 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现 注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题 模板方法模式(Template Method) 主要解决:一些方法通用,却在每一个子类都重新写了这一方法 何时使用:有一些通用的方法 优点: 1、封装不变部分,扩展可变部分 2、提取公共代码,便于维护 3、行为由父类控制,子类实现 缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大 使用场景: 1、有多个子类共有的方法,且逻辑相同 2、重要的、复杂的方法,可以考虑作为模板方法 访问者模式(Visitor) 主要解决:稳定的数据结构和易变的操作耦合问题 何时使用: 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中 即遍历集合时,这个集合中元素的类型不同,但类型都是已知的,且未来不会改变访问单个元素的方式有多种,每种方式在访问不同类型的元素 时所做的操作不同,并且未来可能会有新的访问方式 优点: 1、符合单一职责原则 2、优秀的扩展性 3、灵活性 缺点: 1、具体元素对访问者公布细节,违反了迪米特原则 2、具体元素变更比较困难 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象 使用场景: 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类