C++设计模式之Adapter
一、功能
二、结构图
(1)class adapter
(2)object adapter
三、实现
和其他很多模式一样,学习设计模式的重点是学习每种模式的思想,而不应拘泥于它的某种具体结构图和实现。因为模式是灵活的,其实现可以是千变万化的,只是所谓万变不离其宗。在STL中大量运用了Adapter模式,象function
adapter、iterator
adpter,它们与这里说的adapter结构并不一样,但思想是一样的。具体的介绍可到侯捷网站上找相关文章,他讲得非常好。
四、示例代码
(1)class adapter
namespace DesignPattern_Adapter
{
// class Adaptee
class Adaptee
{
public:
void SpecialRequest() {}
} ;
// class Target
class Target
{
public:
virtual void Request() = 0 ;
} ;
// class Adapter
class Adapter : public Target, private Adaptee
{
public:
virtual void Request() { SpecialRequest() ; }
} ;
}
客户端代码:
{
using namespace DesignPattern_Adapter ;
Target *p = new Adapter() ;
p->Request() ; //实际上调用的是Adaptee::SpecialRequest()
}
(2)object adapter namespace
DesignPattern_Adapter
{
// class Adaptee
class Adaptee
{
public:
void SpecialRequest() {}
} ;
// class Target
class Target
{
public:
virtual void Request() = 0 ;
} ;
// class Adapter
class Adapter : public Target
{
public:
virtual void Request() { _adaptee.SpecialRequest() ; }
private:
Adaptee _adaptee ;
} ;
}
客户端代码:
{
using namespace DesignPattern_Adapter ;
Target *p = new Adapter() ;
p->Request() ; //实际上调用的是Adaptee::SpecialRequest()
}
六、实例
(1)STL中的Class Adapter
STL中的Adapter
Class包括:a.stack(对应的adaptee是deque)。b.queue(对应的adaptee是deque)。
c.priority_queue(对应的adaptee是vector)。 下面是从VC中的< stack
>拷出的stack的类定义:
template
class _Container = deque<_Ty> >
class stack
{ // LIFO queue implemented with a container
public:
typedef _Container container_type;
typedef typename _Container::value_type value_type;
typedef typename _Container::size_type size_type;
stack()
: c()
{ // construct with empty container
}
explicit stack(const _Container& _Cont)
: c(_Cont)
{ // construct by copying specified container
}
bool empty() const
{ // test if stack is empty
return (c.empty());
}
size_type size() const
{ // test length of stack
return (c.size());
}
value_type& top()
{ // return last element of mutable stack
return (c.back());
}
const value_type& top() const
{ // return last element of nonmutable stack
return (c.back());
}
void push(const value_type& _Val)
{ // insert element at end
c.push_back(_Val);
}
void pop()
{ // erase last element
c.pop_back();
}
bool _Eq(const stack<_Ty, _Container>& _Right)
const
{ // test for stack equality
return (c == _Right.c);
}
bool _Lt(const stack<_Ty, _Container>& _Right)
const
{ // test if this < _Right for stacks
return (c < _Right.c);
}
protected:
_Container c; // the underlying container
};
关键之处在于_Container c,stack所有的操作都转交给c去处理了。(这实际上就是前面所说的"object
adapter",注意STL中的class adapter与上面所说的class adapter概念不完全一致)
stack的使用方法很简单,如下:
{
int ia[] = { 1,3,2,4 };
deque id(ia, ia+4);
stack is(id);
}
(2)近日看了一篇文章“Generic< Programming
>:简化异常安全代码”,原文出自http://www.cuj.com/experts/1812/alexandr.htm?topic=
experts, 中文译文出自"C++
View第5期"。文章绝对一流,作者给出的代码中也使用了Adaptor模式,也有一定代表性。我将其问题一般化,概括出以下示例:
问题:假设有几个已有类,他们有某些共同的行为,但它们彼此间是独立的(没有共同的基类)。如:
class T1
{
public:
void Proc() {}
} ;
class T2
{
public:
void Proc() {}
} ;
// ...
如何以统一的方式去调用这些行为呢?
解决方法1:很自然的会想到用模板,如:
template
void Test(T
t)
{
t.Proc()
;
}
的确不错,但这只适用于简单的情况,有时情况是很复杂的,比如我们无法把类型放到模板参数中!
解决方法2:困难来自于这些类没有共同的基类,所以我们就创造一个基类,然后再Adapt。
// class
IAdaptor,抽象基类
class
IAdaptor
{
public:
virtual void
Proc() = 0 ;
}
;
// class
Adaptor
template
class
Adaptor : public IAdaptor, private T //实现继承
{
public:
virtual void
Proc() { T::Proc() ; }
}
;
//
以统一方式调用函数Proc,而不关心是T1、T2或其他什么类
void
Test(const std::auto_ptr& sp)
{
sp->Proc()
;
}
客户端代码:
Test(std::auto_ptr(new
Adaptor)) ;
Test(std::auto_ptr(new
Adaptor)) ;
上例很简单,用方法一中的模板函数就可以很好地解决了。下面是一个略微复杂一点的例子,根据参数类型来创建适当的对象:
class T1
{
public:
T1(int) { }
void Proc() { }
} ;
class T2
{
public:
T2(char) { }
void Proc() { }
} ;
// class IAdaptor,抽象基类
class IAdaptor
{
public:
virtual void Proc() = 0 ;
} ;
// class Adaptor
template
class Adaptor : public IAdaptor, private T //实现继承
{
public:
Adaptor(int n) : T(n) {}
Adaptor(char c) : T(c) {}
virtual void Proc() { T::Proc() ; }
} ;
class Test
{
public:
Test(int n) : sp(new Adaptor(n)) {}
Test(char c) : sp(new Adaptor(c)) {}
void Proc() { sp->Proc() ; }
private:
std::auto_ptr sp ;
} ;
客户端代码:
Test t1(10) ;
t1.Proc() ;
Test t2('c') ;
t2.Proc() ;
上面是示例而非实例,你也许更愿意看看它实际的运用。去下载作者所写的代码,好好欣赏一下吧。
C++设计模式之Abstract
Factory
一、功能
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
二、结构图
类厂最基本的结构示意图如下:
在实际应用中,类厂模式可以扩充到很复杂的情况,如下图所示:
三、优缺点
优点:(1)封装创建过程。客户不用知道类厂是如何创建类实例的,类厂封闭了所有创建的细节。这样可选择不同的创建方法,增加了灵活性。
(2)将客户与具体类隔离,提高了各自的可重用性。
缺点:Factory类层次与具体类层次通常是平行的(即一一对应的)。增加一个具体类,一般也要相应地增加一个factory类,增加了系统复杂度。
四、实现
(1)Abstract Factory类中通常是一组Factory Method的集合。个人认为与Factory
Method模式没有本质区别。
(2)通常可以把工厂作为单件。
五、示例代码
namespace
{
class
{};
class
:
class
:
class
{};
class
:
class
:
class
{
public:
virtual
CreateProductA() = 0 ;//
创建ProductA
virtual
CreateProductB() = 0 ;//
创建ProductB
} ;
class
:
{
public:
virtual
CreateProductA() {
virtual
CreateProductB() {
static
{
;
; }
protected:
ConcreteFactory1() {}
private:
ConcreteFactory1(const
ConcreteFactory1&
} ;
class
:
{
public:
virtual
CreateProductA() {
virtual
CreateProductB() {
static
{
;
; }
protected:
ConcreteFactory2() {}
private:
ConcreteFactory2(const
ConcreteFactory2&
} ;
}
客户端代码:
{
using
;
// 第一种创建方法
AbstractFactory *pFactory = ConcreteFactory1::Instance() ;
AbstractProductA *pProductA = pFactory->CreateProductA()
;
AbstractProductB *pProductB = pFactory->CreateProductB()
;
// 第二种创建方法
pFactory = ConcreteFactory2::Instance() ;
pProductA = pFactory->CreateProductA() ;
pProductB = pFactory->CreateProductB() ;
}
六、实例
最早知道类厂的概念是在COM中,但当时也没想到这是如此重要的一种模式,在许多其他模式中都可以用到类厂模式。
COM中不能直接创建组件,这也是由COM的一个特性决定的:即客户不知道要创建的组件的类名。
C++设计模式之Singleton
一、功能
保证一个类仅有一个实例。
二、结构图
三、优缺点
Singleton模式是做为"全局变量"的替代品出现的。所以它具有全局变量的特点:全局可见、贯穿应用程序的整个生命期,它也具有全局变量不具备的性质:同类型的对象实例只可能有一个。
四、实现
教科书上的Singleton定义如下:
class Singleton
{
public:
static Singleton* Instance() ;
protected:
Singleton() {}
private:
static Singleton *_instance ;
Singleton(const Singleton&) ;
Singleton& operator=(const Singleton&) ;
} ;
Singleton* Singleton::_instance = NULL ;
Singleton* Singleton::Instance()
{
(_instance == NULL) ? _instance = new Singleton() : 0 ; //lazy
initialization
return _instance ;
}
(1)因为返回的是指针,为防止用户调用delete函数,可把static Singleton
*_instance;改为在Instance()中定义static Singleton
_instance。这样显然更安全,同时也具有lazy initialization的特性(即第一次访问时才创建)。
(2)假设需要从Singleton派生子类,而子类也需要有同样的性质,既只能创建一个实例。我觉得,这很难办。根本原因在于Instance()函数不是虚函数,不具有多态的性质。一种常用方法是把
Instance()函数移到子类中,这时就只能用static Singleton *_instance,而不能用static
Singleton
_instance了,除非把_instance也要移到子类,无论怎么做都不优雅。另一种方法是用模板。具体用什么方法,只能根据实际情况权衡。
五、示例代码
(1)没子类的情况
namespace
DesignPattern_Singleton
{
class Singleton
{
public:
static Singleton* Instance() { static Singleton _instance ; return
&_instance ; }
protected:
Singleton() {}
private:
Singleton(const Singleton&) ;
Singleton& operator=(const Singleton&) ;
} ;
}
客户端代码:
{
using namespace DesignPattern_Singleton ;
Singleton *p = Singleton::Instance() ;
......
}
(2)有子类的情况
方法一:
namespace DesignPattern_Singleton
{
// class Singleton
class Singleton
{
protected:
Singleton() {}
static Singleton *_instance ;
private:
Singleton(const Singleton&) ;
Singleton& operator=(const Singleton&) ;
} ;
Singleton* Singleton::_instance = NULL
;
// class ConcreteSingleton
class ConcreteSingleton : public Singleton
{
public:
static Singleton* Instance() ;
protected:
ConcreteSingleton() {}
} ;
Singleton* ConcreteSingleton::Instance()
{
(_instance == NULL) ? _instance = new ConcreteSingleton() : 0
;
return _instance ;
}
}
客户端代码:
{
using namespace DesignPattern_Singleton ;
Singleton *p = ConcreteSingleton::Instance() ;
}
方法二:
namespace DesignPattern_Singleton
{
// class Singleton
class Singleton
{
protected:
Singleton() {}
private:
Singleton(const Singleton&) ;
Singleton& operator=(const Singleton&) ;
} ;
// class ConcreteSingleton
class ConcreteSingleton : public Singleton
{
public:
static Singleton* Instance() { static ConcreteSingleton _instance ;
return &_instance ; }
protected:
ConcreteSingleton() {}
} ;
}
客户端代码:
{
using namespace DesignPattern_Singleton ;
Singleton *p = ConcreteSingleton::Instance() ;
}
方法三:
namespace DesignPattern_Singleton
{
template < class T >
class Singleton
{
public:
static T* Instance() { static T _instance ; return &_instance ;
}
protected:
Singleton() {}
private:
Singleton(const Singleton &) ;
Singleton& operator=(const Singleton&) ;
} ;
class ConcreteSingleton : public Singleton< ConcreteSingleton
> {} ;
}
客户端代码
{
using namespace DesignPattern_Singleton ;
ConcreteSingleton *p = ConcreteSingleton::Instance() ;
}
C++模式开发之Bridge
一、功能
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
二、结构图
三、示例代码
namespace
DesignPattern_Bridge
{
// class
Implementor
class
Implementor
{
public:
virtual void
OperationImp() = 0 ;
}
;
// class
ConcreteImplementorA
class
ConcreteImplementorA : public Implementor
{
public:
virtual void
OperationImp() {}
}
;
// class
ConcreteImplementorB
class
ConcreteImplementorB : public Implementor
{
public:
virtual void
OperationImp() {}
}
;
// class
Abstraction
class
Abstraction
{
public:
void
Operation(Implementor* imp) { assert(imp) ; imp->OperationImp()
; }
}
;
}
客户端代码:
{
using
namespace DesignPattern_Bridge ;
Abstraction
obj ;
Implementor
*impa = new ConcreteImplementorA() ;
Implementor
*impb = new ConcreteImplementorB() ;
obj.Operation(impa)
; //第一种实现方法
obj.Operation(impb)
; //第二种实现方法
}
四、实例
(1)创建可以在X Window System和IBM的Presentation
Manager系统中都可以使用的窗口。(书上的例子)
Bridge的魅力在于抽象和实现之间是松散的关系,它们之间可以进行随意组合。如上图中,就有IconWindow+
XWindowImp、TransientWindow+XWindowImp、IconWindow+PMWindowImp、
TransientWindow+PMWindowImp四种组合。
C++模式设计之Builder
一、功能
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
二、结构图
各类之间的交互关系如下图所示:
三、示例代码
namespace DesignPattern_Builder
{
class Product1 { } ;
class Product2 { } ;
// class Builder
class Builder //抽象基类
{
public:
virtual void BuilderPartA() {} //提供缺省实现
virtual void BuilderPartB() {}
virtual void BuilderPartC() {}
protected:
Builder() {}
} ;
// class ConcreteBuilder1
class ConcreteBuilder1 : public Builder //创建Product1
{
public:
ConcreteBuilder1() : _product(NULL) {}
virtual void BuilderPartA() { }
virtual void BuilderPartB() { }
virtual void BuilderPartC() { }
virtual Product1* GetProduct1() { return _product ; }
//返回创建的Product1对象
private:
Product1 *_product ;
} ;
// class ConcreteBuilder2
class ConcreteBuilder2 : public Builder //创建Product2
{
public:
ConcreteBuilder2() : _product(NULL) {}
virtual void BuilderPartA() { }
virtual void BuilderPartB() { }
virtual void BuilderPartC() { }
virtual Product2* GetProduct2() { return _product ; }
//返回创建的Product2对象
private:
Product2 *_product ;
} ;
// class Director
class Director
{
public:
//创建对象(Director并不知道具体创建出来的对象是什么样的,只有调用该函数的client知道)
void Construct(Builder *builder)
{
builder->BuilderPartA() ;
builder->BuilderPartB() ;
builder->BuilderPartC() ;
}
} ;
}
客户端代码:
{
using namespace DesignPattern_Builder ;
Director director ;
// 创建第一种对象
ConcreteBuilder1 *pBuilder1 = new ConcreteBuilder1() ;
director.Construct(pBuilder1) ;
Product1 *product1 = pBuilder1->GetProduct1()
;
// 创建第二种对象
ConcreteBuilder2 *pBuilder2 = new ConcreteBuilder2() ;
director.Construct(pBuilder2) ;
Product2 *product2 = pBuilder2->GetProduct2() ;
}
四、实例
(1)例子一。如下图所示:
上图的功能是是把一个RTF文件转换为多种正文格式。RTFReader进行语法分析,然后将所有的token串逐一转换。可见builder就是一步步地把各个部分组装为一个整体。它封闭了组装的方法,组装出来的对象也大相径庭。
C++设计模式之Prototype
一、功能
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
二、结构图
三、优缺点
优点:复制自身。客户不知道需要对象的实际类型,只需知道它的抽象基类即可。(即有继承树的情况)
缺点:必须先有一个对象实例(即原型)才能clone。
四、示例代码
namespace
DesignPattern_Prototype
{
// class
Prototype
class
Prototype //抽象基类
{
public:
virtual
Prototype* Clone() = 0 ;
}
;
// class
ConcretePrototype1
class
ConcretePrototype1 : public Prototype
{
public:
virtual
Prototype* Clone()
{
ConcretePrototype1
*p = new ConcretePrototype1() ;
*p = *this ;
//复制对象
return p
;
}
}
;
// class
ConcretePrototype2
class
ConcretePrototype2 : public Prototype
{
public:
virtual
Prototype* Clone()
{
ConcretePrototype2
*p = new ConcretePrototype2() ;
*p = *this ;
//复制对象
return p
;
}
}
;
}
客户端代码:
{
using
namespace DesignPattern_Prototype
;
ConcretePrototype1
*obj1 = new ConcretePrototype1() ;//原型对象1
ConcretePrototype2
*obj2 = new ConcretePrototype2()
;//原型对象2
Prototype
*newobj1 = obj1->Clone() ;//克隆对象1
Prototype
*newobj2 = obj2->Clone()
;//克隆对象2
//使用复制出的对象newobj1和newobj2
}
五、实例
在一个图形编辑器中,每一个图形元素,如线、圆、文字等都应该支持拷贝操作,即点中图形,按下Ctrl+C,再按下Ctrl+V后就会复制一个新的图形。显然这是一种clone操作。所以在每个从Graphic派生出的图形子类都应运用Prototype模式,加上Clone操作。
C++设计模式之Factory
Method
一、功能
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
二、结构图
三、实现
(1)在某些情况下,比如仅仅为了创建适当的Product对象而派生新的Creator子类,并且创建不同Product的方法一致时,可以考虑用模板代替继承。如:
class
Creator
{
public:
virtual
Product* CreateProduct() = 0 ;
};
template
< class ConcreteProduct >
class
ConcreteCreator: public Creator
{
public:
virtual
Product* CreateProduct() { return new ConcreteProduct() ;
}
};
模板与继承的本质区别之一是:模板:行为不依赖于类型。继承:行为依赖于类型。(Effective C++ Item 41)
事实上,在很多模式中都存在着可以用模板代替继承的情况,其根本原因就在于子类的行为是一致的。
四、示例代码
namespace
DesignPattern_FactoryMethod
{
class
Product { } ;
class
ConcreteProduct : public Product { }
;
// class
Creator
class
Creator
{
public:
virtual
Product* CreateProduct() = 0 ;
void
Operate() ;
}
;
void
Creator::Operate()
{
//
...
Product *p =
CreateProduct() ;
//
...
}
// class
ConcreteCreator
class
ConcreteCreator : public Creator
{
public:
virtual
Product* CreateProduct() { return new ConcreteProduct() ;
}
}
;
}
客户端代码:
{
using
namespace DesignPattern_FactoryMethod ;
ConcreteCreator
p ;
p.Operate()
;
}
这里的CreateProduct其实也是一个Template Method。
五、实例
Factory Method的运用太广泛了,它经常运用在其它模式中,其实例举不胜数。
(1)
MFC中的CDocument类就包含了类似于上图CApplication中的三个函数。这里的CreateDocument就是一个factory
method,因为它负责创建一个文档对象。
(2)
当一个类将它的一些职责委托给一个独立的类时,就产生了平行类层次。上图中Figure和Manipulator就是平行类层次,Figure代表一些图形元素,如线、文字等,Manipulator表示作用于这些图形元素的操作,如拖拉、移动、选中等。如果这些操作所需要的状态信息并不需要保存在Figure中,那么把Figure和Manipulator分成两个类层次是个好主意。这里的CreateManipulator就是一个factory
method。
C++设计模式之Composite
一、功能
表示“部分-整体”关系,并使用户以一致的方式使用单个对象和组合对象。
二、结构图
上图中,也可以做些扩展,根据需要可以将Leaf和Composite做为抽象基类,从中派生出子类来。
三、优缺点
优点:对于Composite模式,也许人们一开始的注意力会集中在它是如何实现组合对象的。但Composite最重要之处在于用户并不关心是组合对象还是单个对象,用户将以统一的方式进行处理,所以基类应是从单个对象和组合对象中提出的公共接口。
缺点:Composite最大的问题在于不容易限制组合中的组件。
四、实现
有时需要限制组合中的组件,即希望一个Composite只能有某些特定的Leaf。这个问题我是用多继承和动态类型转换来解决的。假如组合对象Composite1只能包含单个对象ConcreteLeaf1,Composite2可以包含单个对象
ConcreteLeaf1和ConcreteLeaf2。如下图所示:
上图中的类层次比较多,使用了AbstractLeaf1和AbstractLeaf2,但没使用
AbstractComposite1和AbstractComposite2,这个并不重要,也可以把AbstractLeaf1和
AbstractLeaf2去掉,这个并不重要,可以根据具体情况决定要不要。
简单的代码实现如下:
namespace
DesignPattern_Composite
{
class
Component
{
public:
virtual void
operation() = 0 ;
virtual void
Add(Component*) {}
}
;
class
AbstractComponent1 : virtual public Component {}
;
class
AbstractLeaf1 : virtual public AbstractComponent1 {}
;
class
Composite1 : public AbstractComponent1
{
public:
virtual void
operation() { }
virtual void
Add(Component*) ;
}
;
void
Composite1::Add(Component *p)
{
AbstractComponent1
*pc1 = dynamic_cast<ABSTRACTCOMPONENT1*>(p) ;
if (pc1 ==
NULL) return ;
// do add
operation
}
class
AbstractComponent2 : virtual public Component {} ;
class
AbstractLeaf2 : virtual public AbstractComponent2 {}
;
class
Composite2 : public AbstractComponent2
{
public:
virtual void
operation() { }
virtual void
Add(Component*) ;
}
;
void
Composite2::Add(Component *p)
{
AbstractComponent2
*pc2 = dynamic_cast<ABSTRACTCOMPONENT2*>(p) ;
if (pc2 ==
NULL) return ;
// do add
operation
}
class
ConcreteLeaf1 : public AbstractLeaf1
{
public:
virtual void
operation() { }
}
;
class
ConcreteLeaf2 : public AbstractLeaf1, public
AbstractLeaf2
{
public:
virtual void
operation() { }
}
;
}
客户端代码:
{
using
namespace DesignPattern_Composite
;
Component
*pc1 = new ConcreteLeaf1() ;
Component
*pc2 = new ConcreteLeaf2() ;
Component
*pc3 = new Composite1() ;
Component
*pc4 = new Composite2() ;
pc3->Add(pc1)
; // ok
pc3->Add(pc2)
; // ok
pc3->Add(pc3)
; // ok
pc3->Add(pc4)
; // fail
pc4->Add(pc1)
; // fail
pc4->Add(pc2)
; // ok
pc4->Add(pc3)
; // fail
pc4->Add(pc4)
; // ok
}
有两点需要注意,一是因为用了多继承,所以需要使用virtual
inheritance。二是要用dynamic_cast来判断是否允许组合该组件。
五、示例代码
namespace
DesignPattern_Composite
{
// class
Component
class
Component
{
public:
virtual void
Operation() = 0 ;
virtual void
Add(Component*) {}
}
;
// class
Leaf
class Leaf :
public Component
{
public:
virtual void
Operation() {}
}
;
// class
Composite
class
Composite : public Component
{
public:
virtual void
Add(Component *p) { _list.push_back(p) ; }
virtual void
Operation()
{
vector<
Component* >::const_iterator it ;
for (it =
_list.begin(); it != _list.end(); it++)
(*it)->Operation()
;
}
private:
vector<
Component* > _list ;
}
;
}
六、实例
(1)JUnit中就用的是Composite模式。
原文链接: https://www.cnblogs.com/mr-totoro/archive/2012/12/05/5785717.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/71527
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!