C++设计模式 | 四种创建型模式——简单工厂模式、工厂方法模式、抽象工厂模式、单例模式...

简单工厂模式

20988794-ddb04f00a1fcce11.png
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;


//抽象水果
class Fruit{
public:
    virtual void shoName() = 0;
};

//苹果类
class Apple : public Fruit{
public:
    virtual void shoName(){
        cout << "我是苹果" << endl;
    }
};

//香蕉类
class Banana : public Fruit{
public:
    virtual void shoName(){
        cout << "我是香蕉" << endl;
    }
};

//鸭梨类
class Pear : public Fruit{
public:
    virtual void shoName(){
        cout << "我是鸭梨" << endl;
    }
};

//水果工厂
class FruitFactory{
public:
    static Fruit* CreateFruit(string name){
        if (name.compare("apple") == 0){
            return new Apple;
        }
        else if (name.compare("banana") == 0){
            return new Banana;
        }
        else if (name.compare("pear") == 0){
            return new Pear;
        }
    }
};

//测试
void test01(){
    
    Fruit* fruit = NULL;
    fruit = FruitFactory::CreateFruit("apple");  //工厂生产苹果
    fruit->shoName();
    delete fruit;

    fruit = FruitFactory::CreateFruit("banana"); //工厂生产香蕉
    fruit->shoName();
    delete fruit;

    fruit = FruitFactory::CreateFruit("pear"); //工厂生产鸭梨
    fruit->shoName();
    delete fruit;

}

int main(){

    test01();

    
    return EXIT_SUCCESS;
}
简单工厂模式的优缺点及适用场景

优点:
(1)实现了对象创建和使用的分离。
(2)不需要记住具体类名,记住参数即可,减少使用者记忆量。

缺点:
(1)对工厂类职责过重,一旦不能工作,系统受到影响。
(2)增加系统中类的个数,复杂度和理解度增加。
(3)违反“开闭原则”,添加新产品需要修改工厂逻辑,工厂越来越复杂。

适用场景:

  1. 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
  2. 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

工厂方法模式

工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。
首先完全实现‘开闭原则’,实现了可扩展。

工厂方法模式中的角色与职责:
抽象工厂(Abstract Factory)角色:工厂方法模式的核心,任何工厂类都必须实现这个接口。
工厂(Concrete Factory)角色:具体工厂类是抽象工厂的一个实现,负责实例化产品对象。
抽象产品(Abstract Product)角色:工厂方法模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
具体产品(Concrete Product)角色:工厂方法模式所创建的具体实例对象。

20988794-8550d5eab050ebf2.png
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;


//抽象水果
class AbstractFruit{
public:
    virtual void showName() = 0;
};

/* 具体水果 start  */

//苹果
class Apple : public AbstractFruit{
public:
    virtual void showName(){
        cout << "我是苹果" << endl;
    }
};

//香蕉
class Banana : public AbstractFruit{
public:
    virtual void showName(){
        cout << "我是香蕉" << endl;
    }
};

//鸭梨
class Pear : public AbstractFruit{
public:
    virtual void showName(){
        cout << "我是鸭梨" << endl;
    }
};

/* 具体水果 end  */

//抽象工厂
class AbstractFactory{
public:
    virtual AbstractFruit* CreateFruit() = 0;
};

/* 具体工厂类 start */

//苹果工厂
class AppleFactory : public AbstractFactory{
public:
    virtual AbstractFruit* CreateFruit(){
        return new Apple;
    }
};

//香蕉工厂
class BananaFactory : public AbstractFactory{
public:
    virtual AbstractFruit* CreateFruit(){
        return new Banana;
    }
};

//鸭梨工厂
class PearFactory : public AbstractFactory{
public:
    virtual AbstractFruit* CreateFruit(){
        return new Pear;
    }
};

/* 具体工厂类 end */


//测试
void test01(){
    
    AbstractFactory* factory = NULL;
    AbstractFruit* fruit = NULL;

    factory = new AppleFactory; //创建苹果工厂
    fruit = factory->CreateFruit(); //苹果工厂生产苹果
    fruit->showName();

    factory = new BananaFactory; //创建香蕉工厂
    fruit = factory->CreateFruit(); //香蕉工厂生产苹果
    fruit->showName();

    factory = new PearFactory; //创建鸭梨工厂
    fruit = factory->CreateFruit(); //鸭梨工厂生产苹果
    fruit->showName();

}

int main(){

    test01();

    
    return EXIT_SUCCESS;
}
工厂方法模式的优缺点及适用场景

优点:
(1)不需要记住具体类名,甚至连具体参数都不用记忆。
(2)实现了对象创建和使用的分离。
(3)系统的可扩展性也就变得非常好,无需修改接口和原类。

缺点:
(1)增加系统中类的个数,复杂度和理解度增加。
(2)增加了系统的抽象性和理解难度。

适用场景:

  1. 客户端不知道它所需要的对象的类。
  2. 抽象工厂类通过其子类来指定创建哪个对象。

抽象工厂模式

工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个“产品族,由同一个工厂来统一生产,这就是我们本文将要学习的抽象工厂模式的基本思想。

模式中的角色和职责:

抽象工厂(Abstract Factory)角色:它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。
具体工厂(Concrete Factory)角色:它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
抽象产品(Abstract Product)角色:它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
具体产品(Concrete Product)角色:它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。

20988794-e90d5dfb6aaa85d2.png
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//抽象苹果类
class AbstractApple{
public:
    virtual void showName() = 0;
};

//抽象香蕉
class AbstractBanana{
public:
    virtual void showName() = 0;
};

//抽象鸭梨
class AbstractPear{
public:
    virtual void showName() = 0;
};

//中国苹果
class ChineseApple : public AbstractApple{
public:
    virtual void showName(){
        cout << "中国苹果" << endl;
    }
};

//美国苹果
class AmericanApple : public AbstractApple{
public:
    virtual void showName(){
        cout << "美国苹果" << endl;
    }
};

//日本苹果
class JapaneseApple : public AbstractApple{
public:
    virtual void showName(){
        cout << "日本苹果" << endl;
    }
};

//中国香蕉
class ChineseBanana : public AbstractBanana{
public:
    virtual void showName(){
        cout << "中国香蕉" << endl;
    }
};

//美国香蕉
class AmericanBanana : public AbstractBanana{
public:
    virtual void showName(){
        cout << "美国香蕉" << endl;
    }
};

//日本香蕉
class JapaneseBanana : public AbstractBanana{
public:
    virtual void showName(){
        cout << "日本香蕉" << endl;
    }
};

//中国鸭梨
class ChinesePear : public AbstractPear{
public:
    virtual void showName(){
        cout << "中国鸭梨" << endl;
    }
};

//美国鸭梨
class AmericanPear : public AbstractPear{
public:
    virtual void showName(){
        cout << "美国鸭梨" << endl;
    }
};

//日本鸭梨
class JapanesePear : public AbstractPear{
public:
    virtual void showName(){
        cout << "日本鸭梨" << endl;
    }
};

//抽象工厂
class AbstractFactory{
public:
    virtual AbstractApple* CreateApple() = 0;
    virtual AbstractBanana* CreateBanana() = 0;
    virtual AbstractPear* CreatePear() = 0;
};

//中国工厂
class ChineseFactory : public AbstractFactory{
public:
    virtual AbstractApple* CreateApple(){
        return new ChineseApple;
    }
    virtual AbstractBanana* CreateBanana(){
        return new ChineseBanana;
    }
    virtual AbstractPear* CreatePear(){
        return new ChinesePear;
    }
};

//美国工厂
class AmericanFactory : public AbstractFactory{
public:
    virtual AbstractApple* CreateApple(){
        return new AmericanApple;
    }
    virtual AbstractBanana* CreateBanana(){
        return new AmericanBanana;
    }
    virtual AbstractPear* CreatePear(){
        return new AmericanPear;
    }
};

//美国工厂
class JapaneseFactory : public AbstractFactory{
public:
    virtual AbstractApple* CreateApple(){
        return new JapaneseApple;
    }
    virtual AbstractBanana* CreateBanana(){
        return new JapaneseBanana;
    }
    virtual AbstractPear* CreatePear(){
        return new JapanesePear;
    }
};


void test01(){
    
    AbstractFactory* factory = NULL;
    AbstractApple* apple = NULL;
    AbstractBanana* banana = NULL;
    AbstractPear* pear = NULL;

    factory = new ChineseFactory; //创建中国工厂
    apple = factory->CreateApple();
    banana = factory->CreateBanana();
    pear = factory->CreatePear();

    apple->showName();
    banana->showName();
    pear->showName();

    delete pear;
    delete banana;
    delete apple;
    delete factory;

    factory = new AmericanFactory; //创建美国工厂
    apple = factory->CreateApple();
    banana = factory->CreateBanana();
    pear = factory->CreatePear();

    apple->showName();
    banana->showName();
    pear->showName();

    delete pear;
    delete banana;
    delete apple;
    delete factory;

    factory = new JapaneseFactory; //创建日本工厂
    apple = factory->CreateApple();
    banana = factory->CreateBanana();
    pear = factory->CreatePear();

    apple->showName();
    banana->showName();
    pear->showName();

    delete pear;
    delete banana;
    delete apple;
    delete factory;

}

int main(){

    test01();


    return EXIT_SUCCESS;
}
抽象工厂模式的优缺点及适用场景

优点:
(1)拥有工厂方法模式的优点
(2)当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
(3)增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点:
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。

适用场景:
(1) 系统中有多于一个的产品族。而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。
(2) 产品等级结构稳定。设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。

单例模式

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

20988794-f6201c8b0aa3520f.png

Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。

(重点)
如何构建单例:
一是单例模式的类只提供私有的构造函数,
二是类定义中含有一个该类的静态私有对象,
三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

/* 懒汉式 */
class Chairman_lazy{
private:
    Chairman_lazy(){}
public:
    static Chairman_lazy* getInstance(){
        if (s_singleton == NULL){
            s_singleton = new Chairman_lazy;
        }
        return s_singleton;
    }
private:
    static Chairman_lazy* s_singleton;
};

Chairman_lazy* Chairman_lazy::s_singleton = NULL;

void test01(){
    
    Chairman_lazy* chairman1 = Chairman_lazy::getInstance();
    Chairman_lazy* chairman2 = Chairman_lazy::getInstance();
    if (chairman1 == chairman2){
        cout << "指向同一个对象!" << endl;
    }
    else{
        cout << "指向不是同一个对象!" << endl;
    }

}

/* 饿汉式 */
class Chairman_hangry{
private:
    Chairman_hangry(){}
public:
    static Chairman_hangry* getInstance(){
        return s_singleton;
    }
private:
    static Chairman_hangry* s_singleton;
};

//初始化
Chairman_hangry* Chairman_hangry::s_singleton = new Chairman_hangry;

void test02(){
    
    Chairman_hangry* chairman1 = Chairman_hangry::getInstance();
    Chairman_hangry* chairman2 = Chairman_hangry::getInstance();
    if (chairman1 == chairman2){
        cout << "指向同一个对象!" << endl;
    }
    else{
        cout << "指向不是同一个对象!" << endl;
    }
}

int main(){

    //test01();
    test02();

    
    return EXIT_SUCCESS;
}
单例模式遇到多线程时
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<Windows.h>
using namespace std;

/* 懒汉式 */
class Chairman_lazy{
private:
    Chairman_lazy(){}
public:
    static Chairman_lazy* getInstance(){
        if (s_singleton == NULL){
            //Sleep(1000); //等到1000秒
            s_singleton = new Chairman_lazy;
        }
        return s_singleton;
    }
private:
    static Chairman_lazy* s_singleton;
};

Chairman_lazy* Chairman_lazy::s_singleton = NULL;


/* 饿汉式 */
class Chairman_hangry{
private:
    Chairman_hangry(){}
public:
    static Chairman_hangry* getInstance(){
        return s_singleton;
    }
private:
    static Chairman_hangry* s_singleton;
};

//初始化
Chairman_hangry* Chairman_hangry::s_singleton = new Chairman_hangry;

DWORD WINAPI MyThread_hangry(LPVOID lpThreadParameter){
    
    Chairman_hangry* chairman = Chairman_hangry::getInstance();
    cout << "单例对象地址:" << (int*)chairman << endl;

    return 0;
}

//饿汉式单例碰到多线程测试
void test01(){

    HANDLE handler[10];
    for (int i = 0; i < 10;i++){
        handler[i] = CreateThread(NULL, NULL, MyThread_hangry, NULL, NULL, NULL);
    }

}

DWORD WINAPI MyThread_lazy(LPVOID lpThreadParameter){

    Chairman_lazy* chairman = Chairman_lazy::getInstance();
    cout << "单例对象地址:" << (int*)chairman << endl;

    return 0;
}

//懒汉式单例碰到多线程
void test02(){

    HANDLE handler[10];
    for (int i = 0; i < 10; i++){
        handler[i] = CreateThread(NULL, NULL, MyThread_lazy, NULL, NULL, NULL);
    }

}

int main(){

    test01();
    test02();
    return EXIT_SUCCESS;
}

运行结果:
饿汉式单例模式的单例对象地址是全部一致的
懒汉式单例模式的单例对象地址都不一致

由上面的结果可知,懒汉式单例模式下的多线程是不安全的。
原因:懒汉式的getInstance语句是这样的:

static Chairman_lazy* getInstance(){
        if (s_singleton == NULL){
            //Sleep(1000); //等到1000秒
            s_singleton = new Chairman_lazy;
        }
        return s_singleton;
    }

假如多个Chairman_lazy* Chairman_lazy::s_singleton = NULL;语句
因为多线程下这些语句会同时执行,那么会同时new很多实例,所以不安全

相反,饿汉模式只会在全局new一个实例,getInstance语句是直接返回指针,所以安全。

单例模式的优缺点及适用场景

优点:
(1)单例模式提供了对唯一实例的受控访问。
(2)节约系统资源。由于在系统内存中只存在一个对象。

缺点:
(1) 扩展略难。单例模式中没有抽象层。
(2) 单例类的职责过重。

适用场景:
(1) 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
(2) 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。

关于单例对象内存的释放问题

一般一个单例对象占用的内存很少,就算不释放内存也没关系
如果真的想是释放,可以用嵌套类来实现

最重要的一点,之所以不写析构函数,是因为单例模式的实例是存储在堆中,不会自动析构,所以需要借助内嵌类delete触发。

class Chairman_hangry{
private:
    Chairman_hangry(){}
        ~Chairman_hangry(){}
public:
    static Chairman_hangry* getInstance(){
        return s_singleton;
    }
class Garbo{
  public:
  ~Garbo(){
    if(s_singleton!=NULL)
        delete s_singleton;
  }
};

private:
    static Chairman_hangry* s_singleton;
        static Garbo garbo;
};
Chairman_hangry* Chairman_hangry::s_singleton=new Chairman_hangry;
Chairman_hangry::Garbo Chairman_hangry::garbo; //这句很重要
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值