设计模式复习

单例模式

确保一个类最多只有一个实例,并提供一个全局访问点。

(某个类的对象有且仅有一个,单例的对象充当的是全局变量的角色,为什么在C++里面不直接使用全局变量,而是使用单例来代替全局变量,因为如果直接使用全局变量会破坏类的封装,全局变量没有被封装,他的访问权限是不受限制的,任何模块在任意位置都可以对全局变量进行读或者写的操作,如果在程序中大量使用全局变量,全局变量在一个位置被恶意篡改,在其他位置获取全局变量会产生影响,其他模块在工作的时候就得不到正确的值了,解决方案就是使用单例模式,全局变量被封装到一个类里面,并且被private修饰,就不会在类外被随意访问,在单例模式的类里面提供对私有成员的访问函数,对变量的读和写设定指定的规则,这样类里面的数据不是直接被访问的,而是间接被访问的,间接的通过单例模式的类提供的成员函数进行访问,这样既把数据进行了封装又可以保证数据的安全性)

创建一个单例模式的类

构造拷贝构造私有,提供静态公有的获取方法 

类外new来创建对象已经不行了,只能通过类名得到对象,所以对象是静态的

饿汉模式 :定义类的时候创建单例对象

还没有使用该单例对象,该单例对象就已经被加载到内存了,在对象过多时会造成内存浪费,在多线程模式下,饿汉模式没有线程安全问题

#include
using namespace std;

//饿汉模式->定义类的时候创建单例对象
//定义一个单例模式的任务队列
class TaskQueue
{
public:
	static TaskQueue* getInstance()
	{
		return m_taskQ;
	}
	void pirntf() {
		cout << "sss" << endl;
	}

private:
	TaskQueue() = default;
	TaskQueue(const TaskQueue& t) = default;

	static TaskQueue* m_taskQ;
};
TaskQueue* TaskQueue::m_taskQ = new TaskQueue;

int main() {
	TaskQueue* taskQ = TaskQueue::getInstance();
	taskQ->pirntf();

	return 0;
}

懒汉模式:什么时候使用这个单例对象,在使用的时候再去创建对应的实例

解决了饿汉式内存浪费问题,但是线程不安全的,可以通过互斥量mutex.lock()和mutex.unlock()来解决 

#include
using namespace std;

//定义一个单例模式的任务队列
class TaskQueue
{
public:
	static TaskQueue* getInstance()
	{
        if(m_taskQ == nullptr)
        {
            m_taskQ = new TaskQueue;
        }
		return m_taskQ;
	}
	void pirntf() {
		cout << "sss" << endl;
	}

private:
	TaskQueue() = default;
	TaskQueue(const TaskQueue& t) = default;

	static TaskQueue* m_taskQ;
};
TaskQueue* TaskQueue::m_taskQ = nullptr;

int main() {
	TaskQueue* taskQ = TaskQueue::getInstance();
	taskQ->pirntf();

	return 0;
}

类加载的时候,没有立刻实例化,第一次调用getInstance()的时候,才真的实例化.

如果要是代码一整场都没有调用getInstance,此时实例化的过程也就被省略掉了,

我们一般成懒汉模式为“懒加载”或者“延时加载”,“懒汉模式"比“饿汉模式"效率更高,有很大的可能是“实例用不到",此时就节省了实例化的开销。

饿汉模式,懒汉模式线程安全吗?
了解了线程安全之后,对于饿汉模式来说,多线程同时调用getInstance(),由于getInstance()里只做了一件事:读取instance实例的地址,也就是多个线程在同时读取同一个变量,并没有构成多个线程同时修改同一个变量这一情况,所以说饿汉模式是线程安全的。

而对于懒汉模式来说,多线程调用getInstance(),getInstance()做了四件事情~

1.读取 instance 的内容

2.判断 instance 是否为 null

3.如果 instance 为null,就 new实例 (这就会修改 intance 的值)

4.返回实例的地址

由于懒汉模式造成了多个线程同时修改同一个变量这一情况,所以说懒汉模式是线程不安全的。

懒汉模式-使用双重检查锁定解决线程安全问题 

比如说三个线程A B C 同时访问 ,第一次判空,到锁那阻塞,A先进去第二次判空创建对象 B C等A完事锁解开才能进去 第一次ABC是一个接一个进去,如果再来三个线程的话会直接判断非空,直接到return m_taskQ了,使程序的效率提升。

#include
#include
using namespace std;

//定义一个单例模式的任务队列
class TaskQueue
{
public:
	static TaskQueue* getInstance()
	{
		if (m_taskQ == nullptr) {
			m_mutex.lock();
			if (m_taskQ == nullptr) {
				m_taskQ = new TaskQueue;
			}
			m_mutex.unlock();
		}
        return m_taskQ;
	}
	void pirntf() {
		cout << "sss" << endl;
	}

private:
	TaskQueue() = default;
	TaskQueue(const TaskQueue& t) = default;

	static TaskQueue* m_taskQ;
	static mutex m_mutex;
};
TaskQueue* TaskQueue::m_taskQ = nullptr;
mutex TaskQueue::m_mutex;

int main() {
	TaskQueue* taskQ = TaskQueue::getInstance();
	taskQ->pirntf();

	return 0;
}

工厂模式

主要是对对象的创建进行了一个封装,提供了一种创建对象的方式。

(1)在没有工厂的时代,如果客户需要一款宝马车,那么就需要客户去创建一款宝马车,然后拿来用。

(2)简单工厂模式:后来出现了工厂,用户不再需要去创建宝马车,由工厂进行创建,想要什么车,直接通过工厂创建就可以了。比如想要320i系列车,工厂就创建这个系列的车。

(3)工厂方法模式:为了满足客户,宝马车系列越来越多,如320i、523i等等系列,一个工厂无法创建所有的宝马系列,于是又单独分出来多个具体的工厂,每个具体工厂创建一种系列,即具体工厂类只能创建一个具体产品。但是宝马工厂还是个抽象,你需要指定某个具体的工厂才能生产车出来。

(4)抽象工厂模式:随着客户要求越来越高,宝马车必须配置空调,于是这个工厂开始生产宝马车和需要的空调。最终是客户只要对宝马的销售员说:我要523i空调车,销售员就直接给他523i空调车了。而不用自己去创建523i空调车宝马车。

简单工厂模式

简单工厂模式的结构组成

1. 工厂类:工厂模式的核心类,会定义一个用于创建指定的具体实例对象的接口。

2. 抽象产品类:是具体产品类的继承的父类或实现的接口。

3. 具体产品类:工厂类所创建的对象就是此具体产品实例

设计模式复习_第1张图片

#include
using namespace std;

// 产品类的父亲 - 人造恶魔果实
class AbstractSmile
{
public:
	virtual void transform() = 0;
	virtual void ability() = 0;
	virtual ~AbstractSmile(){}
};

class SheepSmile :public AbstractSmile
{
public:
	void transform() override
	{
		cout << "变身-山羊形态。。" << endl;
	}
	void ability() override
	{
		cout << "将手臂变成羊角。。" << endl;
	}
};

class LionSmile :public AbstractSmile
{
public:
	void transform() override
	{
		cout << "变身-狮子人形态。。" << endl;
	}
	void ability() override
	{
		cout << "火遁。。" << endl;
	}
};

class BatSmile :public AbstractSmile
{
public:
	void transform() override
	{
		cout << "变身-蝙蝠人形态。。" << endl;
	}
	void ability() override
	{
		cout << "吸血。。" << endl;
	}
};

//定义工厂类
enum class Type:char{Sheep,Lion,Bat};
class SmileFactory
{
public:
	AbstractSmile* createSmile(Type type)
	{
		AbstractSmile* ptr = nullptr;
		switch (type)
		{
		case Type::Sheep:
			ptr = new SheepSmile;
			break;
		case Type::Lion:
			ptr = new LionSmile;
			break;
		case Type::Bat:
			ptr = new BatSmile;
			break;
		default:
			break;
		}
		return ptr;
	}
};

int main()
{
	SmileFactory* factroy = new SmileFactory;
	AbstractSmile* obj = factroy->createSmile(Type::Lion);
	obj->transform();
	obj->ability();
	return 0;
}

可以看到,简单工厂可以做到,让用户创建对象的时候只需要知道对象的名称(BMW、AUDI)就好,而不需要关心创建对象的细节(BMW是如何建造的、型号是什么等等细节)。

当然缺点也很明显:
每当我们想要扩展对象的时候(增加BENZ的对象)就需要在SimpleFactory类中添加代码,增加switch后面的case选项。这样一来,就需要修改源代码。灵活性非常的差!!!

那么,能不能做到添加对象的时候,不对现有代码进行修改呢?(也就是我们开发软件时候需要遵守的开-闭原则)

工厂模式

工厂方法模式将工厂抽象化,并定义一个创建对象的接口。每增加新产品,只需增加该产品以及对应的具体实现工厂类,由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合“开闭原则”了,扩展时不必去修改原来的代码。在使用时,用于只需知道产品对应的具体工厂,关注具体的创建过程,甚至不需要知道具体产品类的类名,当我们选择哪个具体工厂时,就已经决定了实际创建的产品是哪个了。

        但缺点在于,每增加一个产品都需要增加一个具体产品类和实现工厂类,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。

工厂方法模式的结构组成:

1. 抽象工厂类:工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现。

2. 具体工厂类:继承于抽象工厂,实现创建对应具体产品对象的方式。

3. 抽象产品类:它是具体产品继承的父类(基类)。

4. 具体产品类:具体工厂所创建的对象,就是此类。

(自己理解就是,工厂模式相当于简单工厂模式,如果需要增加新产品,只需要增加新的产品类继承抽象产品类,再增加生产该类型产品的工厂继承抽象工厂类,只需要拓展代码而不需要更改原先的代码)

设计模式复习_第2张图片

#include
using namespace std;

// 产品类的父亲 - 人造恶魔果实
class AbstractSmile
{
public:
	virtual void transform() = 0;
	virtual void ability() = 0;
	virtual ~AbstractSmile(){}
};

class SheepSmile :public AbstractSmile
{
public:
	void transform() override
	{
		cout << "变身-山羊形态。。" << endl;
	}
	void ability() override
	{
		cout << "将手臂变成羊角。。" << endl;
	}
};

class LionSmile :public AbstractSmile
{
public:
	void transform() override
	{
		cout << "变身-狮子人形态。。" << endl;
	}
	void ability() override
	{
		cout << "火遁。。" << endl;
	}
};

class BatSmile :public AbstractSmile
{
public:
	void transform() override
	{
		cout << "变身-蝙蝠人形态。。" << endl;
	}
	void ability() override
	{
		cout << "吸血。。" << endl;
	}
};

//定义工厂类  - 父类
class AbstractFactory
{
public:
	virtual AbstractSmile* createSmile() = 0;
	virtual ~AbstractFactory(){}
};

// 生产山羊的恶魔果实
class SheepFactory:public AbstractFactory
{
public:
	AbstractSmile* createSmile()
	{
		return new SheepSmile;
	}
	~SheepFactory()
	{
		cout << "SheepFactory 被析构了。。" << endl;
	}
};

// 生产狮子的恶魔果实
class LionFactory :public AbstractFactory
{
public:
	AbstractSmile* createSmile()
	{
		return new LionSmile;
	}
	~LionFactory()
	{
		cout << "LionFactory 被析构了。。" << endl;
	}
};

// 生产蝙蝠的恶魔果实
class BatFactory :public AbstractFactory
{
public:
	AbstractSmile* createSmile()
	{
		return new BatSmile;
	}
	~BatFactory()
	{
		cout << "BatFactory 被析构了。。" << endl;
	}
};

int main()
{
	AbstractFactory* factroy = new LionFactory;
	AbstractSmile* obj = factroy->createSmile();
	obj->transform();
	obj->ability();

	delete obj;
	delete factroy;
	return 0;
}

抽象工厂模式

抽象工厂模式的结构组成(和工厂方法模式一样):

1. 抽象工厂类:工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现。

2. 具体工厂类:继承于抽象工厂,实现创建对应具体产品对象的方式。

3. 抽象产品类:它是具体产品继承的父类(基类)。

4. 具体产品类:具体工厂所创建的对象,就是此类。

抽象工厂模式的特点:

提供一个接口,可以创建多个产品族中的产品对象。比如船工厂,可以创建基础款船,升级款船

设计模式复习_第3张图片

#include
using namespace std;

//船体
class shipBody
{
public:
	virtual string getBody() = 0;
	virtual ~shipBody(){}
};

//木头船体
class WoodBody :public shipBody
{
public:
	string getBody() override
	{
		return "使用<木头>制作船体";
	}
};

//钢铁船体
class IronBody :public shipBody
{
public:
	string getBody() override
	{
		return "使用<钢铁>制作船体";
	}
};


//引擎
class Engine
{
public:
	virtual string getEngine() = 0;
	virtual ~Engine(){}
};

class Human :public Engine
{
public:
	string getEngine()
	{
		return "船体动力方式是<手动>";
	}
};

class Diesel :public Engine
{
public:
	string getEngine()
	{
		return "船体动力方式是<内燃机>";
	}
};


//船
class Ship
{
public:
	Ship(shipBody*body,Engine* engine):
		m_body(body),m_engine(engine){}
	~Ship()
	{
		delete m_body;
		delete m_engine;
	}
	string getProperty()
	{
		string Info = m_body->getBody() + m_engine->getEngine();
		return Info;
	}

private:
	shipBody* m_body;
	Engine* m_engine;
};

//工厂类 - 抽象
class AbstractFactory
{
public:
	virtual Ship* createShip() = 0;
	virtual ~AbstractFactory(){}
};

class BasicFactory :public AbstractFactory
{
public:
	Ship* createShip() override
	{
		Ship* ship = new Ship(new WoodBody, new Human);
		cout << "<基础形>海贼船已经打造完毕" << endl;
		return ship;
	}
};

class StandardFactory :public AbstractFactory
{
public:
	Ship* createShip() override
	{
		Ship* ship = new Ship(new IronBody, new Diesel);
		cout << "<标准形>海贼船已经打造完毕" << endl;
		return ship;
	}
};

int main()
{
	//下单标准型海贼船
	AbstractFactory* factory = new StandardFactory;
	Ship* ship = factory->createShip();
	cout<getProperty();
	delete ship;
	delete factory;
	return 0;
}

抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,能够创建多个产品族的产品对象

工厂模式只能生产一个产品。例如:产品要么香蕉、要么苹果,但抽象工厂可以一下生产一个产品族,如水果、动物、蔬菜等

#include
using namespace std;


class AbstractFactory;

//抽象产品族Tea,茶可以是都匀毛尖、铁观音、普洱等品种。
class Tea{
public:
	virtual void getName() = 0;
};

//抽象产品族Fruit
class Fruit{
public:
	virtual void getName() = 0;
};

//抽象工厂,声明具体可以生产的产品族
class AbstractFactory{
public:
	virtual Tea* CreateTea() = 0;//生产产品族Tea
	virtual Fruit* CreateFruit() = 0 ;//生产产品族Fruit
};

//水果族的具体水果
class AppleFruit:public Fruit{
public:
	void getName(){
	    cout<<"I'm Apple"<CreateTea();
	tea->getName();
	delete tea;
	fruit = factory->CreateFruit();
	fruit->getName();
	delete fruit;

	delete factory;

	//云南工厂
	factory = new YunnanFactory;
	tea = factory->CreateTea();
	tea->getName();
	delete tea;
	fruit = factory->CreateFruit();
	fruit->getName();
	delete fruit;

	delete factory;
	return 0;
}

以上只是我的学习过程,真正写的牛逼还清晰的还是下边这个 。。。。。

C++ 深入浅出工厂模式(初识篇) - 知乎 (zhihu.com)

中介者模式 

中介者模式可以减少对象之间混乱无序的依赖关系,从而使其耦合松散,限制对象之间的直接交互,迫使他们通过一个中介者对象进行合作。

如果编写一个类,这个类的功能强大,但是和其他的类的耦合度非常高,我们在使用这个类的时候就会考虑很多情况,所以感觉复用这个类比较吃力的时候,就可以使用中介者模式,把这个耦合度高的类进行拆分,分离之后就会得到一个中介者类,通过中介者类对其他的类进行服务。

生活中最常见的例子就是租房中介是一种中介,如果我们想要租房可以通过中介和房东沟通,这时候其实并不需要知道对方是谁,也并不需要面对面,类似于电话、QQ、微信等等都是中介,能大大降低了沟通的复杂性。还有飞机塔台。。。 

优点:

  • 降低了对象之间的耦合性,增加了它们的独立性和可维护性。
  • 减少了对象间的直接通信,简化了对象之间的关系,降低了系统的复杂性。

缺点:

  • 中介者对象会成为系统的核心,并承担大量的职责,如果这些职责变得过于复杂,就会导致中介者对象变得过于庞大和难以维护。
  • 可能会降低整体系统的效率,因为所有的通信都需要经过中介者对象。

应用场景:

  • 当对象之间的关系过于复杂,而且它们之间的依赖关系难以理解和管理时,可以考虑使用中介者模式,将这些依赖管理起来。
  • 当一个对象需要和多个其他对象进行互动,并且这些对象之间存在复杂的互动关系时,可以考虑使用中介者模式,将它们之间的交互都集中到一个中介者对象中。
  • 当修改一个对象会涉及到许多其他对象时,可以使用中介者模式来避免修改所有对象,只需要更新中介者即可。
  • 当一组对象在协作过程中会产生大量相互关联的代码时,可以使用中介者模式来将其抽象出来,更好地隐藏其复杂性。

中介者模式包含以下主要角色。

  1. 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
  2. 具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
  3. 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
  4. 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

设计模式复习_第4张图片

 类似于网络类,tcp udp继承网络类。中介者类与网络类有对应的指针关联

观察者模式

观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

设计模式复习_第5张图片

在观察者模式中有如下角色:

Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

观察者模式这种发布-订阅的形式我们可以拿微信公众号来举例,假设微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号,当这个公众号更新时就会通知这些订阅的微信用户。好了我们来看看用代码如何实现:

 

你可能感兴趣的:(设计模式)