设计模式与重构

软件开发相关知识,主要是一些设计思想.

基本设计思想

开闭原则

由Bertrand Meyer提出的开闭原则(Open Closed Principle)是指,软件应该对扩展开放,而对修改关闭。这里的意思是在增加新功能的时候,能不改代码就尽量不要改,如果只增加代码就完成了新功能,那是最好的。

里氏替换原则

里氏替换原则是Barbara Liskov提出的,这是一种面向对象的设计原则,即如果我们调用一个父类的方法可以成功,那么替换成子类调用也应该完全可以运行。

设计模式

创建型模式

这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复用性。

工厂方法模式

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。

工厂方法即Factory Method,是一种对象创建型模式。

抽象工厂

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

抽象工厂模式(Abstract Factory)是一个比较复杂的创建型模式

抽象工厂模式和工厂方法不太一样,它要解决的问题比较复杂,不但工厂是抽象的,产品是抽象的,而且有多个产品需要创建,因此,这个抽象工厂会对应到多个实际工厂,每个实际工厂负责创建多个实际产品

生成器

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

生成器模式(Builder)是使用多个“小型”工厂来最终创建出一个完整对象。

当我们使用Builder的时候,一般来说,是因为创建这个对象的步骤比较多,每个步骤都需要一个零部件,最终组合成一个完整的对象。

原型

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式,即Prototype,是指创建新对象的时候,根据现有的一个原型来创建。

单例

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

单例模式(Singleton)的目的是为了保证在一个进程中,某个类有且仅有一个实例。

1
2
3
4
5
6
7
8
public class Singleton {
// 静态字段引用唯一实例:
public static final Singleton INSTANCE = new Singleton();

// private构造方法保证外部无法实例化:
private Singleton() {
}
}

结构性模式

这类模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。

适配器

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适配器模式是Adapter,也称Wrapper

桥接

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

组合

将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

组合模式(Composite)经常用于树形结构,为了简化代码,使用Composite可以把一个叶子节点与一个父节点统一起来处理。

在XML或HTML中,从根节点开始,每个节点都可能包含任意个其他节点,这些层层嵌套的节点就构成了一颗树。

装饰器

动态地给一个对象添加一些额外的职责。就增加功能来说,相比生成子类更为灵活。

装饰器(Decorator)模式,是一种在运行期动态给某个对象的实例增加功能的方法

外观

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

外观模式,即Facade,是一个比较简单的模式。它的基本思想如下:

如果客户端要跟许多子系统打交道,那么客户端需要了解各个子系统的接口,比较麻烦。如果有一个统一的“中介”,让客户端只跟中介打交道,中介再去跟各个子系统打交道,对客户端来说就比较简单。所以Facade就相当于搞了一个中介。

享元

运用共享技术有效地支持大量细粒度的对象。

享元(Flyweight)的核心思想很简单:如果一个对象实例一经创建就不可变,那么反复创建相同的实例就没有必要,直接向调用方返回一个共享的实例就行,这样即节省内存,又可以减少创建对象的过程,提高运行速度。

代理

为其他对象提供一种代理以控制对这个对象的访问。

代理模式,即Proxy,它和Adapter模式很类似

行为模式

这类模式负责对象间的高效沟通和职责委派。

责任链

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

责任链模式(Chain of Responsibility)是一种处理请求的模式,它让多个处理器都有机会处理该请求,直到其中某个处理成功为止

命令

命令模式(Command)是指,把请求封装成一个命令,然后执行该命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class TextEditor {
private StringBuilder buffer = new StringBuilder();

public void copy() {
...
}

public void paste() {
String text = getFromClipBoard();
add(text);
}

public void add(String s) {
buffer.append(s);
}

public void delete() {
if (buffer.length() > 0) {
buffer.deleteCharAt(buffer.length() - 1);
}
}

public String getState() {
return buffer.toString();
}
}
public interface Command {
void execute();
}
public class CopyCommand implements Command {
// 持有执行者对象:
private TextEditor receiver;

public CopyCommand(TextEditor receiver) {
this.receiver = receiver;
}

public void execute() {
receiver.copy();
}
}

public class PasteCommand implements Command {
private TextEditor receiver;

public PasteCommand(TextEditor receiver) {
this.receiver = receiver;
}

public void execute() {
receiver.paste();
}
}

解释器

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

迭代器

提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式(Iterator)实际上在Java的集合类中已经广泛使用了。我们以List为例,要遍历ArrayList,即使我们知道它的内部存储了一个Object[]数组,也不应该直接使用数组索引去遍历,因为这样需要了解集合内部的存储结构

中介模式

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

中介模式(Mediator)又称调停者模式,它的目的是把多方会谈变成双方会谈,从而实现多方的松耦合。

Mediator模式经常用在有众多交互组件的UI上。为了简化UI程序,MVC模式以及MVVM模式都可以看作是Mediator模式的扩展。

备忘录模式

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。

备忘录模式(Memento),主要用于捕获一个对象的内部状态,以便在将来的某个时候恢复此状态。

其实我们使用的几乎所有软件都用到了备忘录模式。最简单的备忘录模式就是保存到文件,打开文件。对于文本编辑器来说,保存就是把TextEditor类的字符串存储到文件,打开就是恢复TextEditor类的状态。对于图像编辑器来说,原理是一样的,只是保存和恢复的数据格式比较复杂而已。Java的序列化也可以看作是备忘录模式。

观察者

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

观察者模式(Observer)又称发布-订阅模式(Publish-Subscribe:Pub/Sub)。它是一种通知机制,让发送通知的一方(被观察方)和接收通知的一方(观察者)能彼此分离,互不影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Store {
private List<ProductObserver> observers = new ArrayList<>();
private Map<String, Product> products = new HashMap<>();

// 注册观察者:
public void addObserver(ProductObserver observer) {
this.observers.add(observer);
}

// 取消注册:
public void removeObserver(ProductObserver observer) {
this.observers.remove(observer);
}

public void addNewProduct(String name, double price) {
Product p = new Product(name, price);
products.put(p.getName(), p);
// 通知观察者:
observers.forEach(o -> o.onPublished(p));
}

public void setProductPrice(String name, double price) {
Product p = products.get(name);
p.setPrice(price);
// 通知观察者:
observers.forEach(o -> o.onPriceChanged(p));
}
}

状态

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

状态模式(State)经常用在带有状态的对象中。

策略

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

策略模式:Strategy,是指,定义一组算法,并把其封装到一个对象中。然后在运行时,可以灵活的使用其中的一个算法

模板方法

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模板方法(Template Method)是一个比较简单的模式。它的主要思想是,定义一个操作的一系列步骤,对于某些暂时确定不下来的步骤,就留给子类去实现好了,这样不同的子类就可以定义出不同的步骤。

访问者

表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

访问者模式(Visitor)是一种操作一组对象的操作,它的目的是不改变对象的定义,但允许新增不同的访问者,来定义新的操作。

参考链接

  1. 设计模式 - 廖雪峰的官方网站 (liaoxuefeng.com)
  2. 设计模式目录:22种设计模式 (refactoringguru.cn)
  3. 设计模式简介 | 菜鸟教程 (runoob.com)
-------------本文结束感谢您的阅读-------------
感谢阅读.

欢迎关注我的其它发布渠道