设计原则:
-
识别应用中变化的方面,把它们和不变的方面分开
- 把会变化的部分取出并封装,这样,以后你就可以修改或扩展这个部分,而不会影响其他不需要变化的部分。
-
针对接口编程,而不是针对实现编程。
- “针对接口编程”真正的意思是“针对超类型编程”。
-
优先使用组合而不是继承
策略模式(Strategy):策略模式定义了一个算法族,分别封装起来,使得它们之间可以互相变换。策略让算法的变化独立于使用它的客户。
观察者模式(Observer):定义对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
**设计原则:**尽量做到交互的对象之间的松耦合设计。
开放-关闭原则:类应该对扩展开放,但对修改关闭。
装饰者模式(Decorator):动态地将额外责任附加到对象上。用于扩展功能,装饰者提供子类变化之外的弹性替代方案。
工厂模式(Factory Method):定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法让类把实例化推迟到子类。
依赖倒置原则(Dependency Inversion Principle):依赖抽象,不依赖具体类。
抽象工厂模式(Abstract Factory):提供一个接口来创建相关或依赖对象的家族,而不需要制定具体类。
单件模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。
go语言的一个单件模式的示例:
package singleton
import (
"sync"
)
// Singleton 类型定义
type Singleton struct {
// ... 其他字段
}
var instance *Singleton
var once sync.Once
// GetInstance 用于获取单一实例
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
命令模式(Command):把请求封装为对象,以便用不同的请求、队列或者日志请求来参数化其他对象,并支持可撤销的操作。
适配器模式(Adapter):将一个类的接口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作。
外观模式(Facade):为子系统中的一组接口提供了一个统一的接口。外观定义了一个更高级别的接口,使得子系统更容易使用。
最少知识原则:只和你的密友谈话。
- 当你在设计一个系统时,对于任何对象,都要注意它所交互的类的数量,以及它和这些类的交互。
- 这个原则防止我们创建有大量的类在一起的设计,免得系统一部分的变化会连锁影响到其他部分。当你在许多类之间造成许多依赖时,你的系统就是一个易碎的系统,需要花费许多成本维护,而且复杂得难以让别人理解。
模版方法模式(Template Method):在一个方法中定义一个算法的骨架,而把一些步骤延迟到子类。模版方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
好莱坞原则:不要打电话给(调用)我们,我们会打电话给(调用)你。
迭代器模式(Iterator Pattern):提供一种方式,可以访问一个聚合对象中的元素而又不暴露其潜在的表示。
单一责任原则:一个类应该只有一个变化的原因。
- 类的每个责任都是一个潜在的变化区域。超过一个责任,意味着超过一个变化区域。
- 这个原则知道我们要让每个类保持单一责任。
组合模式(Composite Pattern):允许你将对象组合成树形结构来表现部分-整体层次结构。组合让客户可以统一处理个别对象和对象组合。
状态模式(State):允许对象在内部状态改变时改变其行为。对象看起来好像改变了它的类。
代理模式(Proxy):为另一个对象提供一个替身或占位符来控制对这个对象的访问。
复合模式(Composite):复合模式把两个或更多的模式结合成一个解决方案,解决重复发生或一般的问题。
模式:模式是在某个上下文中针对某个问题的解决方案。
- 上下文指某个模式适用的情况。这应该是一种会不断出现的情况。
- 问题指在此上下文中你想要达到的目标,但也要考虑该上下文中发生的任何约束。
- 解决方案就是你所追求的东西:一个通用的设计,所有人都可以用来解决目标和约束集。
创建型模式牵涉到对象实例化,这类模式都提供一种将客户从需要实例化的对象中解耦的方式。
单件、抽象工厂、工厂方法、生成器、原型
行为型模式都牵涉到类和对象如何交互,以及分配责任。
模版方法、命令、迭代器、观察者、策略、状态、责任链、解释器、中介者、备忘录、访问者
结构型模式让你组合类或对象得到更大的结构。
装饰者、代理、外观、组合、适配器、桥接、蝇量
类模式描述类之间的关系如何通过继承定义。类模式中的关系是在编译时建立的。
模版方法、工厂方法、适配器、解释器
对象模式描述对象之间的关系,而且对象模式主要通过组合定义。对象模式中的关系通常在运行时创建,更加动态和有弹性。
组合、装饰者、代理、策略、外观、命令、迭代器、观察者、抽象工厂、单件、状态、桥接、中介者、责任链、访问者、备忘录、原型、蝇量
反模式告诉你如何从问题到达一个坏的解决方案。
使用桥接模式不知改变你的实现,也改变你的抽象。
优缺点:
优点 | 用途和缺点 |
---|---|
解耦实现,不再永久绑定到接口 | 在跨多个平台的图形和窗口系统中很有用。 |
抽象和实现可以独立扩展 | 当你需要以不同的方式改变接口和实现时,桥接很好用。 |
针对“具体的抽象类”的变化,不会影响客户 | 缺点是增加了复杂度。 |
使用生成器模式来封装一个产品的构造过程,并允许按步骤构造。
优缺点:
优点 | 用途和缺点 |
---|---|
封装复杂对象的构造方式 | 经常被用来建造组合结构 |
允许对象用多个步骤构造,并且可以改变过程(和一步到位的工厂不同) | 和使用工厂相比,构造对象需要更多客户的领域知识。 |
向客户隐藏产品的内部表现 | |
产品的实现可以被替换,因为客户只看到抽象的接口 |
当你要把一个处理请求的机会给予多余一个的对象时,使用责任链模式。
优缺点:
优点 | 用途和缺点 |
---|---|
解耦请求的发送者和接收者 | 经常用在窗体系统中,处理鼠标点击和键盘事件。 |
简化你的对象,因为它不需要知道链的结构以及保持对其成员的直接引用 | 并不保证请求一定会被执行;如果没有对象处理它,可能会掉到链的末尾(这可以是优点或缺点) |
允许你通过改变链的成员或次序,动态地添加或移除责任。 | 运行时可能会难以观察和调试 |
当某个类的一个实例可以用于提供许多虚拟实例时,使用蝇量模式。
优缺点:
优点 | 用途和缺点 |
---|---|
减少运行时对象实例的树木,节省内存 | 当一个类有许多实例,而这些实例能够用一致的方法控制时,用蝇量。 |
把许多“虚拟”对象的状态集中放进一个地方 | 蝇量模式的一个缺点在于,一旦你实现了它,那么类的单个逻辑实例,将无法拥有和其他实例不同的独立行为。 |
使用解释器模式为语言建造解释器。
优缺点:
优点 | 用途和缺点 |
---|---|
将每一个语法规则表达成一个类,使得语言容易实现 | 当你需要实现一门简单的语言时,使用解释器 |
因为语法由类表达,你可以轻易地改变或扩展该语言 | 当你有一个单间的语法,而且简单比效率重要时,适合用解释器 |
通过在类结构中添加方法,可以添加解释之外的新行为,例如打印格式的美化和更复杂的程序验证。 | 用于脚本和编程语言 |
当语法规则的数目很大时,这个模式可能变得笨重。在这些情况下,一个解析器/编译器的生成器可能更适合。 |
使用中介者模式来集中相关对象之间复杂的沟通和控制方式。
优缺点:
优点 | 用途和缺点 |
---|---|
通过将中介者支持的对象从系统中解耦,增加对象的复用性。 | 中介者常用于协调相关的GUI组件 |
通过将控制逻辑集中,简化了系统的维护。 | 中介者模式的一个缺点是,如果设计不当,中介者对象本身会变得过度复杂。 |
简化以减少系统中对象之间发送的消息的变化。 |
当你需要让对象返回之前的某个状态时,例如,你的用户请求“撤销”,使用备忘录模式。
优缺点:
优点 | 用途和缺点 |
---|---|
保持被保存的状态处于关键对象外面,有助于维护内聚 | 备忘录用于保持状态 |
保持关键对象的数据封装 | 使用备忘录的一个缺点是,保存和恢复状态可能相当耗时 |
提供容易实现的恢复能力 | 在Java系统中,考虑使用序列化(Serialization)来保存系统的状态。 |
当创建给定类的实例昂贵或者复杂时,使用原型模式。
优缺点:
优点 | 用途和缺点 |
---|---|
向客户隐藏制作新实例的复杂性 | 在一个复杂的类层次中,当系统必须创建许多类型的新对象时,应该考虑原型。 |
提供一种选择,让客户生成类型未知的对象 | 使用原型的缺点是,对象的复制有时候相当复杂。 |
在某些环境下,复制对象比创建新对象更有效 |
当你想要为一个对象组合增加能力,且封装不重要时,使用访问者模式。
优缺点:
优点 | 用途和缺点 |
---|---|
允许你添加操作到组合结构,而不改变结构本身 | 使用访问者模式时,组合类的封装被打破 |
添加新操作相对容易 | 因为涉及到导游的功能,因此组合结构更加难以变化。 |
由访问者执行的操作的代码被集中化了 |