设计模式——单例模式和工厂模式

本文转载于:https://www.jianshu.com/p/c579d9ba80be

1、单例模式

    确保某一个对象只有一个实例,而且自行实例化并向整个程序提供这个实例。单例模式为什么能保证只有一个实例存在,原因是在于它的构造函数是私有的,你不能去new 它,该单例类里面已经实例化好了一个了,并且是static的,并提供一个获取该实例的方法。客户端只能通过该方法获取这个已经实例化好了的,这样就保证了只有一个实例存在!

(1)优点

  • 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
  • 减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
  • 避免对资源的多重占用。如避免对同一个资源文件的同时写操作。
  • 单例模式可以在系统设置全局的访问点,优化和共享资源访问。

(2)缺点

  • 单例模式一般没有接口,扩展困难。
  • 不利于测试。

(3)使用场景

  • 要求生成唯一序列号的环境。
  • 在整个项目中需要一个共享访问点或共享数据
  • 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源。
  • 需要定义大量的静态常量和静态方法的环境。

(4)实现

  1)懒汉实现方法,即实例化在对象首次被访问时进行。可以使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。同时需将默认构造函数声明为private,防止用户调用默认构造函数创建对象。

class CSingleton
{
private:
    CSingleton(){};
    static CSingleton* m_pInstance;
    class CGarbo
    {
    public:
        ~CGarbo(){
            if(CSingleton::m_pInstance != NULL)
            delete CSingleton::m_pInstance;
        }
    };
    static CGarbo garbo;
public:
    static CSingleton* GetInstance(){
        if (m_Instance == NULL )
        {
            Lock(); 
            if (m_Instance == NULL )
            {
                m_Instance = new Singleton ();
            }
            UnLock(); 
        }
        return m_pInstance;
    }
};

    类CGarbo被定义为CSingleton的私有内嵌类,以防该类被在其他地方滥用。程序运行结束时,系统会调用CSingleton的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例。

  2)饿汉实现方法:在程序开始时就自行创建实例。如果说懒汉式是“时间换空间”,那么饿汉式就是“空间换时间”,因为一开始就创建了实例,所以每次用到的之后直接返回就好了。

class CSingleton
{
private:
    CSingleton(){};
    static CSingleton* m_pInstance;
    class CGarbo
    {
    public:
        ~CGarbo(){
            if(CSingleton::m_pInstance != NULL)
            delete CSingleton::m_pInstance;
        }
    };
    static CGarbo garbo;
public:
    static CSingleton* GetInstance(){
        return m_pInstance;
    }
};
CSingleton *CSingleton::m_pInstance = new CSingleton();

2、工厂模式

    这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

                       

意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个产品类,工厂模式使其创建过程延迟到子类进行。

主要解决:主要解决接口选择的问题。

何时使用:我们明确地计划不同条件下创建不同实例时。

如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。

优点:

  • 一个调用者想创建一个对象,只要知道其名称就可以了。
  • 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
  • 屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:

  •  每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

使用场景:

  • 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
  • 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
  • 设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。

注意事项:
    作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

实现

(1)简单工厂模式

#include <iostream>
using namespace std;

class Shape
{
public:
    virtual void draw()=0;
};

class Square : public Shape
{
public:
    void draw(){
        cout<<"This is a square."<<endl;
    }
};

class Rectangle : public Shape
{
public:
    void draw(){
        cout<<"This is a rectangle."<<endl;
    }
};

class Circle :public Shape
{
public:
    void draw(){
        cout<<"This is a circle"<<endl;
    }
};

class SimpleFactory
{
public:
    typedef enum ShapeType
    {
        ShapeSquare,
        ShapeRectangle,
        ShapeCircle
    }SHAPETYPE;
    Shape* getShape(SHAPETYPE type)
    {
        switch(type)
        {
        case ShapeSquare:
            return new Square();
        case ShapeRectangle:
            return new Rectangle();
        case ShapeCircle:
            return new Circle();
        default:
            return NULL;
        }
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    SimpleFactory* myFactory = new SimpleFactory();
    Shape* myShape = myFactory->getShape(SimpleFactory::ShapeCircle);
    myShape->draw();
    delete myShape;
    myShape = NULL;
    delete myFactory;
}

(2)工厂方法模式
    拥有几个工厂,都派生自同一抽象工厂类,可以生产不同的产品。

class AbstractProduct
{
public:
    virtual void operation()=0;
};

class ProductA : public AbstractProduct
{
public:
    virtual void operation(){
        cout<<"This is A!"<<endl;
    }
};

class ProductB : public AbstractProduct
{
public:
    virtual void operation(){
        cout<<"This is B!"<<endl;
    }
};

class AbstractFactory
{
public:
    virtual AbstractProduct* createProduct()=0;
};

class FactoryA : public AbstractFactory
{
public:
    AbstractProduct* createProduct(){
        return new ProductA();
    }
};

class FactoryB : public AbstractFactory
{
public:
    AbstractProduct* createProduct(){
        return new ProductB();
    }
};

int main()
{
    AbstractFactory* myFactory = new FactoryA();
    AbstractProduct* myProduct = myFactory->createProduct();
    myProduct->operation();
    delete myProduct;
    myProduct = NULL;
    delete myFactory;
    myFactory = NULL;
}

注意:在上述两个实现中,为简单起见,并没有定义构造函数和虚构函数。其中,基类的析构函数应为虚函数,这样才能保证,delete操作能够调用合适的析构函数。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值