单例模式
确保一个类最多只有一个实例,并提供一个全局访问点。
(某个类的对象有且仅有一个,单例的对象充当的是全局变量的角色,为什么在C++里面不直接使用全局变量,而是使用单例来代替全局变量,因为如果直接使用全局变量会破坏类的封装,全局变量没有被封装,他的访问权限是不受限制的,任何模块在任意位置都可以对全局变量进行读或者写的操作,如果在程序中大量使用全局变量,全局变量在一个位置被恶意篡改,在其他位置获取全局变量会产生影响,其他模块在工作的时候就得不到正确的值了,解决方案就是使用单例模式,全局变量被封装到一个类里面,并且被private修饰,就不会在类外被随意访问,在单例模式的类里面提供对私有成员的访问函数,对变量的读和写设定指定的规则,这样类里面的数据不是直接被访问的,而是间接被访问的,间接的通过单例模式的类提供的成员函数进行访问,这样既把数据进行了封装又可以保证数据的安全性)
创建一个单例模式的类
构造拷贝构造私有,提供静态公有的获取方法
类外new来创建对象已经不行了,只能通过类名得到对象,所以对象是静态的
饿汉模式 :定义类的时候创建单例对象
还没有使用该单例对象,该单例对象就已经被加载到内存了,在对象过多时会造成内存浪费,在多线程模式下,饿汉模式没有线程安全问题
#include<iostream>
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<iostream>
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<iostream>
#include<mutex>
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<iostream>
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<iostream>
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<iostream>
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<<ship->getProperty();delete ship;delete factory;return 0;
}
抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,能够创建多个产品族的产品对象
工厂模式只能生产一个产品。例如:产品要么香蕉、要么苹果,但抽象工厂可以一下生产一个产品族,如水果、动物、蔬菜等
#include<iostream>
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;//生产产品族Teavirtual Fruit* CreateFruit() = 0 ;//生产产品族Fruit
};//水果族的具体水果
class AppleFruit:public Fruit{
public:void getName(){cout<<"I'm Apple"<<endl;}
};
class BananaFruit:public Fruit{
public:void getName(){cout<<"I'm Banana"<<endl;}
};//茶族的具体茶
class DuyunTea:public Tea{
public:void getName(){cout<<"I'm maojianTea"<<endl;}
};
class PuerhTea:public Tea{
public:void getName(){cout<<"I'm PuerhTea"<<endl;}
};//具体工厂,都匀工厂
class DunyunFactory:public AbstractFactory{
public:Tea* CreateTea(){return new DuyunTea;}Fruit* CreateFruit(){return new BananaFruit();}
};
//具体工厂,云南工厂
class YunnanFactory:public AbstractFactory{
public:Tea* CreateTea(){return new PuerhTea;}Fruit* CreateFruit(){return new AppleFruit();}
};
int main(){AbstractFactory* factory = nullptr;Fruit *fruit = nullptr;Tea *tea = nullptr;//都匀工厂factory = new DunyunFactory;tea = factory->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)