设计模式总结
1、设计模式三种分类
创建型模式
特点:创建型模式关注对象的创建和实例化过程,提供了灵活的方式来创建对象。它们将对象的创建与使用分离,提供了更高的灵活性和可维护性。这些模式通常通过使用接口、抽象类、工厂方法、原型等机制来实现对象的创建和初始化。
优点:可以封装对象的创建过程,使系统与具体对象的创建方式解耦;提供了灵活性和可扩展性,可以方便地添加新的创建方式;能够提供单例、享元等特定类型的对象实例化机制。
缺点:引入了额外的对象创建的复杂性,增加了系统的复杂性。
使用场景:当需要灵活地创建对象,将对象的创建与使用解耦时可以使用创建型模式。常见的使用场景包括对象的初始化过程复杂、对象的创建过程需要隐藏或者由其他对象来控制、系统需要独立于具体类进行实例化等。
结构型模式
特点:结构型模式关注对象之间的组合和关联关系,以及如何通过这些关系来构建更大的结构。这些模式有助于定义对象之间的接口和交互方式,实现松耦合和灵活性。它们通常通过对象的组合、聚合、继承、接口等机制来定义对象之间的关系。
优点:通过定义良好的接口和关系,可以使系统的各个部分可以独立变化;提供了代码组织和可扩展性的好处;能够对系统的结构进行抽象和解耦。
缺点:增加了系统的复杂性,可能导致系统中对象之间的关系复杂化。
使用场景:当需要定义对象之间的关系、组织结构以及对这些结构进行抽象和解耦时可以使用结构型模式。常见的使用场景包括系统中有复杂的对象关系需要管理、需要对对象的组织和结构进行灵活处理、需要实现接口和适配不同的实现等。
行为型模式
特点:行为型模式关注对象之间的通信和交互方式,以及如何有效地管理和组织对象的行为。这些模式定义了对象之间的通信协议和交互方式,使得系统中的对象能够协同工作并完成复杂的任务。它们通常通过定义对象之间的职责、算法、状态机等机制来管理对象的行为。
优点:定义了清晰的对象交互方式和通信协议;使得系统更易于理解、扩展和维护;提供了一种灵活的方式来管理对象的行为。
缺点:增加了系统的复杂性,可能导致对象之间的交互复杂化。
使用场景:当需要定义对象之间的交互和通信方式,以实现更好的代码组织和可维护性时可以使用行为型模式。常见的使用场景包括需要定义对象的交互协议和状态机、需要实现对象的责任链、需要观察和响应对象的状态变化等。
每种类型的设计模式都有其适用的场景和优缺点。选择适当的设计模式取决于具体的需求和设计目标,以及对系统的灵活性、可维护性和可扩展性的要求。在实际应用中,通常会根据具体情况综合考虑并结合多种设计模式来构建高质量的软件系统。
2、每个设计模式具体归属
创建型设计模式(Creational Patterns)
工厂方法模式(FactoryMethodPattern):通过工厂方法创建对象,将对象的创建与使用分离。
抽象工厂模式(AbstractFactoryPattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定其具体类。
单例模式(SingletonPattern):确保一个类只有一个实例,并提供全局访问点。
建造者模式(BuilderPattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
原型模式(PrototypePattern):通过复制现有对象来创建新对象。
各个创建型模式的优势以及不足
创建型设计模式是一组用于创建对象的设计模式,它们都旨在解决对象创建过程中的不同问题。下面是创建型设计模式及其之间的对比以及各自的优势和不足:
简单工厂模式(Simple Factory Pattern):
优势:简单工厂模式通过一个共同的工厂类来创建对象,将对象的创建逻辑封装在一个地方,客户端只需要与工厂进行交互,而不需要关心具体的创建过程。
不足:添加新的产品时需要修改工厂类的代码,违反了开闭原则,而且当产品类型较多时,工厂类可能会变得庞大而复杂。
工厂方法模式(Factory Method Pattern):
优势:工厂方法模式将对象的创建延迟到子类中实现,每个具体子类都可以创建自己特定类型的对象,符合开闭原则。
不足:每个具体子类只能创建一种类型的对象,如果需要创建多个相关产品,需要创建多个具体工厂类。
抽象工厂模式(Abstract Factory Pattern):
优势:抽象工厂模式提供一个接口用于创建一系列相关或相互依赖的对象,客户端通过接口使用这些对象,能够在不指定具体实现类的情况下创建一系列产品。
不足:新增加一种产品族时,需要修改抽象工厂的接口及所有的具体工厂类,违反了开闭原则。
建造者模式(Builder Pattern):
优势:建造者模式将对象的构建过程分解成多个步骤,并提供一个指导者来控制构建过程,可以灵活地创建不同表示的对象。
不足:如果产品的组成部分相对稳定,而只需要改变产品的组装顺序或方式时,使用建造者模式可能会显得过于复杂。
原型模式(Prototype Pattern):
优势:原型模式通过复制现有对象来创建新的对象,避免了对象的创建过程,提高了创建对象的效率。
不足:每个类都需要实现克隆方法,包括引用类型的属性也需要实现深拷贝,否则克隆对象的修改会影响到原始对象。
总体而言,这些创建型设计模式各有其适用的场景和优缺点。选择适当的设计模式取决于具体的需求和情况。
如果只有少量产品类型且不经常变化,可以考虑简单工厂模式
如果需要创建多个产品类型,可以考虑工厂方法模式
如果有多个相关产品族需要创建,可以考虑抽象工厂模式
如果创建过程复杂且需要灵活地创建不同表示的对象,可以考虑建造者模式
如果需要复制对象来创建新对象,可以考虑原型模式。
结构型设计模式(StructuralPatterns)
适配器模式(AdapterPattern):将一个类的接口转换成客户端所期望的另一种接口。
桥接模式(BridgePattern):将抽象部分与它的实现部分分离,使它们可以独立地变化。
组合模式(CompositePattern):将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
装饰者模式(DecoratorPattern):动态地给对象添加额外的职责。
外观模式(FacadePattern):为复杂的子系统提供一个简化的接口。
享元模式(FlyweightPattern):通过共享细粒度对象来减少内存使用和提高性能。
代理模式(ProxyPattern):为其他对象提供一种代理以控制对这个对象的访问。
各个结构型设计模式不足和优势
结构型设计模式是一组用于解决对象之间组合关系、接口定义和实现等结构性问题的设计模式。下面是结构型设计模式及其之间的对比以及各自的优势和不足:
适配器模式(Adapter Pattern):
优势:适配器模式可以将一个类的接口转换成客户端所期望的接口,使得原本不兼容的类可以协同工作。
不足:适配器模式增加了一个额外的适配器类,增加了代码的复杂性。
桥接模式(Bridge Pattern):
优势:桥接模式将抽象部分与实现部分分离,使它们可以独立地变化,从而提高了系统的灵活性。
不足:桥接模式增加了类的数量,增加了系统的复杂性。
组合模式(Composite Pattern):
优势:组合模式允许将对象组织成树形结构,并以统一的方式处理单个对象和组合对象,简化了客户端的代码。
不足:组合模式可能会导致设计中的一些对象变得过于一般化,使得系统变得复杂。
装饰者模式(Decorator Pattern):
优势:装饰者模式允许动态地为对象添加额外的行为,而无需修改现有对象的结构,提供了比继承更灵活的功能扩展方式。
不足:装饰者模式会增加许多具体装饰者类,增加了类的数量,增加了代码的复杂性。
外观模式(Facade Pattern):
优势:外观模式提供了一个统一的接口,隐藏了子系统的复杂性,简化了客户端与子系统之间的交互。
不足:如果子系统发生变化,可能需要修改外观类的代码,违反了开闭原则。
享元模式(Flyweight Pattern):
优势:享元模式通过共享相同或相似的细粒度对象,减少了内存的消耗,提高了系统的性能。
不足:享元模式可能会引入对象共享的复杂性,需要注意线程安全性。
代理模式(Proxy Pattern):
优势:代理模式提供了一个代理对象,可以控制对真实对象的访问,并在必要时添加额外的逻辑。
不足:代理模式增加了一个代理类,增加了代码的复杂性,同时可能引入间接性和性能损耗。
总体而言,这些结构型设计模式各有其适用的场景和优缺点。选择适当的设计模式取决于具体的需求和情况。如果需要将接口转换成另一种接口,可以考虑适配器模式;如果需要将抽象部分和实现部分分离,可以考虑桥接模式;如果需要处理对象的树形结构,可以考虑组合模式;如果需要动态地为对象添加行为,可以考虑装饰者模式;如果需要简化子系统的访问,可以考虑外观模式;如果需要共享对象以节省内存,可以考虑享元模式;如果需要控制对对象的访问,可以考虑代理模式。
行为型设计模式(BehavioralPatterns)
责任链模式(ChainofResponsibilityPattern):将请求的发送者和接收者解耦,使多个对象都有机会处理这个请求。
命令模式(CommandPattern):将请求封装成对象,使得可以用不同的请求对客户进行参数化。
解释器模式(InterpreterPattern):给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
迭代器模式(IteratorPattern):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
中介者模式(MediatorPattern):用一个中介对象来封装一系列的对象交互,使各对象之间不需要显式地相互引用。
备忘录模式(MementoPattern):在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
观察者模式(ObserverPattern):定义对象间的一种一对多的依赖关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
状态模式(StatePattern):允许一个对象在其内部状态改变时改变它的行为。
策略模式(StrategyPattern):定义一系列算法,将每个算法都封装起来,并使它们可以互换。
模板方法模式(TemplateMethodPattern):定义一个算法的骨架,将一些步骤延迟到子类中实现。
访问者模式(VisitorPattern):表示一个作用于某对象结构中的各元素的操作,可以使新增操作更容易。
各个行为型设计模式的不足和优势
行为型设计模式是一组用于对象之间的通信和交互的设计模式,它们解决了对象之间的责任分配和算法封装等行为相关的问题。下面是行为型设计模式及其之间的对比以及各自的优势和不足:
策略模式(Strategy Pattern):
优势:策略模式定义了一族算法,并使它们可以相互替换,客户端可以独立于具体算法进行操作。
不足:客户端需要了解不同的策略类,增加了类的数量。
观察者模式(Observer Pattern):
优势:观察者模式定义了对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知。
不足:观察者模式可能导致依赖关系过于紧密,一旦一个观察者出现问题,可能影响其他观察者。
命令模式(Command Pattern):
优势:命令模式将请求封装成对象,使得可以在不同的请求发送者和接收者之间进行解耦,同时支持请求的排队和记录操作。
不足:可能会增加大量的具体命令类,增加了类的数量。
迭代器模式(Iterator Pattern):
优势:迭代器模式提供了一种统一的方式来访问集合对象的元素,客户端无需关心集合的具体实现细节。
不足:如果需要遍历不同类型的集合,可能需要实现多个不同的迭代器。
责任链模式(Chain of Responsibility Pattern):
优势:责任链模式将请求的发送者和接收者解耦,允许多个对象依次处理请求,直到找到合适的处理者。
不足:责任链可能会导致请求的处理者不明确,可能会造成请求无法被处理。
状态模式(State Pattern):
优势:状态模式允许对象在内部状态改变时改变其行为,将状态的转换和行为的实现解耦。
不足:状态模式可能会导致状态类过多,增加了类的数量。
访问者模式(Visitor Pattern):
优势:访问者模式将数据结构与数据操作分离,可以在不改变数据结构的情况下增加新的操作。
不足:如果数据结构发生变化,可能需要修改所有的访问者类,违反了开闭原则。
模板方法模式(Template Method Pattern):
优势:模板方法模式定义了一个算法的骨架,将具体步骤的实现延迟到子类中,可以在不改变算法结构的情况下重新定义算法的特定步骤。
不足:如果算法的结构需要变化,可能需要修改模板方法的代码,违反了开闭原则。
解释器模式(Interpreter Pattern):
优势:解释器模式用于解释和执行特定语言的语法或表达式,可以将语法规则表示为抽象语法树,从而实现灵活的语言解释。
不足:解释器模式的设计较为复杂,特别是对于复杂的语法规则和表达式。
中介者模式(Mediator Pattern):
优势:中介者模式通过引入中介对象,将对象之间的复杂交互关系转化为中介者与对象之间的简单交互,降低了对象之间的耦合性。
不足:中介者模式可能会导致中介对象的逻辑复杂化,一旦中介对象发生改变,可能影响到整个系统。
备忘录模式(Memento Pattern):
优势:备忘录模式允许在不破坏封装性的情况下捕获和恢复对象的内部状态,可以实现对象状态的保存和回滚。
不足:备忘录模式可能会增加对象的内存消耗,尤其是当需要保存大量状态时。
3、创建型设计模式
抽象工厂模式
特点:提供一个创建一系列或相关依赖对象的接口,而无须指定它们具体的类。
结构图:
建造者模式
特点:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
结构图:
工厂方法模式
特点:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使一个类的实例化延迟到其子类。
结构图:
工厂模式的优势:
当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化。当设计者在设计标准之间进行权衡的时候,了解多个创建型模式可以给设计者更多的选择余 地。
原型模式
特点:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
结构图:
单例模式
特点:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
结构图:
4、行为型设计模式
观察者模式
特点:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
结构图:
模板方法
特点:定义一个操作的算法骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
结构图:
命令模式
特点:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;可以对请求排队或记录请求日志,以及支持可撤销的操作。
结构图:
状态模式
特点:允许一个对象在其内部状态改变时改变它的行为,让对象看起来似乎修改了它的类。
结构图:
职责链模式
特点:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
结构图:
解释器模式
特点:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
结构图:
中介者模式
特点:用一个中介对象来封装一系列的对象交互。中介者使各对像不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
结构图:
访问者模式
特点:表示一个作用于某对象结构中的各元素的操作,使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
结构图:
策略模式
特点:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
结构图:
备忘录模式
特点:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
结构图:
迭代器模式
特点:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内部表示。
结构图:
5、结构性设计模式
适配器模式
特点:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
结构图:
桥接模式
特点:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
结构图:
组合模式
特点:将对象组合成树形结构以表示’部分-整体’的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
结构图:
装饰模式
特点:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更加灵活
结构图:
外观模式
特点:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
结构图:
享元模式
特点:为运用共享技术有效地支持大量细粒度的对象。
结构图:
代理模式
特点:称为其他对象提供一种代理以控制对这个对象的访问。
结构图:
6、七大设计模式原则
单一职责原则(Single Responsibility Principle,SRP):一个类应该只有一个引起变化的原因。换句话说,一个类应该只负责一项功能或任务。这样可以提高类的内聚性,并降低其对其他类的依赖性。
开放封闭原则(Open-Closed Principle,OCP):软件实体(类、模块、函数等)应该对扩展是开放的,对修改是封闭的。通过使用抽象和接口,可以使系统在不修改现有代码的情况下进行扩展。
里氏替换原则(Liskov Substitution Principle,LSP):子类对象应该能够替换掉父类对象,并且不会引发错误或破坏程序的正确性。换句话说,子类应该保持与父类之间的行为兼容性,以确保代码的正确性。
依赖倒置原则(Dependency Inversion Principle,DIP):高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于具体实现细节,而具体实现细节应该依赖于抽象。
接口隔离原则(Interface Segregation Principle,ISP):客户端不应该依赖于它不需要的接口。一个类不应该强迫它的客户端依赖于那些它们不使用的方法。
迪米特法则(Law of Demeter,LoD):一个对象应该与其他对象之间保持最小的交互。一个类应该尽量减少对其他类的依赖,尤其是不应直接与其他类的内部结构进行交互。
合成复用原则(Composite Reuse Principle,CRP):尽量使用对象组合和聚合,而不是通过继承来达到代码的复用。这样可以避免继承的静态绑定关系,并提高系统的灵活性和可维护性。
。
4. 依赖倒置原则(Dependency Inversion Principle,DIP):高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于具体实现细节,而具体实现细节应该依赖于抽象。
5. 接口隔离原则(Interface Segregation Principle,ISP):客户端不应该依赖于它不需要的接口。一个类不应该强迫它的客户端依赖于那些它们不使用的方法。
6. 迪米特法则(Law of Demeter,LoD):一个对象应该与其他对象之间保持最小的交互。一个类应该尽量减少对其他类的依赖,尤其是不应直接与其他类的内部结构进行交互。
7. 合成复用原则(Composite Reuse Principle,CRP):尽量使用对象组合和聚合,而不是通过继承来达到代码的复用。这样可以避免继承的静态绑定关系,并提高系统的灵活性和可维护性。