设计模式的六大原则

1. 设计模式的六大原则

1.1 开闭原则(Open Close Principle)

开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

1.2 里氏代换原则(Liskov Substitution Principle)

里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

1.3 依赖倒转原则(Dependence Inversion Principle)

这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

1.4 接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

1.5 迪米特法则,又称最少知道原则(Demeter Principle)

最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

1.6 合成复用原则(Composite Reuse Principle)

合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。

2 设计模式

2.1 结构型

2.1.1 Abstract Factory(抽象工厂)

目的

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

适用性

  • 一个系统要独立于它的产品的创建、组合和表示时
  • 一个系统要由多个产品系列中的一个来配置时
  • 当你要强调一系列相关的产品对象的设计以便进行联合使用时
  • 当你提供一个产品类库,而只想显示他们的接口而不是实现时

结构图

示例代码

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
class Button;
class WinButton : public Button {};
class MacButton : public Button {};

class Border;
class WinBorder : public Border {};
class MacBorder : public Border {};

class AbstractFactory
{
public:
virtual Button * CreateButton();
virtual Border * CreateBorder();
};

class WinFactory
{
public:
virtual WinButton * CreateButton() { return new WinButton; }
virtual WinBorder * CreateBorder() { return new WinBorder; }
};

class MacFactory
{
public:
virtual MacButton * CreateButton { return new MacButton; }
virtual MacBorder * CreateBorder { return new MacBorder; }
};

客户端如下使用:
AbstractFactory * fac;
switch(style)
{
case Mac:
fac = new MacFactory;
break;
case Win:
fac = new WinFactory;
break;
default :
break;
}
Button * button = fac->CreateButton(); //隐藏了细节实现,封装了多个产品
Border * border = fac->CreateBorder();

2.1.2 Builder—生成器

目的

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

适用性

  1. 当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式时
  2. 当构造过程必须允许被构造的对象有不同的表示时

结构图

示例代码

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
class Builder
{
public:
virtual void buildProduct() {};
virtual void buildPart1() {};
virtual void buildPart2() {};

virtual Produc *GetProduct() {return 0};
};

class ConcreateBuilderA : public Builder
{
public:
virtual void buildProduct() {
m_product = new Porduct;
}

virtual void buildPart1() {
m_product.setPart1(0,0,111);
.....
}

virtual void buildPart2() {
m_product.setPart2(0,0,1333);
}


virtual Produc *GetProduct() {
return m_product;
}
private:
Produc * m_product;
};

class ConcreateBuilderB : public Builder
{
public:
virtual void buildProduct() {
m_product = new Porduct;
}

virtual void buildPart1() {
m_product.setPart1(xxxx,qqqqq,111);
}

virtual void buildPart2() {
m_product.setPart2(xxxxx,bbbb,1333);
}

virtual Produc *GetProduct() {
return m_product;
}
private:
Produc * m_product;
};

class Director
{
public:
Director(Builder * builder){ m_builder = builder; }

Construct()
{
m_builder->buildPart1();
m_builder->buildPart2();
m_builder->buildPart3();
}

private:
Builder * m_builder;
};

2.2 结构型

2.2.1 Adapter—适配器模式

意图:

将一个类的接口转换为客户希望的另外一个接口。此模式是的原本由于接口不兼容而不能一起工作的那些类可以一起工作

适用性:

  1. 想使用一个已经存在的类,而它的接口不符合你的需求。
  2. 想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作
  3. 想使用一些已经存在的类,但是不可能对每一个都进行子类化以匹配他们的接口。对象适配器可以适配他的父类接口

伪代码:

此模式较为简单,有两种实现方式,一种采用组合方式,另外一种采用继承方式

组合方式

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
 class Adaptee
{
public
SpecificRequest();
....other...
};

class Target
{
public:
virtual void Request();
};

class Adapter : public Target
{
public:
virtual void Request()
{
m_daptee.SpecificRequest();
}
private:
Adaptee m_daptee;
};

void main()
{
Target * mytarget = new Adapter;
mytarget->Request();
delete mytarget;
mytarget = NULL;
}

继承方式

直接将Adapter中的继承替换为多继承,继承Target,同时还继承Adaptee

1
2
3
4
5
6
7
8
class Adapter : public Targetpublic Adaptee
{
public:
virtual void Request()
{
SpecificRequest();
}
};

2.3 行为型

2.3.1 Strategy (策略)

目的

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

适用性

当存在以下情况时使用Strategy模式

  1. 许多相关的类仅仅是行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
  2. 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。
  3. 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
  4. 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句

结构图

示例代码

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
#include <iostream>
using namespace std;

class Strategy {
public:
virtual void Interface() = 0;
virtual ~Strategy() { }
};

class ConcreteStrategyA :public Strategy {
public:
void Interface() {
cout << "ConcreteStrategyA::Interface..." << endl;
}
};

class ConcreteStrategyB :public Strategy {
public:
void Interface() {
cout << "ConcreteStrategyB::Interface..." << endl;
}
};

class Context {
public:
Context(Strategy *stg) {
_stg = stg;
}

void DoAction() {
_stg->Interface();
}

private:
Strategy *_stg;
};

int main() {
Strategy *ps = new ConcreteStrategyA();
Context *pc = new Context(ps);
pc->DoAction();

delete pc;
delete ps;

return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!