学会了这么神奇的模版模式,让你C++模版编程之路事半功倍

最近由于开发工作的需要,项目引入了boost::statechart的状态机,它大量了引用了CRTP,  它的全称是Curiously Recurring Template Pattern,奇异递归模版模式,C++模版编程中很常用的一种用法。那么它神奇的地方到底在哪里呢,接下来就一一来揭开它神秘的面纱。

一、奇异递归模版模式的简介

奇异递归模版模式的基本思想要点是:派生类要作为基类的模版参数。它是C++模版编程中常用的手法。理解它之后,学习模版编程过程中也会事半功倍,而不会觉得云里雾里的。

二、奇异递归模版模式的基本格式

奇异递归模版模式的基本格式如下:JCrtpDerived继承JCrtpBase,并且JCrtpDerived作为基类JCrtpBase的模版参数。通过这样的方式,基类就可以使用子类的方法。并且不需要使用到虚函数,一定程度上减少程序的开销。

template <typename T>
class JCrtpBase
{
public:
};

class JCrtpDerived : public JCrtpBase<JCrtpDerived>
{
public:

};

三、奇异递归模版模式的入门

从上面的给出的奇异递归模版模式的基本格式中可以看出,子类是作为基类的模版参数,但是如果传递给基类的模版参数不是基类的子类,那就会造成混乱错误。如下图所示,JCrtpDerived2子类继承了基类JCrtpBase,但是传递给基类的模版参数不是JCrtpDerived2。

template <typename T>
class JCrtpBase
{
public:
    void Do()
    {
        T* derived = static_cast<T *>(this);
    }

};

class JCrtpDerived1 : public JCrtpBase<JCrtpDerived1>
{
public:

};

class JCrtpDerived2 : public JCrtpBase<JCrtpDerived1>
{
public:

};

那么如何解决上面的问题呢,可以将基类的默认构造函数设置为私有,并且模版参数T设置为基类的友元。通过这样的方式,基类的构造函数只能由模版参数T调用。当创建JCrtpDerived2子类对象的时候,会调用基类的构造函数,而这时候发现JCrtpDerived2不是基类的友元,那么就无法调用基类构造函数而出错。

template <typename T>
class JCrtpBase
{
public:
    void Do()
    {
        T* derived = static_cast<T *>(this);
    }

private:
   JCrtpBase();
   friend T;
};

调用运行JCrtpDerived2,就会出现错误

JCrtpDerived1 crtp_derived1;
crtp_derived1.Do();

JCrtpDerived2 crtp_derived2;
crtp_derived2.Do();

四、奇异递归模版模式的应用场景

1、静态多态

奇异递归模版模式可以实现静态多态的效果,顾名思义,就是有多态的特性,但是不需要使用虚函数,是编译的时候确定,因此,能够减少运行时的开销。接下来就来看看两个示例。

基类JCrtpBase实现函数Do,该函数内部对象通过static_cast转换为模版参数对象,模版参数对象再调用对应的实现函数,而模版参数对象由子类来实现。

template <typename T>
class JCrtpBase
{
public:
    void Do()
    {
        T* derived = static_cast<T *>(this);
        derived->DoSomething();
    }

private:
    JCrtpBase(){}
    friend T;
};

class JCrtpDerived1 : public JCrtpBase<JCrtpDerived1>
{
public:
    void DoSomething()
    {
        LOG(INFO) << "I'am is JCrtpDerived1";
    }

};

class JCrtpDerived2: public JCrtpBase<JCrtpDerived2>
{
public:
    void DoSomething()
    {
        LOG(INFO) << "I'am is JCrtpDerived2";
    }
};

调用运行的效果如下所示,从中可以看出,对象调用基类的函数,而基类函数实际上又去调用子类的函数DoSomething。基于这样的思想,我们可以将通用的逻辑放在基类Do中实现,而不同的放到对应的子类函数DoSomething实现。

/// 调用   
JCrtpDerived1 crtp_derived1;
crtp_derived1.Do();

JCrtpDerived2 crtp_derived2;
crtp_derived2.Do();

/// 运行信息
[void JCrtpDerived1::DoSomething():33] I'am is JCrtpDerived1
[void JCrtpDerived2::DoSomething():43] I'am is JCrtpDerived2

这样需要注意的一点是,如果子类再被其他子类继承,那么其他子类就不能按照上面的方式实现。具体可以看下示例:JCrtpSub子类再继承JCrtpDerived1。

class JCrtpSub: public JCrtpDerived1
{
public:
    void DoSomething()
    {
        LOG(INFO) << "I'am is JCrtpSub";
    }
};

调用运行的效果如下所示,JCrtpSub调用基类的函数Do,但是运行没有调用到JCrtpSub类自身的函数DoSomething。

/// 调用
JCrtpDerived1 crtp_derived1;
crtp_derived1.Do();

JCrtpDerived2 crtp_derived2;
crtp_derived2.Do();

JCrtpSub ctrp_sub;
ctrp_sub.Do();

/// 运行信息
[void JCrtpDerived1::DoSomething():33] I'am is JCrtpDerived1
[void JCrtpDerived2::DoSomething():43] I'am is JCrtpDerived2
[void JCrtpDerived1::DoSomething():33] I'am is JCrtpDerived1

上面的例子是子类调用基类函数,由基类再转换调用子类函数,效果类似于策略模式。下面将要说明的例子,更像多态特性,但是不需要虚函数。基类和子类都实现相同的函数DoSomething

template <typename T>
class JCrtpBase
{
public:
    void DoSomething()
    {
        static_cast<T *>(this)->DoSomething();
    }

private:
    JCrtpBase(){}
    friend T;
};

class JCrtpDerived1 : public JCrtpBase<JCrtpDerived1>
{
public:
    void DoSomething()
    {
        LOG(INFO) << "I'am is JCrtpDerived1";
    }

};

class JCrtpDerived2: public JCrtpBase<JCrtpDerived2>
{
public:
    void DoSomething()
    {
        LOG(INFO) << "I'am is JCrtpDerived2";
    }
};

然后实现模版方法,该方法入参为基类JCrtpBase的引用,内部调用基类函数DoSomething。

template<typename T>
void DoAction(JCrtpBase<T> &ctrpbase)
{
    ctrpbase.DoSomething();
}

调用运行效果如下,向模版方法传递不同的子类,调用对应子类的函数。

/// 调用
JCrtpDerived1 crtp_derived1;
JCrtpDerived2 crtp_derived2;
DoAction(crtp_derived1);
DoAction(crtp_derived2);

// 打印信息
[void JCrtpDerived1::DoSomething():38] I'am is JCrtpDerived1
[void JCrtpDerived2::DoSomething():48] I'am is JCrtpDerived2

2、boost::statechart状态机

Boost.Statechart大量使用了CRTP,   派生类必须作为第一个参数传递给所有基类模版,Boost.Statechart状态机后续考虑作为一个专题来研究讨论。

struct Greeting : sc::simple_state< Greeting, Machine >

3、std::enable_shared_from_this特性

C++的特性enable_shared_from_this通常是用于在当你要返回一个被shared_ptr管理的对象。JObj继承enable_shared_from_this,并且JObj作为参数模版传递给enable_shared_from_this,这里就运用到了CRTP。

class JObj : public std::enable_shared_from_this<JObj>
{
public:
    std::shared_ptr<JObj> GetObj() {
        return shared_from_this();
    }
};

正确的调用方式,JObj是被shared_ptr管理,因此,如果要获取对象,那么JObj需要继承enable_shared_from_this。

std::shared_ptr<JObj> share_obj1 = std::make_shared<JObj>();
// JObj对象被shared_ptr管理,因此,如果要获取对象,那么JObj需要继承enable_shared_from_this
std::shared_ptr<JObj> share_obj2 = share_obj1->GetObj();

五、总结

到这里,奇异递归模版模式已经基本讲解完成,我们首先介绍了它的基本格式,使用注意要点,然后重点讲解了它的应用场景,包括静态多态、boost::statechart状态机、std::enable_shared_from_this特性。理解了奇异递归模版模式,不但有利于模版编程的学习,而且对于以后应用的开发也是有好处的。

设计模式详解十一:Flyweight模式

flyweight意思是轻量级,该模式主要为了解决有些场景会产生许多对象,并且其中的许多对象都是相同的。flyweight提供创建对象接口,并且内部保存对象池,当请求的对象没有在对象池中,那么重新创建对象放入对象池,如果请求的对象存在对象池中,那么直接从对象池取出对象返回给请求者,从而避免对象的频繁创建,造成系统开销的问题。

一、设计模式的结构图

Flyweight模式,FlyweightFactory根据key值提供获取Flyweight对象的接口,

二、程序代码实现

1、基类Flyweight的定义和实现

/// 类的定义
class Flyweight
{
public:
    virtual ~Flyweight();
    virtual void Operation(const std::string& strParam);
    std::string GetState();
    
protected:
    Flyweight(std::string strState);
private:
    std::string m_strState;
};

/// 类的实现
Flyweight::Flyweight(std::string strState)
{
    this->m_strState = strState;
}

Flyweight::~Flyweight()
{
}

void Flyweight::Operation(const std::string& strParam)
{
}
string Flyweight::GetState()
{
    return this->m_strState;
}

2、子类SubFlyweight的定义和实现

/// 类的定义
class SubFlyweight:public Flyweight
{
public:
    SubFlyweight(std::string strState);
    ~SubFlyweight();
    
    void Operation(const std::string& strParam);

protected:
private:
};


/// 类的实现
SubFlyweight::SubFlyweight(std::string strState):Flyweight(strState)
{
    DEBUG_LOG("SubFlyweight Build..... ", strState);
}

SubFlyweight::~SubFlyweight()
{
}

void SubFlyweight::Operation(const string& strParam)
{

}

3、FlyweightFactory工厂类的定义和实现

/// 类的定义
class FlyweightFactory
{
public:
    FlyweightFactory();
    ~FlyweightFactory();
    Flyweight* GetFlyweight(const std::string& key);
    
protected:
private:
    vector<Flyweight*> m_fly;
};


/// 类的实现
FlyweightFactory::FlyweightFactory()
{
}

FlyweightFactory::~FlyweightFactory()
{
}

Flyweight* FlyweightFactory::GetFlyweight(const std::string& key)
{
    vector<Flyweight*>::iterator it = m_fly.begin();
    for (; it != m_fly.end();it++)
    {
        if ((*it)->GetState() == key)
        {
            cout<<"already created by users...."<<endl;
            return *it;
        }
    }
    Flyweight* fw = new SubFlyweight(key);
    m_fly.push_back(fw);
    return fw;
}

三、测试验证结果

1、主程序输入如下所示的测试代码

FlyweightFactory* fc = new FlyweightFactory();
Flyweight* fw1 = fc->GetFlyweight("li");
Flyweight* fw2 = fc->GetFlyweight("zheng!");
Flyweight* fw3 = fc->GetFlyweight("li");

2、运行结果如下所示,第三次请求对象,因为已经存在在对象池中,所以直接从对象池中获取对象并返回

[2019-09-14 19:54:45.160  SubFlyweight:31]  SubFlyweight Build.....  = li
[2019-09-14 19:54:45.160  SubFlyweight:31]  SubFlyweight Build.....  = zheng!
already created by users....

四、总结

FlyweightFactory类采用vector存储对象,当然需要根据具体场景来选择合适的容器,比如,map、hash表等。需要注意的是FlyweightFactory查找的key值是存放在Flyweight对象中的成员变量。

设计模式详解十:外观模式

外观模式,即Facade模式,封装了内部细节,对外提供统一接口。比如做一件事的时候,需要经过几个步骤,但是使用者并不关心内部细节,因此,这时候就可以采用外观模式,将几个步骤封装起来,然后提供一个统一的接口。

一、设计模式的结构图

外观模式对外提供统一接口OperationWrapper, 内部封装两个子系统的接口。

二、程序代码实现

1、定义和实现子系统类1

/// 类的定义
class Subsystem1
{
public:
    Subsystem1();
    ~Subsystem1();
    void Operation();
    
protected:
private:
};


/// 类的实现
Subsystem1::Subsystem1()
{
}

Subsystem1::~Subsystem1()
{
}

void Subsystem1::Operation()
{
    DEBUG_LOG("", "Subsystem1 operation...");
}

2、定义和实现子系统类2

/// 类的定义
class Subsystem2
{
public:
    Subsystem2();
    ~Subsystem2();
    void Operation();

protected:
private:
};


/// 类的实现
Subsystem2::Subsystem2()
{
}

Subsystem2::~Subsystem2()
{
}
void Subsystem2::Operation()
{
    DEBUG_LOG("", "Subsystem2 operation...");
}

3、定义和实现外观模式

/// 类的定义
class Facade
{
public: Facade();
    ~Facade();
    void OperationWrapper();

protected:
private:
    Subsystem1* m_subs1;
    Subsystem2* m_subs2;
};


/// 类的实现
Facade::Facade()
{
    this->m_subs1 = new Subsystem1();
    this->m_subs2 = new Subsystem2();
}

Facade::~Facade()
{
    delete m_subs1;
    delete m_subs2;
}

void Facade::OperationWrapper()
{
    this->m_subs1->Operation();
    this->m_subs2->Operation();
}

从外观模式类看,它负责控制两个子系统类的创建和销毁,对外提供统一接口,内部实现调用两个子系统的函数。

三、测试验证结果

1、主程序输入如下所示的测试代码

Facade* facade = new Facade();
facade->OperationWrapper();

2、运行结果如下所示

[2019-09-08 16:16:18.993  Operation:21]   = Subsystem1 operation...
[2019-09-08 16:16:18.993  Operation:34]   = Subsystem2 operation...

四、总结

外观模式在实际中应用比较广泛,它通常采用单例模式实现,比如采用外观模式封装各个对象的创建过程。

设计模式详解九:迭代器模式

迭代器模式, 即iterator模式,封装集合对象,提供访问集合的接口。

一、设计模式的结构图

迭代器模式的处理逻辑是:iterator迭代器内部保存持有集合对象,用于访问集合,而集合提供创建返回迭代器的函数接口。

二、程序代码实现

1、 定义实现迭代器的基类

#ifndef Object
typedef int Object;
#endif


/// 类定义
class Iterator
{
public:
    virtual ~Iterator();
    virtual void First() = 0;
    virtual void Next() = 0;
    virtual bool IsDone() = 0;
    virtual Object CurrentItem() = 0;
    
protected:
    Iterator();
private:
};


/// 类实现

Iterator::Iterator()
{
    
}

Iterator::~Iterator()
{
    
}

2、 定义实现迭代器的子类

/// 类的定义
class Aggregate;
class ConcreteIterator : public Iterator
{
public:
    ConcreteIterator();
    ConcreteIterator(Aggregate *ag, int idx = 0);
    ~ConcreteIterator();
    void First();
    void Next();
    bool IsDone();
    Object CurrentItem();

protected:
private:
    Aggregate* m_ag;
    int m_idx;
};

/// 类的实现
ConcreteIterator::ConcreteIterator()
{
    
}

ConcreteIterator::ConcreteIterator(Aggregate *ag, int idx)
{
    this->m_ag = ag;
    this->m_idx = idx;
}

ConcreteIterator::~ConcreteIterator()
{
}

Object ConcreteIterator::CurrentItem()
{
    return m_ag->GetItem(m_idx);
}

void ConcreteIterator::First()
{
    m_idx = 0;
}

void ConcreteIterator::Next()
{
    if (m_idx < m_ag->GetSize())
    {
        m_idx++;
    }
}

bool ConcreteIterator::IsDone()
{
    return (m_idx == m_ag->GetSize());
}

3、定义实现集合的基类

#ifndef Object
typedef int Object;
#endif

/// 类的定义
class Iterator;
class Aggregate
{
public:
    virtual ~Aggregate();
    virtual Iterator* CreateIterator() = 0;
    virtual Object GetItem(int idx) = 0;
    virtual int GetSize() = 0;
    
protected:
    Aggregate();
private:
};


/// 类的实现
Aggregate::Aggregate()
{
}

Aggregate::~Aggregate()
{
}

4、定义实现集合的子类

/// 类的定义
class ConcreteAggregate:public Aggregate
{
public:
    enum {SIZE = 3};
    ConcreteAggregate();
    ~ConcreteAggregate();
    Iterator* CreateIterator();
    Object GetItem(int idx);
    int GetSize();
    
protected: private:
    Object m_objs[SIZE];
};


/// 类的实现
ConcreteAggregate::ConcreteAggregate()
{
    for (int i = 0; i < SIZE; i++)
        m_objs[i] = i;
}

ConcreteAggregate::~ConcreteAggregate()
{
}

Iterator* ConcreteAggregate::CreateIterator()
{
    return new ConcreteIterator(this);
}

Object ConcreteAggregate::GetItem(int idx)
{
    if (idx < this->GetSize())
    {
        return m_objs[idx];
    }
    else
    {
        return -1;
    }
}

int ConcreteAggregate::GetSize()
{
    return SIZE;
}

 

三、测试验证结果

1、主程序输入如下所示的测试代码

Aggregate* ag = new ConcreteAggregate();
Iterator* it = new ConcreteIterator(ag);
for (; !(it->IsDone()) ; it->Next())
{
    std::cout<< "item = "<< it->CurrentItem()<<std::endl;
}

2、运行结果如下所示,迭代器访问集合内的所有数据,并输出显示

item = 0
item = 1
item = 2

四、总结

迭代器的实现过程中,为了避免集合过多的暴露公有接口,可以将迭代器声明为集合的友元,来访问集合的私有数据。另外,从上面的代码实现中,可以看出集合提供创建迭代器的接口,而迭代器持有集合对象,内部包装集合的访问接口。

设计模式详解八:Starategy模式

strategy策略模式就是实现和抽象解耦,逻辑处理放到Context中,并在逻辑处理中调用抽象接口,而组合对象实现具体的内容。

一、设计模式的结构图

策略模式context实现主要逻辑,具体实现放到strategy类,strategy是抽象基类,子类继承该基类,实现具体的功能,context与strategy是一对多的组合关系,这样实现的好处就是context可以支持多个strategy类型对象,strategy类型也可以复用到其他不同的逻辑算法。

二、程序代码实现

1、定义实现抽象基类Strategy

class Strategy
{
public:
    Strategy();
    virtual ~Strategy();
    virtual void AlgrithmInterface() = 0;

protected:
private:
};

Strategy::Strategy()
{
}

Strategy::~Strategy()
{
    DEBUG_LOG("", "~Strategy...");
}

void Strategy::AlgrithmInterface()
{
}

2、定义实现基类Strategy的子类ConcreteStrategyA

class ConcreteStrategyA:public Strategy
{
public:
    ConcreteStrategyA();
    virtual ~ConcreteStrategyA();
    void AlgrithmInterface();

protected:
private:
};

ConcreteStrategyA::ConcreteStrategyA()
{
}

ConcreteStrategyA::~ConcreteStrategyA()
{
    DEBUG_LOG("", "~ConcreteStrategyA...");
}

void ConcreteStrategyA::AlgrithmInterface()
{
    DEBUG_LOG("", "ConcreteStrategyA::AlgrithmInterface...");
}

3、定义实现基类Strategy的子类ConcreteStrategyB

class ConcreteStrategyB:public Strategy {
public:
    ConcreteStrategyB();
    virtual ~ConcreteStrategyB();
    void AlgrithmInterface();
    
protected:
private:
};

ConcreteStrategyB::ConcreteStrategyB()
{
}

ConcreteStrategyB::~ConcreteStrategyB()
{
    DEBUG_LOG("", "~ConcreteStrategyB...");
}

void ConcreteStrategyB::AlgrithmInterface()
{
    DEBUG_LOG("", "ConcreteStrategyB::AlgrithmInterface...");
}

4、定义实现Context类

class Context
{
public:
    Context(Strategy* stg);
    ~Context();
    void DoAction();
protected:
private:
    Strategy* m_stg;
};

Context::Context(Strategy* stg)
{
    m_stg = stg;
}

Context::~Context()
{
    if (!m_stg)
        delete m_stg;
}
void Context::DoAction()
{
    m_stg->AlgrithmInterface();
}

三、测试验证结果

1、主程序输入如下所示的测试代码

Strategy* p_strategy;
p_strategy = new ConcreteStrategyA();
Context* p_context = new Context(p_strategy);
p_context->DoAction();
if (NULL != p_context)
{
    delete p_context;
    p_context = NULL;
}

2、运行结果如下图所示,Context对象内部最终调用ConcreteStrategyA对象的算法功能。

[2019-06-30 14:30:24.680  AlgrithmInterface:35]   = ConcreteStrategyA::AlgrithmInterface...

四、总结

策略模式,不变的逻辑算法放到Context对象中实现,而动态变化的算法实现则由Strategy子类来实现,这里有一点需要注意的是Context析构的时候,也会析构Strateby类型的对象,所以,外部定义的Strateby对象不需要再次进行delete。

设计模式详解七:Adapter模式

Adapter模式就是当已经设计好的接口与第三方库的接口不一致的时候,可以起到兼容的作用。Adapter模式一般分为:类模式(继承方式)和对象模式(组合方式)。

一、设计模式的结构图

1、类模式是采用继承的方式来复用Adaptee的接口

2、对象模式是采用组合的方式来调用Adaptee接口

二、程序代码实现

1、定义和实现用户的接口Target

//定义用户接口
class Target
{
public:
    Target();
    virtual ~Target();
    virtual void Request();
    
protected:
private:
};

//实现用户接口
Target::Target()
{
}

Target::~Target()
{
}

void Target::Request()
{
    DEBUG_LOG("", "Target::Request...");
}

2、 定义和实现第三方库接口Adaptee

//第三方
class Adaptee
{
public:
    Adaptee();
    ~Adaptee();
    void SpecificRequest();

protected:
private:
};


Adaptee::Adaptee()
{
}

Adaptee::~Adaptee()
{
}

void Adaptee::SpecificRequest()
{
    DEBUG_LOG("", "Adaptee::SpecificRequest...");
}

3、定义和实现类模式的适配器

//适配器1
class AdapterOne:public Target,private Adaptee
{
public:
    AdapterOne();
    ~AdapterOne();
    void Request();
    
protected:
private:
};


AdapterOne::AdapterOne()
{
}

AdapterOne::~AdapterOne()
{
}

void AdapterOne::Request()
{
    this->SpecificRequest();
}

4、定义和实现对象模式的适配器

//适配器2
class AdapterTwo:public Target
{
public:
    AdapterTwo(Adaptee* ade);
    ~AdapterTwo();
    void Request();

protected:
private:
    Adaptee* _ade;
};


AdapterTwo::AdapterTwo(Adaptee* ade)
{
    this->_ade = ade;
}

AdapterTwo::~AdapterTwo()
{
}

void AdapterTwo::Request()
{
    _ade->SpecificRequest();
}

三、测试验证结果

1、主函数输入如下所示的测试代码段,用来测试对象模式的适配器模式

Adaptee* ade = new Adaptee;
Target* adt = new AdapterTwo(ade);
adt->Request();

2、运行结果如下图所示,调用Request之后,内部实际上调用的是第三方库接口

[2019-06-23 13:51:20.713  SpecificRequest:36]   = Adaptee::SpecificRequest...

四、总结

适配器顾名思义就是一个转接口,将目标对象和第三方连接起来,Target与Adapter之间存在多态的特性,而Adapter内部调用Adaptee完成实际的功能。

设计模式详解六:装饰者模式

Decorator模式,即装饰者模式是通过组合的方式给类添加新的职责。通过组合的好处是不会增加类的继承深度。

一、设计模式的结构图

结构图中,SubComponent和Decorator都继承自同一个基类Component, SubComponet继承自Component基类,这个体现多态的特性,大家应该都明白。而Decorator也继承自Component基类是为什么呢?我的理解是,Decorator是为component添加新的职责,它应该是属于Component,后续才能同SubComponent一样的方式进行使用。Decorator作为SubDecorator的基类,是为了能够提供更多的修饰,比如后续有新的修饰SubDecorator2,那么原来的实现的接口是不需要修改的。

二、程序代码实现

1、定义和实现Component基类

class Component
{
public:
    virtual ~Component();
    virtual void Operation();
protected:
    Component();
private:
};


Component::Component()
{
}

Component::~Component()
{
}

void Component::Operation()
{
}

2、定义和实现SubComponent

class SubComponent:public Component
{
public:
    SubComponent();
    ~SubComponent();
    void Operation();

protected:
private:
};

SubComponent::SubComponent() {}

SubComponent::~SubComponent() {}

void SubComponent::Operation()
{
    DEBUG_LOG("", "SubComponent operation..." );
}

3、定义和实现Decorator类

class Decorator:public Component
{
public:
    Decorator(Component* com);
    virtual ~Decorator();
    void Operation();
    
protected:
    Component* m_com;
private:
};


Decorator::Decorator(Component* com)
{
    this->m_com = com;
}

Decorator::~Decorator()
{
    delete m_com;
}

void Decorator::Operation()  {}

4、定义和实现SubDecorator

class SubDecorator:public Decorator
{
public:
    SubDecorator(Component* com);
    ~SubDecorator();
    void Operation();
    void AddedBehavior();
    
protected:
private:
};


SubDecorator::SubDecorator(Component* com):Decorator(com)  {}

SubDecorator::~SubDecorator()  {}

void SubDecorator::AddedBehavior()
{
    DEBUG_LOG("", "SubDecorator::AddedBehacior...." );
}

void SubDecorator::Operation()
{
    m_com->Operation();
    this->AddedBehavior();
}

三、测试验证结果

程序输入如下所示的代码段,然后运行调试查看结果:

Component* com = new SubComponent();
Decorator* dec = new SubDecorator(com);
dec->Operation();
//com在dec在析构函数中释放
delete dec;

运行结果如下所示,SubDecorator内部调用SubComponent后,并且为SubComponent添加新的行为。

[2019-06-02 16:20:02.135  Operation:29]   = SubComponent operation...
[2019-06-02 16:20:02.135  AddedBehavior:53]   = SubDecorator::AddedBehacior....

四、总结

Decorator模式提供了一种添加职责的方式,采用组合的方式,因此,当需要添加操作的时候,可以考虑Decorator模式来解决。

备注:Decorator与Component之间是整体与部分的关系,一个Decorator可以对应多个Component对象。

设计模式详解五:Factory模式


创建Factory模式(工厂模式),主要有两个目的,(1)如果一个基类有很多的子类,那么可以通过Factory模式统一创建子类对象, 这样可以达到方便维护,提高扩展性的目的; (2) 将具体化类的工作延迟到子类中,举个例子:父类A会调用到类B,进行逻辑的判断处理,但是类B是一个抽象类,类A不知道类B的具体类是什么,当创建类A的子类C就可以知道类B的子类D,这样就可以达到将不变的逻辑放在父类处理,而将具体的实现放到子类中。

一、设计模式的结构图

1、Factory类提供创建类Product的接口CreateProduct,这是工厂模式的第一个作用:负责维护众多类的创建。

2、Factory类是抽象类,Product也是抽象类,可以在抽象类中做公共的处理,而具体的实现工作延迟到子类,这就是工厂模式的第二个作用。

二、程序代码实现

首先定义和实现生产基类和子类

1、定义生产基类和它的子类

class Product;

//生产基类
class Product
{
public:
    virtual ~Product() = 0;
    
protected:
    Product();
    
private:
};

//生产子类
class SubProduct:public Product
{
public:
    ~SubProduct();
    SubProduct();
    
protected:
private:
};

2、实现生产基类和它的子类

Product::Product()
{
}

Product::~Product()
{
}

SubProduct::SubProduct()
{
    DEBUG_LOG("", "SubProduct...");
}

SubProduct::~SubProduct()
{
}

 

下面是定义和实现工厂的基类和它的子类

1、定义工厂的基类和它的子类

//工厂基类
class Factory
{
public:
    virtual ~Factory() = 0;
    virtual Product* CreateProduct() = 0;
    
protected:
    Factory();
    
private:
};

//工厂子类
class SubFactory:public Factory
{
public:
    ~SubFactory();
    SubFactory();
    
    Product* CreateProduct();
protected:
    
private:
};

2、实现工厂的基类和它的子类

Factory::Factory()
{
}

Factory::~Factory()
{
}

SubFactory::SubFactory()
{
    DEBUG_LOG("", "SubFactory...");
}

SubFactory::~SubFactory()
{
}

Product* SubFactory::CreateProduct()
{
    return new SubProduct();
}

三、测试验证结果

程序输入如下所示的代码段,然后运行调试查看结果:

Factory *fac = new SubFactory();
Product *product = fac->CreateProduct();

delete fac;
delete product;

运行结果如下所示,构造具体的工厂对象,然后工厂对象创建具体的生产对象。

[2019-05-25 14:03:49.814  SubFactory:45]   = SubFactory...
[2019-05-25 14:03:49.814  SubProduct:27]   = SubProduct...

四、总结

工厂模式可以解决两个问题,第一个是负责对象的创建,第二个是将具体类的工作延迟到子类中。上面的示例中,通过SubFactory调用CreateProduct创建具体类SubProduct,如果这时候又要创建一个新的类SubProduct2,  那么有得在SubFactory中创建方法CreateProduct2来创建具体类SubProduct2,这样类SubFactory就封闭不了,那么解决的办法就是创建Factory的一个新的子类SubFactory2。另一个方式就是CreateProduct方法提供一个参数,然后通过参数来决定创建具体的Product。

设计模式详解四:Bridge模式

Bridge模式是将两个结合的对象进行解耦,以便达到一个对象的修改不会影响到另一个对象的目的。比如,不同操作系统和不同算法的两个对象,操作系统可以调用不同的算法,而不同的算法可以运行在不同的操作系统上。这里操作系统我们认为是抽象部分,算法是实现部分。

一、设计模式的结构图

Abstraction对应操作系统部分, Imp对应算法部分。Abstraction是操作系统的基类,SubAbstraction是该基类的子类,负责实现操作系统上的公共的部分。Imp是算法的基类,SubImpA和SubImpB是该基类的子类,负责实现算法功能。Abstraction和Imp之间是一对多的关系,一个操作系统可以支持多种算法。具体形象的结构图如下图:

二、程序代码实现

首先定义实现操作系统的部分。

1、定义操作系统的基类以及它的子类

//操作系统基类
class Abstraction
{
public:
    virtual ~Abstraction();
    virtual void Operation() = 0;

protected:
    Abstraction();
    private:
};

//具体操作系统的实现
class SubAbstraction:public Abstraction {
public:
    SubAbstraction(Imp* imp);
    ~SubAbstraction();
    void Operation();

protected:
    
private:
    Imp* m_imp;
};

2、实现操作系统的基类以及它的子类

Abstraction::Abstraction()
{
}
Abstraction::~Abstraction()
{
}


SubAbstraction::SubAbstraction(Imp* imp)
{
    m_imp = imp;
}

SubAbstraction::~SubAbstraction()
{
}

void SubAbstraction::Operation()
{
    m_imp->Operation();
}

然后定义实现算法的部分

1、定义算法的基类以及它的子类

//算法的基类
class Imp
{
public:
    virtual ~Imp();
    virtual void Operation() = 0;

protected:
    Imp();
private:
};

//具体算法的实现A
class SubImpA:public Imp
{
public:
    SubImpA(); ~SubImpA();
    virtual void Operation();
    
protected:
private:
};

//具体算法的实现B
class SubImpB:public Imp
{
public:
    SubImpB(); ~SubImpB();
    virtual void Operation();
    
protected:
private:
};

2、实现算法的基类以及它的子类

Imp::Imp()
{
}

Imp::~Imp()
{
}

void Imp::Operation()
{
    DEBUG_LOG("", "call Imp");
}


SubImpA::SubImpA()
{
}

SubImpA::~SubImpA()
{
}

void SubImpA::Operation()
{
    DEBUG_LOG("", "call SubImpA");
}


SubImpB::SubImpB()
{
}

SubImpB::~SubImpB()
{
}

void SubImpB::Operation()
{
    DEBUG_LOG("", "call SubImpB");
}

三、测试验证结果

主程序中输入如下的代码段,然后编译运行查看它的运行结果

Imp *imp = new SubImpA();
Abstraction *abs = new SubAbstraction(imp);
abs->Operation();

Imp *imp2 = new SubImpB();
Abstraction *abs2 = new SubAbstraction(imp2);
abs2->Operation();

运行结果如下所示,操作系统和算法的对象通过组合的方式结合,操作系统根据不同的算法对象调用不同的算法,而操作系统内部的处理逻辑是不需要改变的。

[2019-05-19 21:16:12.453  Operation:61]   = call SubImpA
[2019-05-19 21:16:12.453  Operation:73]   = call SubImpB

四、总结

Bridge模式的原理是采用组合的方式,将两个对象结合起来,两个对象各自实现内部的处理逻辑,但是对外接口保持不变。比如上面的操作系统和算法两个对象,操作系统结合C++的多态特性,调用算法的接口,而算法接口不变,根据不同的算法内部实现不同的算法处理逻辑。当增加新的操作系统的时候,算法不需要修改。当增加新的算法的时候,操作系统也不需要修改。真正实现两个对象解耦的目的。

 

设计模式详解三:Observer模式(观察者模式)

观察者模式是一种“一(Subject)对多(Observer)”的设计模式,并且当“一”发生变化的时候,对应的“多”也能够同步收到通知或者改变。

一、设计模式的结构图

Observer模式的结构图:Subject目标基类中的Attatch函数实现将Observer指针存储到list列表,Detach函数实现将Observer指针从list列表中移除,Notify函数内部是循环检查list列表中存放的Observer指针,如果存在,那么调用Observer实现的Update函数。Observer观察者基类定义Update虚函数,它的继承者SubObserverA、SubObserverB……实现Update函数。

二、程序代码实现

1、首先定义目标基类Subject以及它的子类SubSubject

class Observer;

////目标基类
class Subject
{
public:
    virtual ~Subject();
    virtual void Attach(Observer* obv);
    virtual void Detach(Observer* obv);
    virtual void Notify();
    
protected:
    
    Subject();
private:
    list<Observer* >* m_obvs;
};

////目标实现类
class SubSubject:public Subject
{
public:
    SubSubject();
    ~SubSubject();

protected:

private:
    
};

2、实现目标基类以及它的子类SubSubject

////目标基类
Subject::Subject()
{
    m_obvs = new list<Observer*>;
}

Subject::~Subject()
{
    
}

void Subject::Attach(Observer* obv)
{
    m_obvs->push_front(obv);
}

void Subject::Detach(Observer* obv)
{
    if (obv != NULL) m_obvs->remove(obv);
}

void Subject::Notify()
{
    list<Observer*>::iterator it;
    it = m_obvs->begin();
    for (;it != m_obvs->end();it++)
    {
        (*it)->Update(this);
    }
}

////目标子类
SubSubject::SubSubject()
{
}

SubSubject::~SubSubject()
{
}

3、定义观察者基类,声明Update为虚函数,同时定义观察者的子类A和观察者的子类B,并且子类A和子类B维护者指向Subject的指针。

//// Observer观察者的基类
class Observer
{
public:
    virtual ~Observer();
    virtual void Update(Subject* sub) = 0;
    
protected:
    Observer();
private:
};

//// Observer观察者的子类A
class SubObserverA:public Observer
{
public:
    virtual Subject* GetSubject();
    SubObserverA(Subject* sub);
    virtual ~SubObserverA();
    
    //传入 Subject 作为参数,这样可以让一个 View 属于多个的 Subject。
    void Update(Subject* sub);
private:
    Subject* _sub;
};

//// Observer观察者的子类B
class SubObserverB:public Observer
{
public:
    virtual Subject* GetSubject();
    SubObserverB(Subject* sub);
    virtual ~SubObserverB();
    
    //传入 Subject 作为参数,这样可以让一个 View 属于多个的 Subject。
    void Update(Subject* sub);
    
private:
    Subject* _sub;
};

4、实现观察者基类,观察者的子类A和观察者的子类B

//// Observer观察者的基类
Observer::Observer()
{
}

Observer::~Observer()
{
}

//// Observer观察者的子类A
SubObserverA::SubObserverA(Subject* sub)
{
    _sub = sub;
    _sub->Attach(this);
}

SubObserverA::~SubObserverA()
{
    _sub->Detach(this);
    if (_sub != 0)
        delete _sub;
}

Subject* SubObserverA::GetSubject()
{
    return _sub;
}

void SubObserverA::Update(Subject* sub)
{
    DEBUG_LOG("", "SubObserverA update");
    // 由于SubObserverA持有Subject的指针,因此这里可以去调用Subject的相关方法
}

//// Observer观察者的子类B
SubObserverB::SubObserverB(Subject* sub)
{
    _sub = sub;
    _sub->Attach(this);
}

SubObserverB::~SubObserverB()
{
    _sub->Detach(this);
    if (_sub != 0)
        delete _sub;
}

Subject* SubObserverB::GetSubject()
{
    return _sub;
}

void SubObserverB::Update(Subject* sub)
{
    DEBUG_LOG("", "SubObserverB update");
    // 由于SubObserverB持有Subject的指针,因此这里可以去调用Subject的相关方法
}

三、测试验证结果

代码实现完成之后,主程序输入如下所示的代码段,用于验证subject调用Notify的时候是否会通知到注册到它的列表中的观察者对象。

SubSubject * sub = new SubSubject();
Observer *obsA = new SubObserverA(sub);
Observer *obsB = new SubObserverB(sub);

sub->Notify();

运行结果如下所示,从结果看Subject调用Notify函数之后,就会调用注册到列表中的观察者对象的Update函数。

[2019-05-12 22:40:09.426  Update:110]   = SubObserverB update
[2019-05-12 22:40:09.426  Update:85]   = SubObserverA update

由于观察者对象维护者Subject的指针,因此,可以通过观察者对象来间接调用Notify函数

SubSubject * sub = new SubSubject();
Observer *obsA = new SubObserverA(sub);
Observer *obsB = new SubObserverB(sub);

SubObserverA *obsA2 =dynamic_cast<SubObserverA *>(obsA);
obsA2->GetSubject()->Notify();

四、MVC结构的理解

MVC架构是Observer的一个实例,Observer就是观察者, Observable对应目标Subject,M代表Model,V代表View,C代表Control, Control为控制模块,接收到用户的输入信息之后,传递给Model模块,该模块进行业务处理,最后展示出不同的视图View。

五、总结

观察者模式也称为发布订阅模式,目标就是就是通知的发布者,观察者是通知的订阅者。目标可以通知观察者,并且由于观察者持有目标对象的指针,因此,观察者也可以主动触发目标去通知其他的观察者对象。