代码设计模式

意义

设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案

分为三大类:

创建型模式:

对象实例化的模式,创建型模式用于解耦对象的实例化过程

结构型模式:

把类或对象结合在一起形成一个更大的结构

行为模式:

类和对象如何交互,及划分责任和算法

共23种

其中c++常见的10种有:单例、工厂方法、抽象工厂、建造者、原型、适配器、桥接、组合、装饰器、外观模式

一、创建型模式

创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是 new 一个对象,然后 set 相关属性。但是,在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者用的时候。

1.Factory模式(工厂)

解决的问题

  • 抽象出一些类的公共接口以形成抽象基类或者接口,

这样我们可以通过声明一个指向基类的指针来指向实际的子类实现,达到了多态的目的。

因此需要在用到子类的地方写new对象。

所以实体类的使用者必须知道实际的子类名称

使得程序的扩展性和维护性越来越困难

  • 在父类中并不知道具体要实例化哪一个具体的子类

只能在父类中写方法调用,具体调用哪一个类的方法交给子类实现。

该模式的重要功能

  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
#include <iostream>
#include <memory>

class Product {
public:
virtual ~Product() {}
virtual void doSomething() = 0;
};

class ConcreteProductA : public Product {
public:
void doSomething() override {
std::cout << "ConcreteProductA::doSomething()\n";
}
};

class ConcreteProductB : public Product {
public:
void doSomething() override {
std::cout << "ConcreteProductB::doSomething()\n";
}
};

class Creator {
public:
virtual ~Creator() {}
virtual std::unique_ptr<Product> createProduct() = 0;
};

class ConcreteCreatorA : public Creator {
public:
std::unique_ptr<Product> createProduct() override {
return std::make_unique<ConcreteProductA>();
}
};

class ConcreteCreatorB : public Creator {
public:
std::unique_ptr<Product> createProduct() override {
return std::make_unique<ConcreteProductB>();
}
};

int main() {
ConcreteCreatorA creatorA;
auto productA = creatorA.createProduct();
productA->doSomething();

ConcreteCreatorB creatorB;
auto productB = creatorB.createProduct();
productB->doSomething();

return 0;
}

ConcreteProductA::doSomething()
ConcreteProductB::doSomething()

分析

抽象基类Product:定义要创建的接口,doSomething纯虚函数,定义具体产品必须实现的功能

ConcreteProductA/ConcreteProductB:这些是Product接口的具体实现,重写doSomething来为各自的产品提供特定行为

抽象基类Creator:createproduct方法返回了一个指向新创建产品的指针,包装在(unique_ptr智能指针中)。该类充当创建产品的接口

ConcreteCreatorA/ConcreteCreatorB:Creator接口的具体实现,重写 createProduct方法实例化并返回具体产品的特定实例

main函数:创建 ConcreteCreatorA 和 ConcreteCreatorB 的实例,调用createproduct方法,去创建各自的产品;然后用这些产品去调用各自的doSomething方法

其实就是供客户选择:比如可乐,看是选择百事牌制作,还是选择可口牌制作。

具体的工厂->具体的产品->具体的行为(选择了百事工厂(具体的creator),选择百事可乐(具体的product),得到百事可乐(dosomething()))。

总结

允许客户代码通过工厂接口来创建产品,而不需要直接与具体产品类进行交互

该模式仅局限于一类;Product是一类,有个共同基类;如果为不同类提供一个对象创建的接口呢?

2.AbstactFactory 模式(抽象工厂)

引入

造电脑为例;如果将CPU和主板抽象,cpu由cpufactory生产,主板由mbfactory生产

客户端调用为:

1
2
3
4
5
6
7
8
9
10
11
//java语言
// 得到 Intel 的 CPU
CPUFactory cpuFactory = new IntelCPUFactory();
CPU cpu = intelCPUFactory.makeCPU();

// 得到 AMD 的主板
MainBoardFactory mainBoardFactory = new AmdMainBoardFactory();
MainBoard mainBoard = mainBoardFactory.make();

// 组装 CPU 和主板
Computer computer = new Computer(cpu, mainBoard);

但是,因特尔的cpu和AMD的主板不兼容使用,导致代码出错,但用户不知道这件事,所以随意组合还是不稳妥。

因此,引入产品族这一概念,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品

所以不再定义cpu工厂、主板工厂;而是直接定义电脑工厂,每个电脑工厂负责所有的设备生产,杜绝了不兼容问题。

那么用户的调用就是

1
2
3
4
5
6
7
8
9
10
11
//java语言
//定义电脑工厂,比如英特尔
cpfactory cp=new Intelfactory();
//生产它的cpu
Cpu cpu=cp.makecpu();
//生产该工厂的主板
Mainboard mb=cp.makemb();
//生产硬盘啊...
....
//组装啊
Computer computer = new Computer(cpu, mb,...);

解决的问题

为不同类提供一个对象创建的接口

该模式重要的功能

将一组相关的产品组织在一起,可以确保产品之间的兼容性

代码

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
#include<iostream>

//cpu
class cpu{
public:
virtual ~cpu(){}
virtual std::string getname()=0;
};
class intelcpu:public cpu{
public:
std::string getname() override{
return "Intel cpu";
}
};
class admcpu:public cpu{
public:
std::string getname() override{
return "Adm cpu";
}
};
//mb
class mainboard{
public:
virtual ~mainboard() {}
virtual std::string getmodel()=0;
};
class intelmainborad:public mainboard{
public:
std::string getmodel() override{
return "Intel mainborad";
}
};
class admmainborad:public mainboard{
public:
std::string getmodel() override{
return "Adm mainborad";
}
};
//factory
class computerfactory{
public:
virtual ~computerfactory(){}
virtual cpu* createcpu()=0;
virtual mainboard* createmainboard()=0;
};
class intelcomputerfactory:public computerfactory{
public:
cpu* createcpu() override{
return new intelcpu();
}
mainboard* createmainboard() override{
return new intelmainborad();
}
};
class admcomputerfactory:public computerfactory{
public:
cpu* createcpu() override{
return new admcpu();
}
mainboard* createmainboard() override{
return new admmainborad();
}
};
int main(){

//客户端代码
//选择一个intel厂家
intelcomputerfactory infac;
cpu* cp=infac.createcpu();
mainboard* mb=infac.createmainboard();

std::cout<<"cpu: "<<cp->getname()<<", mainboard: "<<mb->getmodel();

return 0;
}

分析

抽象产品:cpumainboard,是抽象产品,定义了接口

具体产品:intelcpuintelmainboardadmcpuadmmainboard,分别实现了抽象产品的接口

抽象工厂:computerfactory,是抽象工厂,定义了创建抽象产品的接口方法

具体工厂:intelcomputerfactoryadmcomputerfactory是具体工厂,分别实现了抽象工厂接口方法,用于创建具体产品的实例。

main函数:即客户端代码,选择了具体工厂intelcomputerfactory实例,通过调用方法 createcpu``和createmainboard 生产了特定的产品对象cpu和mainboard

总结

问题就是,比如再加个设备,加个显示器,需要修改所有工厂,为它们都加上制造显示器的方法。违反 对修改关闭,对扩展开放 的设计原则

关注点 工厂模式 抽象工厂模式
关注点 创建单个对象,在一个抽象的工厂类中声明一个工厂方法,具体的子类负责实现这个方法来创建特定的产品对象 创建一组相关对象,在一个抽象的工厂类中声明多个工厂方法,每个方法用于创建一类产品对象
产品结构 适用于一组具有相似结构的产品,每个具体工厂类负责创建一个具体产品类 适用于一组相关的产品,每个具体工厂类负责创建一组具有相关性的产品,这些产品可能在不同的维度上有关联
扩展性 新增产品种类较为容易,只需要创建新的具体产品类和对应的具体工厂类 新增产品族较为容易,只需要创建新的一组具体产品类和对应的具体工厂类

3.Singleton 模式( 单例模式)

指在内存中只会创建且仅创建一次对象的设计模式,当程序中其他地方需要使用到该对象的相同功能时,都会调用创建好的这一个,不会再额外创建实例,这样做的好处就是避免过多的创建相同作用的对象使得内存浪费。

实现单例模式的步骤:
1、构造函数私有化
2、增加静态私有的当前类的指针变量
3、提供静态对外接口,可以让用户获得单例对象

单例模式分类:

懒汉式:解决了饿汉式内存浪费问题,但是线程不安全的,可以通过互斥量mutex.lock()和mutex.unlock()来解决(程序调用时创建实例)
饿汉式:还没有使用该单例对象,该单例对象就已经被加载到内存了,在对象过多时会造成内存浪费(程序加载时就创建好实例,等待调用)

解决的问题

在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

代码

懒汉式创建单例对象

懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。,否则则先执行实例化操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Singleton {
private:
static Singleton* singleton;
Singleton(){}
public:
static Singleton* getInstance() {
if (singleton ==nullptr) {
singleton = new Singleton();
}
return singleton;
}

};

缺点:多线程时,同时进入if判断,会导致被实例化两个对象

饿汉式创建单例对象

饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建

以下代码为java

1
2
3
4
5
6
7
8
9
10
public class Singleton{

private static final Singleton singleton = new Singleton();

private Singleton(){}

public static Singleton getInstance() {
return singleton;
}
}

c++版本

1
2
3
4
5
6
7
8
9
10
11
12
class Singleton {
private:
static Singleton* instence;
Singleton() {}
public:
static Singleton* getInstence() {
return instence;
}
static void freespace(){
if(instence!=NULL) delete instence;
}
};

总结

  • Singleton 不可以被实例化,因此将其构造函数声明private
  • Singleton 模式经常和 Factory(AbstractFactory)模式在一起使用,因为系统中工厂对象一般来说只要一个

CSDN小菠萝的IT之旅

https://www.yii666.com/blog/395092.html

来源

知乎

CSDN:toBetterMe

CSDN:yingjiejk

https://www.yii666.com/blog/395092.html

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2023-2025 是羽泪云诶
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信