确保一个类最多只有一个实例,并提供一个全局访问点。
(某个类的对象有且仅有一个,单例的对象充当的是全局变量的角色,为什么在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. 具体产品类:工厂类所创建的对象就是此具体产品实例
#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. 具体产品类:具体工厂所创建的对象,就是此类。
(自己理解就是,工厂模式相当于简单工厂模式,如果需要增加新产品,只需要增加新的产品类继承抽象产品类,再增加生产该类型产品的工厂继承抽象工厂类,只需要拓展代码而不需要更改原先的代码)
#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. 具体产品类:具体工厂所创建的对象,就是此类。
抽象工厂模式的特点:
提供一个接口,可以创建多个产品族中的产品对象。比如船工厂,可以创建基础款船,升级款船
#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、微信等等都是中介,能大大降低了沟通的复杂性。还有飞机塔台。。。
优点:
缺点:
应用场景:
中介者模式包含以下主要角色。
类似于网络类,tcp udp继承网络类。中介者类与网络类有对应的指针关联
观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
在观察者模式中有如下角色:
Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
观察者模式这种发布-订阅的形式我们可以拿微信公众号来举例,假设微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号,当这个公众号更新时就会通知这些订阅的微信用户。好了我们来看看用代码如何实现: