C++模块5:常见的设计模式
1 .单例模式:
当仅允许类的一个实例在应用中被创建的时候,我们使用单例模式(Singleton Pattern)。它保护类的创建过程来确保只有一个实例被创建,它通过设置类的构造方法为私有(private)来实现。要获得类的实例,单例类可以提供一个方法,如GetInstance(),来返回类的实例。该方法是唯一可以访问类来创建实例的方法。
优点:
(1)由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
(2)减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
(3)避免对资源的多重占用。如避免对同一个资源文件的同时写操作。
(4)单例模式可以在系统设置全局的访问点,优化和共享资源访问。
缺点:
单例模式一般没有接口,扩展困难。不利于测试。
使用场景:
(1)在整个项目中需要一个共享访问点或共享数据。
(2)创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源。
(3)需要定义大量的静态常量和静态方法的环境。
实现:懒汉实现与饿汉实现:
懒汉实现,即实例化在对象首次被访问时进行。可以使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。同时需将默认构造函数声明为private,防止用户调用默认构造函数创建对象。
//Singleton.h
class Singleton
{
public:
static Singleton* GetInstance();
private:
Singleton() {
}
static Singleton *m_pInstance;
};
//Singleton.cpp
Singleton* Singleton::m_pInstance = NULL;
Singleton* Singleton::GetInstance()
{
if (m_Instance == NULL)
{
Lock();
if (m_Instance == NULL)
{
m_Instance = new Singleton();
}
UnLock();
}
return m_pInstance;
}
该类有以下特征:
A:它的构造函数是私有的,这样就不能从别处创建该类的实例。
B:它有一个唯一实例的静态指针m_pInstance,且是私有的。
C:它有一个公有的函数,可以获取这个唯一的实例,并在需要的时候创建该实例。
此处进行了两次m_Instance == NULL的判断,是借鉴了Java的单例模式实现时,使用的所谓的“双检锁”机制。因为进行一次加锁和解锁是需要付出对应的代价的,而进行两次判断,就可以避免多次加锁与解锁操作,同时也保证了线程安全。
上面的实现存在一个问题,就是没有提供删除对象的方法。一个妥善的方法是让这个类自己知道在合适的时候把自己删除。程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。
如下面的代码中的CGarbo类(Garbo意为垃圾工人):
class Singleton
{
public:
static Singleton* GetInstance() {
}
private:
Singleton() {
};
static Singleton *m_pInstance;
//CGarbo类的唯一工作就是在析构函数中删除CSingleton的实例
class CGarbo
{
public:
~CGarbo()
{
if (Singleton::m_pInstance != NULL)
delete Singleton::m_pInstance;
}
};
//定义一个静态成员,在程序结束时,系统会调用它的析构函数
static CGarbo Garbo;
};
类CGarbo被定义为Singleton的私有内嵌类,以防该类被在其他地方滥用。程序运行结束时,系统会调用Singleton的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例。
饿汉实现方法:在程序开始时就自行创建实例。如果说懒汉实现是“时间换空间”,那么饿汉实现就是“空间换时间”,因为一开始就创建了实例,所以每次用到的之后直接返回就好了。
//Singleton.h
class Singleton
{
public:
static Singleton* GetInstance();
private:
Singleton() {
}
static Singleton *m_pInstance;
class CGarbo
{
public:
~CGarbo()
{
if (Singleton::m_pInstance != NULL)
delete Singleton::m_pInstance;
}
};
static CGarbo garbo;
};
//Singleton.cpp
Singleton* Singleton::m_pInstance = new Singleton();
Singleton* Singleton::GetInstance()
{
return m_pInstance;
}
2 .简单工厂模式:
简单工厂模式的主要特点是需要在工厂类中做判断,从而创造相应的产品。当增加新的产品时,就需要修改工厂类。
例子:有一家生产处理器核的厂家,它只有一个工厂,能够生产两种型号的处理器核。客户需要什么样的处理器核,一定要显式地告诉生产工厂。
enum CTYPE {
COREA, COREB};
class SingleCore
{
public:
virtual void Show() = 0;
};
//单核A
class SingleCoreA: public SingleCore
{
public:
void Show() {
cout<<"SingleCore A"<<endl; }
};
//单核B
class SingleCoreB: public SingleCore
{
public:
void Show() {
cout<<"SingleCore B"<<endl; }
};
//唯一的工厂,可以生产两种型号的处理器核,在内部判断
class Factory
{
public:
SingleCore* CreateSingleCore(enum CTYPE ctype)
{
if (ctype == COREA) //工厂内部判断
return new SingleCoreA(); //生产核A
else if (ctype == COREB)
return new SingleCoreB(); //生产核B
else
return NULL;
}
};
这样设计的主要缺点之前也提到过,就是要增加新的核类型时,就需要修改工厂类。这就违反了开放封闭原则:软件实体(类、模块、函数)可以扩展,但是不可修改。于是,工厂方法模式出现了。
3 .工厂方法模式:
工厂方法模式是指定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法模式使一个类的实例化延迟到其子类。
例子:这家生产处理器核的厂家赚了不少钱,于是决定再开设一个工厂专门用来生产B型号的单核,而原来的工厂专门用来生产A型号的单核。这时,客户要做的是找好工厂,比如要A型号的核,就找A工厂要;否则找B工厂要,不再需要告诉工厂具体要什么型号的处理器核了。
class SingleCore
{
public:
virtual void Show() = 0;
};
//单核A
class SingleCoreA: public SingleCore
{
public:
void Show() {
cout<<"SingleCore A"<<endl; }
};
//单核B
class SingleCoreB: public SingleCore
{
public:
void Show() {
cout<<"SingleCore B"<<endl; }
};
class Factory
{
public:
virtual SingleCore* CreateSingleCore() = 0;
};
//生产A核的工厂
class FactoryA: public Factory
{
public:
SingleCoreA* CreateSingleCore() {
return new SingleCoreA(); }
};
//生产B核的工厂
class FactoryB: public Factory
{
public:
SingleCoreB* CreateSingleCore() {
return new SingleCoreB(); }
};
工厂方法模式也有缺点,每增加一种产品,就需要增加一个对象的工厂。如果这家公司发展迅速,推出了很多新的处理器核,那么就要开设相应的新工厂。在C++实现中,就是要定义一个个的工厂类。显然,相比简单工厂模式,工厂方法模式需要更多的类定义。
4 .抽象工厂模式:
抽象工厂模式的定义为提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
例子:这家公司的技术不断进步,不仅可以生产单核处理器,也能生产多核处理器。现在简单工厂模式和工厂方法模式都鞭长莫及。这家公司还是开设两个工厂,一个专门用来生产A型号的单核多核处理器,而另一个工厂专门用来生产B型号的单核多核处理器。
//单核
class SingleCore
{
public:
virtual void Show() = 0;
};
class SingleCoreA: public SingleCore
{
public:
void Show() {
cout<<"Single Core A"<<endl; }
};
class SingleCoreB :public SingleCore
{
public:
void Show() {
cout<<"Single Core B"<<endl; }
};
//多核
class MultiCore
{
public:
virtual void Show() = 0;
};
class MultiCoreA : public MultiCore
{
public:
void Show() {
cout<<"Multi Core A"<<endl; }
};
class MultiCoreB : public MultiCore
{
public:
void Show() {
cout<<"Multi Core B"<<endl; }
};
//工厂
class CoreFactory
{
public:
virtual SingleCore* CreateSingleCore() = 0;
virtual MultiCore* CreateMultiCore() = 0;
};
//工厂A,专门用来生产A型号的处理器
class FactoryA :public CoreFactory
{
public:
SingleCore* CreateSingleCore() {
return new SingleCoreA(); }
MultiCore* CreateMultiCore() {
return new MultiCoreA(); }
};
//工厂B,专门用来生产B型号的处理器
class FactoryB : public CoreFactory
{
public:
SingleCore* CreateSingleCore() {
return new SingleCoreB(); }
MultiCore* CreateMultiCore() {
return new MultiCoreB(); }
};