C++ 设计模式


本章内容概述

本文意在记录笔者在学习设计模式相关内容时记录笔记之用,同时适时加入笔者自己的体会与理解,希望能够加深笔者的印象,对读者更好的理解常见的设计模式有所帮助。


一、常见设计模式

设计模式有很多种,在《大话设计模式》一书中,共提到了24种,并不是每一种都熟稔于心,但是必须熟悉常见的设计模式和其设计理念,并在实际设计中加以应用,才能做到举一反三,触类旁通。

1.设计模式原则

设计模式一般具备六大设计原则,分别是:

单一职责原则:对于一个类,应当仅有一个使其发生改变的原因。

开放封闭原则:面对新的需求,可以通过增添代码来实现,但不能改动已有的代码。

里氏代换原则:基类一定适用于派生类,即在程序中,即便将所有基类替换成派生类,也不影响结果。

依赖倒转原则:抽象不依赖细节,细节依赖抽象,即针对接口编程,而不是针对实现编程。

迪米特原则:如果两个类不直接通信,则不应当发生直接的相互作用。如果一个类需要调用另一个类的某个方法,则可以通过第三个类转发此调用。

接口隔离原则:每个接口中,不存在派生类用不到却必须实现的方法,否则应当将接口拆分,使用多个隔离的接口。

2.设计模式分类

设计模式一般分为三类,分别如下:

创造型模式:单例模式、工厂模式、建造者模式、原型模式。

结构型模式:适配器模式、桥接模式、外观模式、组合模式、装饰模式、享元模式、代理模式。

行为型模式:责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式。

3.常见设计模式

本文主要介绍几种常见的设计模式,分别如下:

单例模式:保证一个类仅有一个实例,并提供访问他的一个全局访问点。

工厂模式:包括简单工厂模式、抽象工厂模式、工厂方法模式:

简单工厂模式:主要用于创建对象,通过一个工厂,实现根据不同的输入条件产生不同的类,并且根据类内重写虚函数的不同实现不同的调用结果。

工厂方法模式:对简单工厂模式的修正,将选择判断交给客户端实现,这样的设计可以使得在添加新功能时无需修改现有的类,直接修改客户端即可。

抽象工厂模式:定义一个创建一系列相关或相互依赖的借口,而无需指定具体的类。

观察者模式:定义一种一对多的关系,使得多哥观察对象同时监听一个主题对象,在主题对象发生变化时,通知所有观察者更新自己。

装饰模式:可以动态地给对象添加额外职责,相比于生成派生类更为灵活。

二、单例模式

单例模式,保证能且仅能实例化出一个对象,并提供一个访问该类的全局访问点,通常应用于文件系统和打印程序。

不难理解,在一个操作系统中,有且仅有一个文件系统,因此仅有一个实例;而对于打印程序,虽然一台主机可以连接多台打印机,但是计算机长的打印程序有且仅有一个,从而避免两个打印作业同时输出到打印机。

对于单例模式,有多种实现模式,一般分为两种:懒汉模式(在第一次用到类的时候实例化),饿汉模式(类定义的时候就实例化),接下来分别进行分析。

1.线程不安全的懒汉模式

将类的构造函数、拷贝构造和赋值运算符重载全部设为私有函数,同时定义静态类型的成员函数,返回已经创建好的类对象的指针,此处必须是静态成员函数,不做详细解释。

class Singleton{
private:
    static Singleton * instance;
    Singleton(){}
    Singleton(const Singleton& tmp){}
    Singleton& operator=(const Singleton& tmp){}
public:
    static Singleton* getInstance()
    {
        if(instance == NULL)
        {
            instance = new Singleton();
        }
        return instance;
    }
};

Singleton* Singleton::instance = NULL;

但是需要注意的是,这样的实现方式在多线程环境下是不安全的,如果当多个线程同时创建该类对象,则有可能不满足单例模式要求,因此还需要探讨更合适的方式。

2.线程安全的懒汉模式

对于多线程不安全的情况,可以通过加锁的方式解决,即在判断是否已经创建对象时进入临界区,从而保证经由一个线程可以访问,但是也会导致其余线程全部被阻塞。对于多线程的分析,碍于笔者水平首先,点到即止。

3.饿汉模式

在类定义时,初始化一个类对象指针,并通过静态成员函数返回该值真,同时可以保证在多线程模式下的安全性,笔者更推荐使用该模式。

二、工厂模式

工厂模式一般分为三种:简单工厂模式,工厂方法模式和抽象工厂模式,分别进行分析。

1.简单工厂模式

在创建对象时,根据输入不同可以产生不同的类对象,并且通过虚函数可以执行不同的功能,得到不同的结果。适用于针对不同情况创建类时,只需传入参数而无需了解实现,实现代码如下:

class operation { public: int v1; int v2; virtual double calculate() = 0; };

class add_op :public operation { double calculate() { return v1 + v2; } };

class sub_op :public operation { double calculate() { return v1 - v2; } };

class mul_op :public operation { double calculate() { return v1 * v2; } };

class div_op :public operation { double calculate() { return v1 / v2; } };

class factory
{
public:
	static operation* createProduct(char op)
	{
		if (op == '+') { return new add_op(); }
		else if (op == '-') { return new sub_op(); } 
		else if (op == '*') { return new mul_op(); }
		else if (op == '/') { return new div_op(); }
		else { return nullptr; }
	}
};

但是不难看出,这样的设计并不满足开放封闭原则,如果需要增加功能,仍需修改现有代码,接下来对其进行优化。

2.工厂方法模式

为了满足开放封闭原则,可以对工厂进行分类,每个工厂只产生特定的产品,无需进行判断,进而使得在添加新功能时,只增加新工厂即可,无需修改判断条件,代码如下:

class operation { public: int v1; int v2; virtual double calculate() = 0; };
class add_op :public operation { double calculate() { return v1 + v2; } };
class sub_op :public operation { double calculate() { return v1 - v2; } };
class mul_op :public operation { double calculate() { return v1 * v2; } };
class div_op :public operation { double calculate() { return v1 / v2; } };

class factory { public: virtual operation* createProduct() = 0; };
class add_f :public factory { operation* createProduct() { return new add_op; } };
class sub_f :public factory { operation* createProduct() { return new sub_op; } };
class mul_f :public factory { operation* createProduct() { return new mul_op; } };
class div_f :public factory { operation* createProduct() { return new div_op; } };

3.抽象工厂模式

针对具体问题设计出一系列功能类似的产品,并根据用户需求对外提供服务接口,此处不作详解。

三、观察者模式

观察者会根据被观察者的状态变化,更新自己的状态。

在观察者类中,应当包含被观察者对象,从而更新自身状态;在被观察者类中,应当包括全部观察此被观察者的观察者对象,以便发送通知使其更改自身状态,代码如下:

class teacher;

class student
{
protected:
	string name;
	teacher* t;
	student(string _name, teacher* _t) :name(_name), t(_t) { }
public:
	virtual void update() = 0;
};

class sleepy_student :public student
{
public:
	sleepy_student(string _name, teacher* _t) :student(_name, _t){ }
	void update() { cout << name << " wake up" << endl; }
};
class talking_student :public student
{
public:
	talking_student(string _name, teacher* _t) :student(_name, _t) { }
	void update() { cout << name << " shut up" << endl; }
};
class teacher
{
protected:
	vector<student*> group;

public:
	void attach(student* s) { group.push_back(s); }
	void detach(student* s) 
	{
		for (int i = 0; i < group.size(); i++)
		{
			if (group[i] == s)
			{
				group.erase(group.begin() + i);
				return;
			}
		}
	}

	void notify()
	{
		for (int i = 0; i < group.size(); i++)
		{
			group[i]->update();
		}
	}
};

本章总结

本章主要介绍了集中常见的设计模式,更多的设计模式的知识笔者会酌情继续补充,那么本章就到此结束,希望对读者初步了解设计模式有所帮助。

最后,我是Alkaid#3529,一个追求不断进步的学生,期待你的关注!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alkaid3529

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值