# DesignPattern **Repository Path**: suze/designPattern ## Basic Information - **Project Name**: DesignPattern - **Description**: designPattern - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 0 - **Created**: 2017-06-22 - **Last Updated**: 2025-09-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 设计原则(SOLID) ## 1、单一职责原则 >一个类,只有一个引起它变化的原因。应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。from:百度百科 ## 2、开闭原则(Open Close Principle) >开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。 ## 3、里氏代换原则(Liskov Substitution Principle) >里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。from:百度百科 ## 4、依赖倒转原则(Dependence Inversion Principle) >所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。 实现开闭原则的关键是抽象化,并且从抽象化导出具体化实现,如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要手段。 from:百度百科 ## 5、接口隔离原则(Interface Segregation Principle) >这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。 ## 6、合成复用原则(Composite Reuse Principle) >合成复用原则就是指在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用其已有功能的目的。简言之:要尽量使用组合/聚合关系,少用继承。 ## 7、迪米特法则(最少知道原则)(Demeter Principle) >为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。也就是说一个软件实体应当尽可能少的与其他实体发生相互作用。这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。 # 设计模式 该项目例子列述23种设计模式 ### 创建型模式 在软件工程中,创建型模式是处理对象创建的设计模式,试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。 创建型模式由两个主导思想构成。一是将系统使用的具体类封装起来,二是隐藏这些具体类的实例创建和结合的方式。 创建型模式又分为对象创建型模式和类创建型模式。对象创建型模式处理对象的创建,类创建型模式处理类的创建。详细地说,对象创建型模式把对象创建的一部分推迟到另一个对象中,而类创建型模式将它对象的创建推迟到子类中。 #### SimpleFactory(简单工厂模式) >简单工厂模式定义了一个类,这个类专门用于创建其他类的实例,这些被创建的类都有一个共同的父类。 ![UML](./docs/readme/factoryMethod-UML.png) **参与者** - Factory:工厂角色。专门用于创建实例类的工厂,提供一个方法,该方法根据传递的参数不同返回不同类的具体实例。 - Product:抽象产品角色。为所有产品的父类。 - ConcreteProduct:具体的产品角色。 **优点** - 1、简单工厂模式实现了对责任的分割,提供了专门的工厂类用于创建对象。 - 2、客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。 - 3、通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。 **缺点** - 1、由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。 - 2、使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。 - 3、系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。 - 4、简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。 **简单工厂模式的使用场景** - 1、 工厂类负责创建的对象比较少。 - 2、 客户端只知道传入工厂类的参数,对于如何创建对象不关心。 **总结** - 1、 简单工厂模式的要点就在于当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。 - 2、 简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,但是如果产品过多时,会导致工厂代码非常复杂。 #### FactoryMethod(工厂方法模式) >工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,也就是说工厂方法模式让实例化推迟到子类。 ![UML](./docs/readme/factoryMethod-UML.png) **参与者** - Product:抽象产品。所有的产品必须实现这个共同的接口,这样一来,使用这些产品的类既可以引用这个接口。而不是具体类 。 - ConcreteProduct:具体产品。 - Creator:抽象工厂。它实现了所有操纵产品的方法,但不实现工厂方法。Creator所有的子类都必须要实现factoryMethod()方法。 - ConcreteCreator:具体工厂。制造产品的实际工厂。它负责创建一个或者多个具体产品,只有ConcreteCreator类知道如何创建这些产品。 **优点** - 1、 在工厂方法中,用户只需要知道所要产品的具体工厂,无须关系具体的创建过程,甚至不需要具体产品类的类名。 - 2、 在系统增加新的产品时,我们只需要添加一个具体产品类和对应的实现工厂,无需对原工厂进行任何修改,很好地符合了“开闭原则”。 **缺点** - 1、 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,是的系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。 **工厂方法适用场景** - 1、一个类不知道它所需要的对象的类。在工厂方法模式中,我们不需要具体产品的类名,我们只需要知道创建它的具体工厂即可。 - 2、一个类通过其子类来指定创建那个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。 - 3、将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。 **总结** - 1、工厂方法模式完全符合“开闭原则”。 - 2、工厂方法模式使用继承,将对象的创建委托给子类,通过子类实现工厂方法来创建对象。 - 3、工厂方法允许类将实例化延伸到子类进行。 - 4、工厂方法让子类决定要实例化的类时哪一个。在这里我们要明白这并不是工厂来决定生成哪种产品,而是在编写创建者类时,不需要知道实际创建的产品是哪个,选择了使用哪个子类,就已经决定了实际创建的产品时哪个了。 - 5、在工厂方法模式中,创建者通常会包含依赖于抽象产品的代码,而这些抽象产品是、由子类创建的,创建者不需要真的知道在制作哪种具体产品。 #### AbstractFactory(抽象工厂模式) >所谓抽象工厂模式就是她提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。 ![UML](./docs/readme/AbstractFactory-UML.png) **参与者** - AbstractFactory:抽象工厂。抽象工厂定义了一个接口,所有的具体工厂都必须实现此接口,这个接口包含了一组方法用来生产产品。 - ConcreteFactory:具体工厂。具体工厂是用于生产不同产品族。要创建一个产品,客户只需要使用其中一个工厂完全不需要实例化任何产品对象。 - AbstractProduct:抽象产品。这是一个产品家族,每一个具体工厂都能够生产一整组产品。 - Product:具体产品。 **优点** - 1、 抽象工厂隔离了具体类的生成,是的客户端不需要知道什么被创建。所有的具体工厂都实现了抽象工厂中定义的公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。 - 2、 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。 **缺点** 添加新的行为时比较麻烦。如果需要添加一个新产品族对象时,需要更改接口及其下所有子类,这必然会带来很大的麻烦。 **模式使用场景** - 1. 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。 - 2.系统中有多于一个的产品族,而每次只使用其中某一产品族。 - 3. 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。 - 4. 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。 **总结** - 1、 抽象工厂模式中主要的优点在于具体类的隔离,是的客户端不需要知道什么被创建了。其缺点在于增加新的等级产品结构比较复杂,需要修改接口及其所有子类。 #### Builder(建造模式) >对于建造者模式而已,它主要是将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。 ![UML](./docs/readme/Builder-UML.png) **参与者** - Builder:抽象建造者。它声明为创建一个Product对象的各个部件指定的抽象接口。 - ConcreteBuilder:具体建造者。实现抽象接口,构建和装配各个部件。 - Director:指挥者。构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象,它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。 - Product:产品角色。一个具体的产品对象。 **优点** - 1、将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,使得我们能够更加精确的控制复杂对象的产生过程。 - 2、将产品的创建过程与产品本身分离开来,可以使用相同的创建过程来得到不同的产品。也就说细节依赖抽象。 - 3、每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。 **缺点** - 1、建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。 - 2、如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。 **模式适用场景** - 1、需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。 - 2、隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。 **模式总结** - 1、建造者模式是将一个复杂对象的创建过程给封装起来,客户只需要知道可以利用对象名或者类型就能够得到一个完整的对象实例,而不需要关心对象的具体创建过程。 - 2、建造者模式将对象的创建过程与对象本身隔离开了,使得细节依赖于抽象,符合依赖倒置原则。可以使用相同的创建过程来创建不同的产品对象。 #### Prototype(原型模式) >原型模式就是用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。 ![UML](./docs/readme/Prototype-UML.png) **参与者** - Prototype:抽象原型类。声明克隆自身的接口。 - ConcretePrototype:具体原型类。实现克隆的具体操作。 - Client:客户类。让一个原型克隆自身,从而获得一个新的对象。 **优点** - 1、如果创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。 - 2、可以使用深克隆保持对象的状态。 - 3、原型模式提供了简化的创建结构。 **缺点** - 1、在实现深克隆的时候可能需要比较复杂的代码。 - 2、需要为每一个类配备一个克隆方法,而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事,必须修改其源代码,违背了“开闭原则”。 **模式使用场景** - 1、如果创建新对象成本较大,我们可以利用已有的对象进行复制来获得。 - 2、如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占内存不大的时候,也可以使用原型模式配合备忘录模式来应用。相反,如果对象的状态变化很大,或者对象占用的内存很大,那么采用状态模式会比原型模式更好。 - 3、需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。 **模式总结** - 1、原型模式向客户隐藏了创建对象的复杂性。客户只需要知道要创建对象的类型,然后通过请求就可以获得和该对象一模一样的新对象,无须知道具体的创建过程。 - 2、克隆分为浅克隆和深克隆两种。 - 3、我们虽然可以利用原型模式来获得一个新对象,但有时对象的复制可能会相当的复杂,比如深克隆。 #### Singleton(单例模式) >确保某一个类只有一个实例,并且提供一个全局访问点。 ![UML](./docs/readme/Singleton-UML.png) **参与者** - Singleton:单例。 **优点** - 1、节约了系统资源。由于系统中只存在一个实例对象,对与一些需要频繁创建和销毁对象的系统而言,单例模式无疑节约了系统资源和提高了系统的性能。 - 2、因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。 **缺点** - 1、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。 - 2、单例类的职责过重,在一定程度上违背了“单一职责原则”。 **模式使用场景** - 1、系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。 - 2、客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。 **总结** - 1. 单例模式中确保程序中一个类最多只有一个实例。 - 2. 单例模式的构造器是私有了,而且它必须要提供实例的全局访问点。 - 3. 单例模式可能会因为多线程的问题而带来安全隐患。 ### 结构型模式 >结构型模式主要是用于处理类或者对象的组合,它描述了如何来类或者对象更好的组合起来,是从程序的结构上来解决模块之间的耦合问题。它主要包括适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式这个七个模式。 #### Adapter(适配器模式) >所谓适配器模式就是将一个类的接口,转换成客户期望的另一个接口。它可以让原本两个不兼容的接口能够无缝完成对接。 ![UML](./docs/readme/Adapter-UML.png) **参与者** - Target:目标抽象类 。 - Adapter:适配器类 。通过在内部包装一个Adaptee,将源接口转成目标接口。 - Adaptee:适配者类 。需要适配的类。 - Client:客户类。 **优点** - 1. 将目标类和适配者类解耦,通过使用适配器让不兼容的接口变成了兼容,让客户从实现的接口解耦。 - 2. 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。 - 3. 灵活性和扩展性都非常好在不修改原有代码的基础上增加新的适配器类,符合“开闭原则”。 **使用场景** - 1. 系统需要使用现有的类,而这些类的接口不符合系统的需要。 - 2.想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类 **总结** - 1、当我们需要使用的一个现有的类,但是他的接口并不符合我们的需求时,我们可以使用适配器模式。 - 2、适配器模式分为类适配器和对象适配器,其中类适配器需要用到多重继承。 #### Bridge(桥接模式) >桥接模式就是讲抽象部分和实现部分隔离开来,使得他们能够独立变化。 桥接模式将继承关系转化成关联关系,封装了变化,完成了解耦,减少了系统中类的数量,也减少了代码量。 ![UML](./docs/readme/Bridge-UML.png) **参与者** - Abstraction:抽象类。 - RefinedAbstraction:扩充抽象类。 - Implementor:实现类接口。 - ConcreteImplementor:具体实现类 。 **优点** - 1、分离抽象接口及其实现部分。提高了比继承更好的解决方案。 - 2、桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。 - 3、实现细节对客户透明,可以对用户隐藏实现细节。 **缺点** - 1、桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。 - 2、桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。 **模式使用场景** - 1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 - 2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 - 3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。 **模式总结** - 1、桥接模式实现了抽象化与实现化的脱耦。他们两个互相独立,不会影响到对方。 - 2、对于两个独立变化的维度,使用桥接模式再适合不过了。 - 3、对于“具体的抽象类”所做的改变,是不会影响到客户。 #### Composite(组合模式) >组合模式组合多个对象形成树形结构以表示“整体-部分”的结构层次。它定义了如何将容器对象和叶子对象进行递归组合,使得客户在使用的过程中无须进行区分,可以对他们进行一致的处理。 ![UML](./docs/readme/Composite-UML.png) **参与者** - Component :组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。 - Leaf:叶子对象。叶子结点没有子结点。 - Composite:容器对象,定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。 **优点** - 1、可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易。 - 2、客户端调用简单,客户端可以一致的使用组合结构或其中单个对象。 - 3、定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构。 - 4、更容易在组合体内加入对象构件,客户端不必因为加入了新的对象构件而更改原有代码。 **缺点** - 1、使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联 **模式适用场景** - 1、需要表示一个对象整体或部分层次,在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,可以一致地对待它们。 - 2、让客户能够忽略不同对象层次的变化,客户端可以针对抽象构件编程,无须关心对象层次结构的细节。 **模式总结** - 1、 组合模式用于将多个对象组合成树形结构以表示“整体-部分”的结构层次。组合模式对单个对象(叶子对象)和组合对象(容器对象)的使用具有一致性。 - 2、 组合对象的关键在于它定义了一个抽象构建类,它既可表示叶子对象,也可表示容器对象,客户仅仅需要针对这个抽象构建进行编程,无须知道他是叶子对象还是容器对象,都是一致对待。 - 3、 组合模式虽然能够非常好地处理层次结构,也使得客户端程序变得简单,但是它也使得设计变得更加抽象,而且也很难对容器中的构件类型进行限制,这会导致在增加新的构件时会产生一些问题。 #### Decorator(装饰模式) >装饰者模式,动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。 ![UML](./docs/readme/Decorator-UML.png) **参与者** - Component: 抽象构件。是定义一个对象接口,可以给这些对象动态地添加职责。 - ConcreteComponent:具体构件。是定义了一个具体的对象,也可以给这个对象添加一些职责。 - Decorator: 抽象装饰类。是装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator存在的。 - ConcreteDecorator:具体装饰类,起到给Component添加职责的功能。 **优点** - 1、装饰者模式可以提供比继承更多的灵活性 - 2、可以通过一种动态的方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。 - 3、通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。 - 4、具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。 **缺点** - 1、会产生很多的小对象,增加了系统的复杂性 - 2、这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。 **模式的适用场景** - 1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。 - 2、需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。 **总结** - 1、 组合和委托可以在运行时动态的添加新的行为,而继承是静态的,在系统编译时就已经决定了对象的行为。 - 2、装饰者模式意味着一群装饰者类,这些类用来包装具体组件 - 3、装饰者可以在被装饰者的行为前面或者后面加上自己的行为,甚至可以将被装饰者的行为整个取代掉,从而达到特定的目的。 - 4、可以用多个装饰者包装一个组件。 - 5、装饰者一般对于组件的客户是透明的,除非客户程序依赖于组件的具体类型。 - 6、装饰者会导致设计中出现许多的小对象,如果过度的使用,会让系统变得更加复杂。 - 7、装饰者和被装饰者对象有相同的超类型。 #### Facade(外观模式) >为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 ![UML](./docs/readme/Facade-UML.png) **参与者** - Facade: 外观角色。知道哪些子系统类负责处理请求,将客户的请求代理给适合的子系统处理。 - SubSystem:子系统角色。实现子系统功能,处理Facade对象发来的请求。 **优点** - 1、引入外观模式,是客户对子系统的使用变得简单了,减少了与子系统的关联对象,实现了子系统与客户之间的松耦合关系。 - 2、只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类 - 3、降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程 **缺点** - 1、不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性 - 2、在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则” **使用场景** - 1、当要为一个复杂子系统提供一个简单接口时可以使用外观模式。 - 2、客户程序与多个子系统之间存在很大的依赖性。引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可移植性 **模式总结** - 1、 外观模式的主要优点就在于减少了客户与子系统之间的关联对象,使用客户对子系统的使用变得简单了,也实现了客户与子系统之间的松耦合关系。它的缺点就在于违背了“开闭原则”。 - 2、 如果需要实现一个外观模式,需要将子系统组合进外观中,然后将工作委托给子系统执行。 #### Flyweight(享元模式) >享元模式就是运行共享技术有效地支持大量细粒度对象的复用。 ![UML](./docs/readme/Flyweight-UML.png) **参与者** - Flyweight: 抽象享元类。所有具体享元类的超类或者接口,通过这个接口,Flyweight可以接受并作用于外部专题。 - ConcreteFlyweight: 具体享元类。指定内部状态,为内部状态增加存储空间。 - UnsharedConcreteFlyweight: 非共享具体享元类。指出那些不需要共享的Flyweight子类。 - FlyweightFactory: 享元工厂类。用来创建并管理Flyweight对象,它主要用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory就会提供一个已经创建的Flyweight对象或者新建一个(如果不存在)。 **优点** - 1、享元模式的优点在于它能够极大的减少系统中对象的个数。 - 2、享元模式由于使用了外部状态,外部状态相对独立,不会影响到内部状态,所以享元模式使得享元对象能够在不同的环境被共享。 **缺点** - 1、由于享元模式需要区分外部状态和内部状态,使得应用程序在某种程度上来说更加复杂化了。 - 2、为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。 **模式适用场景** - 1、如果一个系统中存在大量的相同或者相似的对象,由于这类对象的大量使用,会造成系统内存的耗费,可以使用享元模式来减少系统中对象的数量。 - 2、对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。 **模式总结** - 1、享元模式可以极大地减少系统中对象的数量。但是它可能会引起系统的逻辑更加复杂化。 - 2、享元模式的核心在于享元工厂,它主要用来确保合理地共享享元对象。 - 3、内部状态为不变共享部分,存储于享元享元对象内部,而外部状态是可变部分,它应当油客户端来负责。 #### Proxy(代理模式) >代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的引用。 ![UML](./docs/readme/Proxy-UML.png) **参与者** - Subject: 抽象角色。声明真实对象和代理对象的共同接口。 - Proxy: 代理角色。代理对象与真实对象实现相同的接口,所以它能够在任何时刻都能够代理真实对象。代理角色内部包含有对真实对象的引用,所以她可以操作真实对象,同时也可以附加其他的操作,相当于对真实对象进行封装。 - RealSubject: 真实角色。它代表着真实对象,是我们最终要引用的对象。 **优点** - 1、 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。 - 2、 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了的作用和保护了目标对象的 **缺点** - 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 - 2、 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。 **模式适用场景** - 1、 远程代理:为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。 - 2、 虚拟代理:通过使用过一个小的对象代理一个大对象。这样就可以减少系统的开销。 - 3、 保护代理:用来控制对真实对象的访问权限。 **模式总结** - 1、代理模式是通过使用引用代理对象来访问真实对象,在这里代理对象充当用于连接客户端和真实对象的中介者。 - 2、代理模式主要用于远程代理、虚拟代理和保护代理。其中保护代理可以进行访问权限控制。 ### 行为型模式 行为型模式主要是用于描述类或者对象是怎样交互和怎样分配职责的。它涉及到算法和对象间的职责分配,不仅描述对象或者类的模式,还描述了他们之间的通信方式,它将你的注意力从控制流转移到了对象间的关系上来。行为型类模式采用继承机制在类间分派行为,而行为型对象模式使用对象复合而不是继承。它主要包括如何11中设计模式:职责链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式。 #### ResponsibilityChain(责任链模式) >将对象组成一条链,发送者将请求发给链的第一个接收者,并且沿着这条链传递,直到有一个对象来处理它或者直到最后也没有对象处理而留在链末尾端。 ![UML](./docs/readme/ResponsibilityChain-UML.png) **参与者** - Handler: 抽象处理者。定义了一个处理请求的方法。所有的处理者都必须实现该抽象类。 - ConcreteHandler: 具体处理者。处理它所负责的请求,同时也可以访问它的后继者。如果它能够处理该请求则处理,否则将请求传递到它的后继者。 - Client: 客户类。 **优点** - 1、降低耦合度。它将请求的发送者和接受者解耦。 - 2、简化了对象。使得对象不需要知道链的结构。 - 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 - 4、增加新的请求处理类很方便。 **缺点** - 1、不能保证请求一定被接收。 - 2、系统性能将受到一定影响,而且在进行代码调试时不太方便;可能会造成循环调用。 - 3、可能不容易观察运行时的特征,有碍于除错。 **模式适用场景** - 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 - 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 - 3、可动态指定一组对象处理请求。 **总结** - 1、职责链模式将请求的发送者和接受者解耦了。客户端不需要知道请求处理者的明确信息,甚至不需要知道链的结构,它只需要将请求进行发送即可。 - 2、职责链模式能够非常方便的动态增加新职责或者删除职责。 - 3、客户端发送的请求可能会得不到处理。 - 4、处理者不需要知道链的结构,只需要明白他的后续者是谁就可以了。这样就简化了系统中的对象。 #### Command(命令模式) >命令模式将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。同时命令模式支持可撤销的操作。 ![UML](./docs/readme/Command-UML.png) **参与者** - Command: 抽象命令类。用来声明执行操作的接口。 - ConcreteCommand: 具体命令类。将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现Excute。 - Invoker: 调用者。要求该命令执行这个请求。 - Receiver: 接收者。知道如何实施与执行一个请求相关的操作,任何类都有可能成为一个接收者。 - Client:客户类。 **优点** - 1. 降低了系统耦合度 - 2. 新的命令可以很容易添加到系统中去。 **缺点** 使用命令模式可能会导致某些系统有过多的具体命令类。 **模式使用场景** - 1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。 - 2.系统需要在不同的时间指定请求、将请求排队和执行请求。 - 3.系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。 - 5.系统需要将一组操作组合在一起,即支持宏命令。 **总结** - 1. 命令模式的本质就是将命令对象进行封装打包,将发出命令的责任和执行命令的责任进行割开。 - 2. 命令模式中发送者只需要知道如何发送请求命令,无须关心命令执行具体过程。 - 3. 在发送者和接收者两者间是通过命令对象进行沟通的。请求命令本身就当做一个对象在两者间进行传递,它封装了接收者和一组动作。 - 4. 命令模式支持撤销。 - 5. 命令模式队列请求和日志请求。 #### Interpreter(解释器模式) > 所谓解释器模式就是定义语言的文法,并且建立一个解释器来解释该语言中的句子。 ![UML](./docs/readme/Interpreter-UML.png) **参与者** - AbstractExpression: 抽象表达式。声明一个抽象的解释操作,该接口为抽象语法树中所有的节点共享。 - TerminalExpression: 终结符表达式。实现与文法中的终结符相关的解释操作。实现抽象表达式中所要求的方法。文法中每一个终结符都有一个具体的终结表达式与之相对应。 - NonterminalExpression: 非终结符表达式。为文法中的非终结符相关的解释操作。 - Context: 环境类。包含解释器之外的一些全局信息。 - Client: 客户类。 **优点** - 1、 可扩展性比较好,灵活。 - 2、 增加了新的解释表达式的方式。 - 3、 易于实现文法。 **缺点** - 1、 执行效率比较低,可利用场景比较少。 - 2、 对于复杂的文法比较难维护。 **模式适用场景** - 1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 - 2、一些重复出现的问题可以用一种简单的语言来进行表达。 - 3、文法较为简单。 **模式总结** - 1、在解释器模式中由于语法是由很多类表示的,所以可扩展性强。 - 2、虽然解释器的可扩展性强,但是如果语法规则的数目太大的时候,该模式可能就会变得异常复杂。所以解释器模式适用于文法较为简单的。 - 3、解释器模式可以处理脚本语言和编程语言。常用于解决某一特定类型的问题频繁发生情况。 #### Iterator(迭代器模式) >所谓迭代器模式就是提供一种方法顺序访问一个聚合对象中的各个元素,而不是暴露其内部的表示。 ![UML](./docs/readme/Iterator-UML.png) **参与者** - Iterator: 抽象迭代器:所有迭代器都需要实现的接口,提供了游走聚合对象元素之间的方法。 - ConcreteIterator: 具体迭代器。利用这个具体的迭代器能够对具体的聚合对象进行遍历。每一个聚合对象都应该对应一个具体的迭代器。 - Aggregate: 抽象聚合类。 - ConcreteAggregate: 具体聚合类。实现creatorIterator()方法,返回该聚合对象的迭代器。 #### Mediator(中介者模式) >所谓中介者模式就是用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 ![UML](./docs/readme/Mediator-UML.png) **参与者** - Mediator: 抽象中介者。定义了同事对象到中介者对象之间的接口。 - ConcreteMediator: 具体中介者。实现抽象中介者的方法,它需要知道所有的具体同事类,同时需要从具体的同事类那里接收信息,并且向具体的同事类发送信息。 - Colleague: 抽象同事类。 - ConcreteColleague: 具体同事类。每个具体同事类都只需要知道自己的行为即可,但是他们都需要认识中介者。 **优点** - 1、 简化了对象之间的关系,将系统的各个对象之间的相互关系进行封装,将各个同事类解耦,使系统成为松耦合系统。 - 2、 减少了子类的生成。 - 3、 可以减少各同事类的设计与实现。 **缺点** 由于中介者对象封装了系统中对象之间的相互关系,导致其变得非常复杂,使得系统维护比较困难。 **模式适用场景** - 1、 系统中对象之间存在比较复杂的引用关系,导致他们之间的依赖关系结构混乱而且难以复用该对象。 - 2、 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。 **模式总结** - 1、 在中介者模式中通过引用中介者对象,将系统中有关的对象所引用的其他对象数目减少到最少。它简化了系统的结构,将系统由负责的网状结构转变成简单的星形结构,中介者对象在这里起到中转和协调作用。 - 2、 中介者类是中介者模式的核心,它对整个系统进行控制和协调,简化了对象之间的交互,还可以对对象间的交互进行进一步的控制。 - 3、 通过使用中介者模式,具体的同事类可以独立变化,通过引用中介者可以简化同事类的设计和实现。 - 4、 就是由于中介者对象需要知道所有的具体同事类,封装具体同事类之间相互关系,导致中介者对象变得非常复杂,系统维护起来较为困难。 #### Memento(备忘录模式) >所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。 ![UML](./docs/readme/Memento-UML.png) **参与者** - Originator: 原发器。负责创建一个备忘录,用以记录当前对象的内部状态,通过也可以使用它来利用备忘录恢复内部状态。同时原发器还可以根据需要决定Memento存储Originator的那些内部状态。 - Memento: 备忘录。用于存储Originator的内部状态,并且可以防止Originator以外的对象访问Memento。在备忘录Memento中有两个接口,其中Caretaker只能看到备忘录中的窄接口,它只能将备忘录传递给其他对象。Originator可以看到宽接口,允许它访问返回到先前状态的所有数据。 - Caretaker: 负责人。负责保存好备忘录,不能对备忘录的内容进行操作和访问,只能够将备忘录传递给其他对象。 **优点** - 1、 给用户提供了一种可以恢复状态的机制。可以是用户能够比较方便地回到某个历史的状态。 - 2、 实现了信息的封装。使得用户不需要关心状态的保存细节。 **缺点** 消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。 **模式适用场景** - 1、 需要保存一个对象在某一个时刻的状态或部分状态。 - 2、 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过负责人可以间接访问其内部状态。 **模式总结** - 1、 备忘录模式可以实现在不破坏封装的前提下,捕获一个类的内部状态,并且在该对象之外保存该对象的状态,保证该对象能够恢复到历史的某个状态。 - 2、 备忘录模式实现了内部状态的封装,除了创建它的原发器之外其他对象都不能够访问它。 - 3、 备忘录模式会占用较多的内存,消耗资源。 #### Observer(观察者模式) >观察者模式定义了对象之间的一对多依赖关系,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并且自动更新。 ![UML](./docs/readme/Observer-UML.png) **参与者** - Subject:目标。他把所有对观察者对戏的引用保存在一个聚集里,每一个主题都可以有多个观察者。 - Observer:观察者。为所有的具体观察者定义一个接口,在得到主题的通知时能够及时的更新自己。 - ConcreteSubject:具体主题。将有关状态存入具体观察者对象。在具体主题发生改变时,给所有的观察者发出通知。 - ConcreteObserver:具体观察者。实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态相协调。 #### State(状态模式) >当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。 ![UML](./docs/readme/State-UML.png) **参与者** - Context:环境类。可以包括一些内部状态。 - State: 抽象状态类。State定义了一个所有具体状态的共同接口,任何状态都实现这个相同的接口,这样一来,状态之间就可以互相转换了。 - ConcreteState:具体状态类。具体状态类,用于处理来自Context的请求,每一个ConcreteState都提供了它对自己请求的实现,所以,当Context改变状态时行为也会跟着改变。 **优点** - 1、封装了转换规则。 - 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 - 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 - 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 - 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。 **缺点** - 1、状态模式的使用必然会增加系统类和对象的个数。 - 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 - 3、状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。 **模式适用场景** - 1、对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。 - 2、代码中包含大量与对象状态有关的条件语句 **模式总结** - 1、状态模式允许一个对象基于内部状态而拥有不同的行为。 - 2、Context会将行为委托给当前状态对象。 - 3、状态模式对“开闭原则”支持不是很好。 #### Strategy(策略模式) >策略模式就是定义了算法族,分别封装起来,让他们之前可以互相转换,此模式然该算法的变化独立于使用算法的客户。 ![UML](./docs/readme/Strategy-UML.png) **参与者** - Context: 环境类。维护一个Strategy对象的引用,用一个ConcreteStrategy来配置,可定义一个接口来让Strategy访问它的数据。 - Strategy: 抽象策略类。定义所有支持算法的公共接口。Context使用这个接口来调用某个Concretestrategy定义的算法。 - ConcreteStrategy: 具体策略类。封装了具体的算法实现。 **优点** - 1、策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。 - 2、策略模式提供了可以替换继承关系的办法。 - 3、使用策略模式可以避免使用多重条件转移语句。 **缺点** - 1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。 - 2、策略模式将造成产生很多策略类, **使用场景** - 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 - 2、一个系统需要动态地在几种算法中选择一种。 - 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。 #### TemplateMethod(模板方法模式) >模板方法模式就是在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。 ![UML](./docs/readme/TemplateMethod-UML.png) **参与者** - AbstractClass: 抽象类。实现了一个模板,实现算法的基本骨架,具体子类将重定义primitiveOperation()方法以实现一个算法步骤。 - ConcreteClass: 具体子类。实现primitiveOperation()方法以完成算法中与特定子类相关的步骤。 **优点** - 1、模板方法模式在定义了一组算法,将具体的实现交由子类负责。 - 2、模板方法模式是一种代码复用的基本技术。 - 3、模板方法模式导致一种反向的控制结构,通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,符合“开闭原则”。 **缺点** - 每一个不同的实现都需要一个子类来实现,导致类的个数增加,是的系统更加庞大。 **使用场景** - 1、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。 - 2、 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。 - 3、控制子类的扩展。 **模式总结** - 1、 模板方法模式定义了算法的步骤,将这些步骤的实现延迟到了子类。 - 2、 模板方法模式为我们提供了一种代码复用的重要技巧。 - 3、 模板方法模式的抽象类可以定义抽象方法、具体方法和钩子。 - 4、 为了防止子类改变算法的实现步骤,我们可以将模板方法声明为final。 #### Visitor(访问者模式) >访问者模式即表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 ![UML](./docs/readme/Visitor-UML.png) **参与者** - Vistor: 抽象访问者。为该对象结构中的ConcreteElement的每一个类声明的一个操作。 - ConcreteVisitor: 具体访问者。实现Visitor申明的每一个操作,每一个操作实现算法的一部分。 - Element: 抽象元素。定义一个Accept操作,它以一个访问者为参数。 - ConcreteElement: 具体元素 。实现Accept操作。 - ObjectStructure: 对象结构。能够枚举它的元素,可以提供一个高层的接口来允许访问者访问它的元素。 **优点** - 1、使得新增新的访问操作变得更加简单。 - 2、能够使得用户在不修改现有类的层次结构下,定义该类层次结构的操作。 - 3、将有关元素对象的访问行为集中到一个访问者对象中,而不是分散搞一个个的元素类中。 **缺点** - 1、增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求。 - 2、破坏封装。当采用访问者模式的时候,就会打破组合类的封装。 - 3、比较难理解。貌似是最难的设计模式了。 **模式适用场景** - 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 - 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。 **模式总结** - 1、访问者模式封装了对象结构元素之上的操作,使得新增元素的操作变得非常简单。所以它比较适用于那么对象结构很少变化的类。 - 2、访问者模式中对象结构存储了不同类型的元素对象,以供不同访问者访问。 **参考资料** 设计模式之间的关系 ![设计模式之间的关系](./docs/design-1.jpg) 设计模式总概况: ![设计模式总概况](./docs/design-2.jpg) **相关链接** https://www.cnblogs.com/chenssy/p/3357683.html