0%

常见设计模式

设计模式五大原则

  • 单一职责:对于一个类来说,应该仅有一个引起他变化的原因,功能要单一,降低耦合性。
  • 开放-封闭原则:对于扩展开放,对于更改时封闭的
  • 依赖倒转原则:高层模块不应该依赖低层模块,应该都依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。
  • 里氏替换原则:子类必须能够替换掉他们的父类;子类继承父类,所以字类拥有父类所有非私有方法。
  • 迪米特法则:强调了类之间的松耦合。如果两个类不必彼此直接通信,那么这两个类就不应该发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

简单工厂模式

简单工厂模式又叫 静态方法模式,因为工厂类中定义了一个静态方法用于创建对象。比如,一台咖啡机就可以理解为一个工厂模式,你只需要按下想喝的咖啡品类的按钮(摩卡或拿铁),它就会给你生产一杯相应的咖啡,你不需要管它内部的具体实现,只要告诉它你的需求即可。

优点

  • 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象;
  • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量;
  • 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

缺点

  • 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
  • 违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂,比如多加一个case。当产品类型较多时,简单工厂的判断将会非常多,不容易维护。
  • 简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Factory {
public static String createProduct(String product) {
String result = null;
switch (product) {
case "Mocca":
result = "摩卡";
break;
case "Latte":
result = "拿铁";
break;
default:
result = "其他";
break;
}
return result;
}
}

工厂模式

工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫多态工厂(Polymorphic 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
//抽象对象接口
class Waiter {
public:
void sweep() {
cout << "扫地" << endl;
}
void wash() {
cout << "洗碗" << endl;
}
void send() {
cout << "送菜" << endl;
}
};
//具体对象接口
class WaiterA :public Waiter {};
class WaiterB:public Waiter{};
//抽象工厂类
class WaiterFactory {
public:
virtual Waiter* createWaiter() = 0;
};
//具体工厂类
class WaiterAFactory :public WaiterFactory {
public:
Waiter* createWaiter() {
return new WaiterA;
}
};
//具体工厂类
class WaiterBFactory :public WaiterFactory {
public:
Waiter* createWaiter() {
return new WaiterB;
}
};

抽象工厂模式

抽象工厂模式(Abstract Factory Pattern),提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。 但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。

一个工厂可以生产多个对象,这些对象被称为一个产品族。比如这里有两个工厂,每个工厂生产一套产品(产品族)。

优点:

  • 隔离了具体类的生成,使得客户端并不需要知道什么被创建
  • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象
  • 增加新的产品族很方便,无须修改已有系统,符合开闭原则。(这里说符合开闭原则是针对产品族的增加,即多一个品牌方)

缺点:

  • 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了开闭原则。(这里是违背的,是因为如果我们增加了一个产品,比如鞋子,其实就要修改抽象类,修改具体类,也就是修改了源码,所以其实是违背了开闭原则)
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
// 武器工厂
public abstract class WuQiFactory { }
// 头饰工厂
public abstract class TouShiFactory { }
// 身体工厂
public abstract class ShenTiFactory { }

// 皮肤工厂
public abstract class PiFuFactory
{
// 创建武器,头饰,身体
public abstract WuQiFactory CreateWuQi();
public abstract TouShiFactory CreateTouShi();
public abstract ShenTiFactory CreateShenTi();
}

// 圣诞武器具体实现
public class ShengDanWuQi : WuQiFactory { }
// 圣诞头饰
public class ShengDanTouShi : TouShiFactory { }
// 圣诞身体
public class ShengDanShenTi : ShenTiFactory { }

// 皮肤工厂
public class ShengDanPiFuFactory : PiFuFactory
{
// 创建武器,头饰,身体
public override WuQiFactory CreateWuQi()
{
Console.WriteLine("... 创建圣诞武器 ...");
return new ShengDanWuQi();
}
public override TouShiFactory CreateTouShi()
{
Console.WriteLine("... 创建圣诞头饰 ...");
return new ShengDanTouShi();
}
public override ShenTiFactory CreateShenTi()
{
Console.WriteLine("... 创建圣诞身体 ...");
return new ShengDanShenTi();
}
}

// 青春武器具体实现
public class QinChunWuQi : WuQiFactory { }
// 青春头饰
public class QinChunTouShi : TouShiFactory { }
// 青春身体
public class QinChunShenTi : ShenTiFactory { }

// 皮肤工厂
public class QinChunFuFactory : PiFuFactory
{
// 创建武器,头饰,身体
public override WuQiFactory CreateWuQi()
{
Console.WriteLine(" --- 创建青春武器 --- ");
return new QinChunWuQi();
}
public override TouShiFactory CreateTouShi()
{
Console.WriteLine(" --- 创建青春头饰 --- ");
return new QinChunTouShi();
}
public override ShenTiFactory CreateShenTi()
{
Console.WriteLine(" --- 创建青春身体 --- ");
return new QinChunShenTi();
}
}

工厂方法总结

一.特点
简单工厂模式:实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。在这个模式中,工厂类是整个模式的关键所在。它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。用户在使用时可以直接根据工厂类去创建所需的实例,而无需了解这些对象是如何创建以及如何组织的。有利于整个软件体系结构的优化。

工厂方法模式:工厂方法是粒度很小的设计模式,因为模式的表现只是一个抽象的方法。提前定义用于创建对象的接口,让子类(具体工厂)决定实例化具体的某一个类,实现了可扩展。在这个模式中,工厂类和产品类往往可以依次对应。即一个抽象工厂对应一个抽象产品,一个具体工厂对应一个具体产品,这个具体的工厂就负责生产对应的产品。

抽象工厂模式:抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。它有多个抽象产品类,每个抽象产品类可以派生出多个具体产品类,一个抽象工厂类,可以派生出多个具体工厂类,每个具体工厂类可以创建多个具体产品类的实例。每一个模式都是针对一定问题的解决方案,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式针对的是多个产品等级结果。

二.优点
简单工厂模式:工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅”消费”产品。简单工厂模式通过这种做法实现了对责任的分割。简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。通过它,外界可以从直接创建具体产品对象的尴尬局面中摆脱出来。外界与具体类隔离开来,偶合性低。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。

工厂方法模式:工厂方法模式是为了克服简单工厂模式的缺点(主要是为了满足OCP)而设计出来的。简单工厂模式的工厂类随着产品类的增加需要增加很多方法(或代码),而工厂方法模式每个具体工厂类只完成单一任务,代码简洁。工厂方法模式完全满足OCP,即它有非常良好的扩展性

抽象工厂模式:抽象工厂模式主要在于应对“新系列”的需求变化。分离了具体的类,抽象工厂模式帮助你控制一个应用创建的对象的类,因为一个工厂封装创建产品对象的责任和过程。它将客户和类的实现分离,客户通过他们的抽象接口操纵实例,产品的类名也在具体工厂的实现中被分离,它们不出现在客户代码中。它使得易于交换产品系列。一个具体工厂类在一个应用中仅出现一次——即在它初始化的时候。这使得改变一个应用的具体工厂变得很容易。它只需改变具体的工厂即可使用不同的产品配置,这是因为一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻改变。它有利于产品的一致性。当一个系列的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要,而抽象工厂很容易实现这一点。抽象工厂模式有助于这样的团队的分工,降低了模块间的耦合性,提高了团队开发效率。

三.缺点
简单工厂模式:当产品有复杂的多层等级结构时,工厂类只有自己,以不变应万变,就是模式的缺点。因为工厂类集中了所有产品创建逻辑,一旦增加产品或者删除产品,整个系统都要受到影响。系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,有可能造成工厂逻辑过于复杂,违背了”开放–封闭”原则(OCP).另外,简单工厂模式通常使用静态工厂方法,这使得无法由子类继承,造成工厂角色无法形成基于继承的等级结构。

工厂方法模式:假如某个具体产品类需要进行一定的修改,很可能需要修改对应的工厂类。当同时需要修改多个产品类的时候,对工厂类的修改会变得相当麻烦。比如说,每增加一个产品,相应的也要增加一个子工厂,会加大了额外的开发量。

抽象工厂模式:抽象工厂模式在于难于应付“新对象”的需求变动。难以支持新种类的产品。难以扩展抽象工厂以生产新种类的产品。这是因为抽象工厂几乎确定了可以被创建的产品集合,支持新种类的产品就需要扩展该工厂接口,这将涉及抽象工厂类及其所有子类的改变。

四.适用范围
简单工厂模式:工厂类负责创建的对象比较少,客户只知道传入了工厂类的参数,对于始何创建对象(逻辑)不关心。

工厂方法模式:支持多扩展少修改的OCP原则。

  • 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
  • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
  • 客户不关心创建产品的细节,只关心产品的品牌。

抽象工厂模式:

  1. 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
  2. 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
  3. 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

建造者模式

建造者模式(Builder Pattern)将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。

优点

  • 封装性好,构建和表示分离
  • 扩展性好,各个具体的建造者相互独立,有利于系统的解耦
  • 控制细节风险,客户端无需详知细节,建造者细化创建过程

缺点

  • 产品的组成部分必须相同,这限制了其使用范围
  • 产品内部发生变化,建造者需同步修改,后期维护成本较大

适用场景 :

  • 结构复杂 : 对象 有非常复杂的内部结构 , 有很多属性 ;
  • 分离创建和使用 : 想把复杂对象的创建和使用分离 ;
  • 当创造一个对象需要很多步骤时 , 适合使用建造者模式 ;
  • 当创造一个对象 只需要一个简单的方法就可以完成 , 适合使用工厂模式 ;
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/// 产品角色(Product)
class ProductMeal {
public:
ProductMeal() {
std::cout << "ProductMeal Hello" << std::endl;
};
~ProductMeal() {
std::cout << "ProductMeal Bye" << std::endl;
};
void setBurger(const std::string &iburger) {
mBurger = iburger;
}
void setDrink(const std::string &idrink) {
mDrink = idrink;
}
void setSnacks(const std::string &isnacks) {
mSnacks = isnacks;
}

void showMeal(){
std::cout << "Burger is " << mBurger << std::endl;
std::cout << "Drink is " << mDrink << std::endl;
std::cout << "Snacks is " << mSnacks << std::endl;
}

private:
std::string mBurger;
std::string mDrink;
std::string mSnacks;
};
/// 抽象建造者(AbstractBuilder)
class AbstractBuilder
{
public:
virtual ~AbstractBuilder() = default;
//抽象方法:
virtual void buildBurger() = 0;
virtual void buildDrink() = 0;
virtual void buildSnacks() = 0;
virtual ProductMeal* getMeal() = 0;

protected:
AbstractBuilder()= default;

};
/// 具体建造者(ConcreteBuilder)
class ConcreteBuilderMeal_A : public AbstractBuilder{ /// 套餐A
public:
ConcreteBuilderMeal_A(){
std::cout << "ConcreteBuilderMeal_A Hello" << std::endl;
meal = new ProductMeal();
}
~ConcreteBuilderMeal_A() override{
std::cout << "ConcreteBuilderMeal_A Bye" << std::endl;
delete meal;
}
void buildBurger() override {
meal->setBurger("Veg Burger");
};
void buildDrink() override {
meal->setDrink("coke");
}
void buildSnacks() override {
meal->setSnacks("French fries");
}
ProductMeal* getMeal() override {
return meal;
}
private:
ProductMeal *meal;
};

class ConcreteBuilderMeal_B : public AbstractBuilder{ /// 套餐B
public:
ConcreteBuilderMeal_B(){
std::cout << "ConcreteBuilderMeal_B Hello" << std::endl;
meal = new ProductMeal();
}
~ConcreteBuilderMeal_B() override{
std::cout << "ConcreteBuilderMeal_B Bye" << std::endl;
delete meal;
}
void buildBurger() override {
meal->setBurger("Chicken Burger");
};
void buildDrink() override {
meal->setDrink("pepsi");
}
void buildSnacks() override {
meal->setSnacks("Onion rings");
}
ProductMeal* getMeal() override {
return meal;
}
private:
ProductMeal *meal;
};
class ConcreteBuilderMeal_C : public AbstractBuilder{ /// 套餐C
public:
ConcreteBuilderMeal_C(){
std::cout << "ConcreteBuilderMeal_C Hello" << std::endl;
meal = new ProductMeal();
}
~ConcreteBuilderMeal_C() override{
std::cout << "ConcreteBuilderMeal_C Bye" << std::endl;
delete meal;
}
void buildBurger() override {
meal->setBurger("Veg Burger");
};
void buildDrink() override {
meal->setDrink("cafe");
}
void buildSnacks() override {
meal->setSnacks("French fries");
}
ProductMeal* getMeal() override {
return meal;
}
private:
ProductMeal *meal;
};
/// 指挥者(Director)
class Director
{
public:
Director() {
std::cout << "Director Hello" << std::endl;
};
~Director() {
std::cout << "Director Bye" << std::endl;
}
//具体实现方法
void setBuilder(AbstractBuilder *iBuilder){
this->builder = iBuilder;
}
//封装组装流程,返回建造结果
ProductMeal *construct(){
assert(builder!= nullptr);
builder->buildBurger(); /// 制作顺序
builder->buildDrink();
builder->buildSnacks();
return builder->getMeal();
}
private:
AbstractBuilder *builder= nullptr;
};

单例模式

单例模式(Singleton Pattern)是一种常用的模式,有一些对象我们往往只需要一个,比如全局缓存、浏览器中的 window 对象等。单例模式用于保证一个类仅有一个实例,并提供一个访问它的全局访问点。懒汉模式使用static可以很简单地实现

优点:不会频繁地创建和销毁对象,浪费系统资源。

使用场景:IO 、数据库连接、Redis 连接等。

  • 需要频繁实例化然后销毁的对象。
  • 创建对象时耗时过多或耗资源过多,但又经常用到的对象。
  • 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
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
#include <iostream>

using namespace std;

//懒汉式单例模式
class SingletonLazy {
private:
static SingletonLazy* instance;
// 构造函数私有化, 不让外界利用new创建实例
SingletonLazy() {
cout << "懒汉式单例模式\n";
}
public:
static SingletonLazy* getInstance() {
//第一次引用时才被实例化
if (instance == nullptr) {
instance = new SingletonLazy;
}
return instance;
}
};

SingletonLazy* SingletonLazy::instance = nullptr;

//饿汉式单例模式
class SingletonHungry {
private:
static SingletonHungry* instance2;
SingletonHungry() {
cout << "饿汉式单例模式\n";
}
public:
static SingletonHungry* getInstance() {
return instance2;
}
};
//加载时实例化
SingletonHungry* SingletonHungry::instance2 = new SingletonHungry;


int main() {

cout << "action: \n";

return 0;
}

适配器模式

在实际生活中,也存在适配器的使用场景,比如:港式插头转换器、电源适配器和 USB 转接口。而在软件工程中,适配器模式的作用是解决两个软件实体间的接口不兼容的问题。 使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体就可以一起工作。

主要优点:

  • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
  • 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
  • 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

适用场景

  • 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
  • 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
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
#include <iostream>
#include <memory>
#include <string>

class Target
{
public:
virtual bool NewRequest(const int value)
{
std::cout << "Calling new request method!" << std::endl;
return true;
}

virtual ~Target(){}
};

class Adaptee
{
public:
bool OldRequest(const std::string& strValue)
{
std::cout << "Calling old request method!" << std::endl;
return true;
}
};

class Adaptor : public Target
{
private:
std::shared_ptr<Adaptee> m_smartAdaptee;

public:
Adaptor(std::shared_ptr<Adaptee> adaptee)
{
m_smartAdaptee = std::move(adaptee);
}

bool NewRequest(const int value)
{
std::cout << "I'm new request method" << std::endl;
std::string strValue = std::to_string(value);
m_smartAdaptee->OldRequest(strValue);

return true;
}
};

int main()
{
std::shared_ptr<Target> target(new Adaptor(std::make_shared<Adaptee>()));
target->NewRequest(1);

system("pause");
return 0;
}

观察者模式

观察者模式是定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

img

优点

  • 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色;
  • 观察者模式在观察目标和观察者之间建立一个抽象的耦合;
  • 观察者模式支持广播通信;
  • 观察者模式符合开闭原则(对拓展开放,对修改关闭)的要求。

缺点

  • 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃;
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

应用场景

  • 一个对象的行为依赖于另一个对象的状态。或者换一种说法,当被观察对象(目标对象)的状态发生改变时 ,会直接影响到观察对象的行为。
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
/\* \* 观察者(消息接收方) \*/
interface Observer {
public void update(String message);
}
/\* \* 具体的观察者(消息接收方) \*/
class ConcrereObserver implements Observer {
private String name;

public ConcrereObserver(String name) {
this.name = name;
}

@Override
public void update(String message) {
System.out.println(name + ":" + message);
}
}

/\* \* 被观察者(消息发布方) \*/
interface Subject {
// 增加订阅者
public void attach(Observer observer);
// 删除订阅者
public void detach(Observer observer);
// 通知订阅者更新消息
public void notify(String message);
}
/\* \* 具体被观察者(消息发布方) \*/
class ConcreteSubject implements Subject {
// 订阅者列表(存储信息)
private List<Observer> list = new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
list.add(observer);
}
@Override
public void detach(Observer observer) {
list.remove(observer);
}
@Override
public void notify(String message) {
for (Observer observer : list) {
observer.update(message);
}
}
}

发布者订阅模式

在软件架构中,发布/订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,然后分别发送给不同的订阅者。 同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者存在。

应用场景

  • 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
  • 作为事件总线,来实现不同组件间或模块间的通信。

img

策略模式

策略模式是指定义一系列算法,将每个算法都封装起来,并且使他们之间可以相互替换。

  • 优点:策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为。干掉复杂难看的if-else。
  • 缺点:调用时,必须提前知道都有哪些策略模式类,才能自行决定当前场景该使用何种策略。

应用场景

  • 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
  • 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。
  • 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。

比如我们要出去旅游,选择性很多,可以选择骑车、开车、坐飞机、坐火车等,就可以使用策略模式,把每种出行作为一种策略封装起来,后面增加了新的交通方式了,如超级高铁、火箭等,就可以不需要改动原有的类,新增交通方式即可,这样也符合软件开发的开闭原则。 策略模式实现代码如下:

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
//Strategt类,定义所有支持的算法的公共接口
class Strategy {
public:
virtual ~Strategy() {};
virtual void AlgorithmInterface() = 0;
};

//ConcreteStrategy 封装了具体的算法或行为,继承Strategy
class ConcreteStrategyA : public Strategy{
void AlgorithmInterface() {
cout << "算法A实现" << endl;
}
};

class ConcreteStrategyB : public Strategy {
void AlgorithmInterface() {
cout << "算法B实现" << endl;
}
};

class ConcreteStrategyC : public Strategy {
void AlgorithmInterface() {
cout << "算法C实现" << endl;
}
};

//Context,用一个ConcreteStrategy来配置,维护一个对Strategy的引用
class Context {
public:
Context(Strategy* strategy) : m_strategy(strategy) {};
~Context() { free_ptr(m_strategy); }
void AlgorithmInterface() {
m_strategy->AlgorithmInterface();
};
private:
Strategy* m_strategy;
};

模板方法模式

模板方法模式由两部分结构组成:抽象父类和具体的实现子类。通常在抽象父类中封装了子类的算法框架,也包括实现一些公共方法以及封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。

优点

  • 良好的扩展性,封装不变部分,扩展可变部分,把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继续扩展。例如增加一个新的功能很简单,只要再增加一个子类,实现父类的基本方法就可以了。
  • 提取公共部分代码,便于维护,减小维护升级成本,基本操作由父类定义,子类实现
  • 基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能,符合开闭原则。

缺点:

  • 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

使用场合

模板方法是一种代码复用的基本技术。它们在类库中尤为重要,它们提取了类库中的公共行为。在使用模板方法时,很重要的一点是模板方法应该指明哪些操作是可以被重定义的,以及哪些是必须被重定义的。要有效的重用一个抽象类,子类编写者必须明确了解哪些操作是设计为有待重定义的。

img

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

class AbstractClass
{
public:
void TemplateMethod()
{
PrimitiveOperation1();
cout<<"TemplateMethod"<<endl;
PrimitiveOperation2();
}

protected:
virtual void PrimitiveOperation1()
{
cout<<"Default Operation1"<<endl;
}

virtual void PrimitiveOperation2()
{
cout<<"Default Operation2"<<endl;
}
};

class ConcreteClassA : public AbstractClass
{
protected:
virtual void PrimitiveOperation1()
{
cout<<"ConcreteA Operation1"<<endl;
}

virtual void PrimitiveOperation2()
{
cout<<"ConcreteA Operation2"<<endl;
}
};

class ConcreteClassB : public AbstractClass
{
protected:
virtual void PrimitiveOperation1()
{
cout<<"ConcreteB Operation1"<<endl;
}

virtual void PrimitiveOperation2()
{
cout<<"ConcreteB Operation2"<<endl;
}
};

int main()
{
AbstractClass *pAbstractA = new ConcreteClassA;
pAbstractA->TemplateMethod();

AbstractClass *pAbstractB = new ConcreteClassB;
pAbstractB->TemplateMethod();

if (pAbstractA) delete pAbstractA;
if (pAbstractB) delete pAbstractB;
}

代理模式

代理模式是给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

优点

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度;
  • 可以灵活地隐藏被代理对象的部分功能和服务,也增加额外的功能和服务。

缺点

  • 由于使用了代理模式,因此程序的性能没有直接调用性能高;
  • 使用代理模式提高了代码的复杂度。

使用场景:

  • 当我们想要隐藏某个类时,可以为其提供代理类
  • 当一个类需要对不同的调用者提供不同的调用权限时,可以使用代理类来实现(代理类不一定只有一个,我们可以建立多个代理类来实现,也可以在一个代理类中金进行权限判断来进行不同权限的功能调用)
  • 当我们要扩展某个类的某个功能时,可以使用代理模式,在代理类中进行简单扩展(只针对简单扩展,可在引用委托类的语句之前与之后进行)

举一个生活中的例子:比如买飞机票,由于离飞机场太远,直接去飞机场买票不太现实,这个时候我们就可以上携程 App 上购买飞机票,这个时候携程 App 就相当于是飞机票的代理商。

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

class Subject //Subject 定义了RealSubject和Proxy的共用接口..这样就在任何使用RealSubject的地方都可以使用Proxy
{
public:
virtual void func()
{
cout << "Subject" << endl;
}
};

class RealSubject :public Subject // RealSubject 定义proxy所代表的真实实体
{
public:
virtual void func()
{
cout << "RealSubject" << endl;
}
};

class Proxy : public Subject //proxy 保存一个引用使得代理可以访问实体,并且提供一个于Subject的接口相同的接口 这样代理就可以用来替代实体
{
RealSubject real;
public:
virtual void func()
{
cout << "Proxy" << endl;
real.func();
}
};