http://www.cnblogs.com/hegezhou_hot/archive/2010/12/09/1901040.html
/////////////////-------------------设计模式
1>高内聚、低耦合
(1)耦合性(Coupling)
也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、
调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关
系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差。软件设计中
通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。
耦合性也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,
其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传
递的信息。
耦合性分类(低――高): 无直接耦合;数据耦合;标记耦合;控制耦合;公共耦合;内容耦合;
1 无直接耦合:
2 数据耦合: 指两个模块之间有调用关系,传递的是简单的数据值,相当于高级语言的值传递;
3 标记耦合: 指两个模块之间传递的是数据结构,如高级语言中的数组名、记录名、文件名等这些名字即
标记,其实传递的是这个数据结构的地址;
4 控制耦合: 指一个模块调用另一个模块时,传递的是控制变量(如开关、标志等),被调模块通过该控
制变量的值有选择地执行块内某一功能;
5 公共耦合: 指通过一个公共数据环境相互作用的那些模块间的耦合。公共耦合的复杂程序随耦合模块的
个数增加而增加。
6 内容耦合: 这是最高程度的耦合,也是最差的耦合。当一个模块直接使用另一个模块的内部数据,或通
过非正常入口而转入另一个模块内部。
(2)高内聚
内聚性又称块内联系。指模块的功能强度的度量,即一个模块内部各个元素彼此结合的紧密程度的度
量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则它的内聚性就越高。
内聚性匪类(低――高): 偶然内聚;逻辑内聚;时间内聚;通信内聚;顺序内聚;功能内聚;
1 偶然内聚: 指一个模块内的各处理元素之间没有任何联系。
2 逻辑内聚: 指模块内执行几个逻辑上相似的功能,通过参数确定该模块完成哪一个功能。
3 时间内聚: 把需要同时执行的动作组合在一起形成的模块为时间内聚模块。
4 通信内聚: 指模块内所有处理元素都在同一个数据结构上操作(有时称之为信息内聚),或者指
各处理使用相同的输入数据或者产生相同的输出数据。
5 顺序内聚: 指一个模块中各个处理元素都密切相关于同一功能且必须顺序执行,前一功能元素输出就是
下一功能元素的输入。
6 功能内聚: 这是最强的内聚,指模块内所有元素共同完成一个功能,缺一不可。与其他模块的耦合是最弱的
1)面向对象系统的分析和设计实际上追求的就是两点,一是高内聚(Cohesion),而是低耦合(Coupling)。
2)一个模式有四个基本要素:
模式名称(pattern name):
3)MVC:类的模型/视图/控制器(Model/View/Controller)三元组(MVC)被用来构建用户界面。
MVC包括三类对象:模型Model是应用对象,视图View是它在屏幕上的表示,控制器Controller定义用户界面对用户输入
的响应方式。
4)我们根据两条准则(表1-1)对模式进行分类。
1、第一是目的准则,即模式是用来完成什么工
作的。模式依据其目的可分为创建型(Creational)、结构型(Structural)、或行为型
(Behavioral)三种。创建型模式与对象的创建有关;结构型模式处理类或对象的组合;行为型
模式对类或对象怎样交互和怎样分配职责进行描述。
2、第二是范围准则,指定模式主要是用于类还是用于对象。类模式处理类和子类之间的关
系,这些关系通过继承建立,是静态的,在编译时刻便确定下来了。对象模式处理对象间的
关系,这些关系在运行时刻是可以变化的,更具动态性。从某种意义上来说,几乎所有模式
都使用继承机制,所以“类模式”只指那些集中于处理类间关系的模式,而大部分模式都属
于对象模式的范畴。
创建型类模式将对象的部分创建工作延迟到子类,而创建型对象模式则将它延迟到另一
个对象中。结构型类模式使用继承机制来组合类,而结构型对象模式则描述了对象的组装方
式。行为型类模式使用继承描述算法和控制流,而行为型对象模式则描述一组对象怎样协作
完成单个对象所无法完成的任务。
5)继承和组合的比较
面向对象系统中功能复用的两种最常用技术是类继承和对象组合(object composition)。正
如我们已解释过的,类继承允许你根据其他类的实现来定义一个类的实现。这种通过生成子
类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方
式中,父类的内部细节对子类可见。
对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象
来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用
(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。
继承和组合各有优缺点。类继承是在编译时刻静态定义的,且可直接使用,因为程序设
计语言直接支持类继承。类继承可以较方便地改变被复用的实现。当一个子类重定义一些而
不是全部操作时,它也能影响它所继承的操作,只要在这些操作中调用了被重定义的操作。
但是类继承也有一些不足之处。首先,因为继承在编译时刻就定义了,所以无法在运行
时刻改变从父类继承的实现。更糟的是,父类通常至少定义了部分子类的具体表示。因为继
承对子类揭示了其父类的实现细节,所以继承常被认为“破坏了封装性” [ S n y 8 6 ]。子类中的
实现与它的父类有如此紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生
变化。
当你需要复用子类时,实现上的依赖性就会产生一些问题。如果继承下来的实现不适合
解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最
终限制了复用性。一个可用的解决方法就是只继承抽象类,因为抽象类通常提供较少的实现。
对象组合是通过获得对其他对象的引用而在运行时刻动态定义的。组合要求对象遵守彼
此的接口约定,进而要求更仔细地定义接口,而这些接口并不妨碍你将一个对象和其他对象
一起使用。这还会产生良好的结果:因为对象只能通过接口访问,所以我们并不破坏封装
性;只要类型一致,运行时刻还可以用一个对象来替代另一个对象;更进一步,因为对象的
实现是基于接口写的,所以实现上存在较少的依赖关系。
对象组合对系统设计还有另一个作用,即优先使用对象组合有助于你保持每个类被封装,
并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控
制的庞然大物。另一方面,基于对象组合的设计会有更多的对象(而有较少的类),且系统的行
为将依赖于对象间的关系而不是被定义在某个类中。
这导出了我们的面向对象设计的第二个原则:
优先使用对象组合,而不是类继承。
6)怎样使用设计模式:
1) 大致浏览一遍模式特别注意其适用性部分和效果部分,确定它适合你的问题。
2 ) 回头研究结构部分、参与者部分和协作部分确保你理解这个模式的类和对象以及它
们是怎样关联的。
3 ) 看代码示例部分,看看这个模式代码形式的具体例子研究代码将有助于你实现模式。
4 ) 选择模式参与者的名字,使它们在应用上下文中有意义设计模式参与者的名字通常过
于抽象而不会直接出现在应用中。然而,将参与者的名字和应用中出现的名字合并起来是
很有用的。这会帮助你在实现中更显式的体现出模式来。
5) 定义类声明它们的接口,建立它们的继承关系,定义代表数据和对象引用的实例变
量。识别模式会影响到的你的应用中存在的类,做出相应的修改。
6) 定义模式中专用于应用的操作名称这里再一次体现出,名字一般依赖于应用。使用
与每一个操作相关联的责任和协作作为指导。还有,你的名字约定要一致。例如,可以使用
“C r e a t e-”前缀统一标记F a c t o r y方法。
7) 实现执行模式中责任和协作的操作实现部分提供线索指导你进行实现。代码示例部
分的例子也能提供帮助。
7)设计类的原则
1.单一职责原则:
就一个类而言,应该仅有一个引起它变化的原因。如果一个类承担的职责过多,就等于把这些职责耦合在一起,一 个职
责的变化可能会削弱或者抑制这个类完成其它职责能力。这种耦合会导制脆弱的设计,当变化发生时,设计会遭受到意
想不到的破坏。
2.开放――封闭原则:
软件实体可以扩展,但是不可修改。即对于扩展是开放的,对于修改是封闭的。面对需求,对程序的改动是通过增加代
码来完成的,而不是改动现有的代码。
当变化发生时,我们就创建抽象来隔离以后发生同类的变化。
开放――封闭原则是面向对象的核心所在。开 发人员应该对程序中呈现出频繁变化的那部分做出抽象,拒绝对任何部分都
刻意抽象及不成熟的抽象。
3.里氏代换原则:
一个软件实体如果使用的是一个父类的话,那么一定适用其子类。而且它察觉不出父类对象和子类对象的区别。也就是
说:在软件里面,把父类替换成子类,程序的行为没有变化。子类型必须能够替换掉它们的父类型。
4.依赖倒转原则:
抽象不应该依赖细节,细节应该依赖抽象。即针对接口编程,不要对实现编程。
高层模块不能依赖低层模块,两者都应依赖抽象。
依赖倒转原则是面向对象的标志,用哪种语言编写程序不重要,如果编写时考虑的是如何针对抽象编程而不是针对细节编
程,即程序的所有依赖关系都终止于抽象类或接口。那就是面向对象设计,反之那就是过程化设计。
//-----------------------------------------创建型模式-----------------------------------------
1)创建型模式抽象了实例化过程。它们帮助一个系统独立于如何创建、组合和表示它的那
些对象。一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委
托给另一个对象。
<1>----Factory(工厂模式Factory Method)
工厂模式属于创建型模式,大致可以分为三类,简单工厂模式、工厂方法模式、抽象工厂模式。
听上去差不多,都是工厂模式。
--:工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。
核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实
现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况
下引进新的产品。
--:主要用于创建对象。新添加类时,不会影响以前的系统代码。核心思想是用一个工厂来根据输入的
条件产生不同的类,然后根据不同类的 virtual 函数得到不同的结果。工厂类与基类为关联关系。
1)为了提高内聚(Cohesion)和松耦合(Coupling),我们经常会抽象出一些类的公共接口以形
成抽象基类或者接口。这样我们可以通过声明一个指向基类的指针来指向实际的子类实现,达到了
多态的目的。这里很容易出现的一个问题n多的子类继承自抽象基类,我们不得不在每次要用到子
类的地方就编写诸如new ×××;的代码。这里带来两个问题1)客户程序员必须知道实际子类的名称
(当系统复杂后,命名将是一个很不好处理的问题,为了处理可能的名字冲突,有的命名可能并不
是具有很好的可读性和可记忆性,就姑且不论不同程序员千奇百怪的个人偏好了。),2)程序的
扩展性和维护变得越来越困难。
2)还有一种情况就是在父类中并不知道具体要实例化哪一个具体的子类。这里的意思为:假设我
们在类A中要使用到类B,B是一个抽象父类,在A中并不知道具体要实例化那一个B的子类,但是在
类A的子类D中是可以知道的。在A中我们没有办法直接使用类似于new ×××的语句,因为根本就不知
道×××是什么。
以上两个问题也就引出了Factory模式的两个最重要的功能:
1)定义创建对象的接口,封装了对象的创建;
2)使得具体化类的工作延迟到了子类中。
我们通常使用Factory模式来解决上面给出的两个问题。在第一个问题中,我们经常就是声明一个
创建对象的接口,并封装了对象的创建过程。Factory这里类似于一个真正意义上的工厂(生产对象)
。在第二个问题中,我们需要提供一个对象创建对象的接口,并在子类中提供其具体实现(因为只有
在子类中可以决定到底实例化哪一个类)。
Factory模式不单是提供了创建对象的接口,其最重要的是延迟了子类的实例化(第二个问题).
////--------------
1、面向对象的系统经常面临着对象创建问题:
要创建的类实在是太多了。而Factory提供的创建对象的接口封装(第一个功能),以及其将类
的实例化推迟到子类(第二个功能)都部分地解决了实际问题。
2、Factory模式也带来至少以下两个问题:
1)如果为每一个具体的ConcreteProduct类的实例化提供一个函数体,那么我们可能不得不在系
统中添加了一个方法来处理这个新建的ConcreteProduct,这样Factory的接口永远就不肯能封闭
(Close)。当然我们可以通过创建一个Factory的子类来通过多态实现这一点,但是这也是以新
建一个类作为代价的。
2)在实现中我们可以通过参数化工厂方法,即给FactoryMethod()传递一个参数用以决定是创
建具体哪一个具体的Product(实际上笔者在VisualCMCS中也正是这样做的)。当然也可以通过模
板化避免1)中的子类创建子类,其方法就是将具体Product类作为模板参数,实现起来也很简单。
3、可以看出,Factory模式对于对象的创建给予开发人员提供了很好的实现策略,但是Factory模式仅
仅局限于一类类(就是说Product是一类,有一个共同的基类),如果我们要为不同类的类提供一
个对象创建的接口,那就要用AbstractFactory了。
1)GOOD:适用于不同情况创建不同的类时
2)BUG:客户端必须要知道基类和工厂类,耦合性差
1.3 实用性
在同时满足下列情况下可以使用Factory Method模式:
1.当一个类不知道他所必须创建的类的对象的时候;
2.当一个类希望由它的子类来指定他所创建的对象的时候;
3.当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信
息局部化的时候.
(1)简单工厂类型----类创建型模式
enum CTYPE {COREA, COREB};
class SingleCore
{
public:
virtual void Show() = 0;
};
//单核A
class SingleCoreA: public SingleCore
{
public:
void Show() { cout<<"SingleCore A"<
//单核B
class SingleCoreB: public SingleCore
{
public:
void Show() { cout<<"SingleCore B"<
//唯一的工厂,可以生产两种型号的处理器核,在内部判断
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;
}
};
这样设计的主要缺点之前也提到过,就是要增加新的核类型时,就需要修改工厂类。这就违反了开
放封闭原则:软件实体(类、模块、函数)可以扩展,但是不可修改。于是,工厂方法模式出现了。
所谓工厂方法模式,是指定义一个用于创建对象的接口,让子类决定实例化哪一个类。
Factory Method使一个类的实例化延迟到其子类。
(2)工厂方法模式---Factory Method--对象创建型模式
class SingleCore
{
public:
virtual void Show() = 0;
};
//单核A
class SingleCoreA: public SingleCore
{
public:
void Show() { cout<<"SingleCore A"<
//单核B
class SingleCoreB: public SingleCore
{
public:
void Show() { cout<<"SingleCore B"<
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++实现中,就是要定义一个个的工厂
类。显然,相比简单工厂模式,工厂方法模式需要更多的类定义。
(3)抽象工厂模式--AbstactFactory---对象创建型模式
它的定义为提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
//单核
class SingleCore
{
public:
virtual void Show() = 0;
};
class SingleCoreA: public SingleCore
{
public:
void Show() { cout<<"Single Core A"<
class SingleCoreB :public SingleCore
{
public:
void Show() { cout<<"Single Core B"<
//多核
class MultiCore
{
public:
virtual void Show() = 0;
};
class MultiCoreA : public MultiCore
{
public:
void Show() { cout<<"Multi Core A"<
};
class MultiCoreB : public MultiCore
{
public:
void Show() { cout<<"Multi Core B"<
//工厂
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(); }
};
AbstractFactory模式和Factory模式的区别是初学(使用)设计模式时候的一个容易引起困惑的地
方。实际上,AbstractFactory模式是为创建一组(有多类)相关或依赖的对象提供创建接口,而
Factory模式正如我在相应的文档中分析的是为一类对象提供创建接口或延迟对象的创建到子类中
实现。并且可以看到,AbstractFactory模式通常都是使用Factory模式实现(ConcreteFactory1)。
抽象工厂和工厂方法的组要区别是工厂方法使用继承来创建对象,而抽象工厂是使用组合。
(4)Factory Method与Abstract Factory的区别:
Factory Method模式与Abstract Factory模式虽然同属于对象创建型模式,并且AbstractFactory
类通常用Factory Method模式实现,并且效果上都可用于连接平行的类层次(Factory Method不限于此)
,但是这两个模式在思想上有着本质的区别。
http://www.cnblogs.com/happyhippy/archive/2010/09/26/1836223.html
1.对象职责上:Abstract Factory中的Factory,只具有创建对象(一个产品系列)的唯一职责;而Factory Method中的Creator,具有实际的逻辑和意义。
2.扩展上:Abstract Factory侧重水平扩展,而Factory Method侧重垂直扩展。
3.使用上:Abstract Factory的思想是聚合,而Factory Method的思想是继承。
<2>--Singleton模式 (单例模式) //http://blog.csdn.net/hzyong_c/article/details/7747478
GOOD:保证一个类仅有一个实例,并提供一个访问它的全局访问点
Singleton不可以被实例化,因此我们将其构造函数声明为protected或者直接声明为private。
把一个类设计成自己管理的一个单独实例,同时避免其他类再自行生成实例(所以构造函数用protect或privite)
也提供全局的访问点。public函数
方法1:local-static对象,也就是函数内部的static对象
//实例
#include
#include
#include
using namespace std;
class Singelton
{
private:
Singelton(){}
static Singelton* singel;
public:
static Singelton* GetInstance()
{
if(singel == NULL)
{
singel = new Singelton();
}
return singel;
}
};
Singelton* Singelton::singel = NULL;//注意静态变量类外初始化
客户端:
int main()
{
Singelton* s1=Singelton::GetInstance();
Singelton* s2=Singelton::GetInstance();
if(s1 == s2)
cout<<"ok"<
cout<<"no"<
}
该方法是简陋的单例模式实现方式,缺点是:
该类只负责类对象的创建,没有负责销毁,还得程序员负责在合适的时机销毁,所以不推荐使用该方法。
方法2:实现方法二(智能指针方式)
// .h文件
class PtrSingleton
{
public:
~PtrSingleton(){} // 必须为public, 智能指针负责析构
static scoped_ptr
{
if(NULL == instance_.get()) // 线程不安全,这里需要同步
instance_.reset(new PtrSingleton);
return instance_;
}
// other members
protected:
PtrSingleton(){}
static scoped_ptr
};
// .cpp文件
scoped_ptr
该方法特点:
1)避免了手动析构.
2)值得注意的是实例化接口返回类型不是对象指针、值、引用,而是智能指针.
3)但在实际使用中要考虑到多线程环境下的使用,Instance接口是线程不安全的,需要同步一下,
本篇重点讲单例模式,代码样例中就不做线程同步了.
3 实现方法三(简单方式)
// .cpp文件
class Singleton
{
public:
~Singleton(){}
static Singleton& Instance(){return instance_;}
// testing member
protected:
Singleton(){}
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
private:
static Singleton instance_;
};
// .cpp文件
Singleton Singleton::instance_;
这种方法实现起来简单,用起来方便,安全。
对于单件模式, 因为内存模型的关系, 理论上是不能被继承的,但是可以利用c++模板技术实现单件模式的继承:
//实例---单例与工厂模式的结合
<3>Builder模式 (创建者模式/建造者模式)
GOOD:在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用
(通常是由很多其他的对象组合而成,我们要要复杂对象的创建过程和这个对象的表示(展示)
分离开来,这样做的好处就是通过一步步的进行复杂对象的构建,由于在每一步的构造过程中
可以引入参数,使得经过相同的步骤创建最后得到的对象的展示不一样。)。
1)建造者模式的定义将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的
表示(DP)。《大话设计模式》举了一个很好的例子——建造小人,一共需建造6个部分,头部、身体
、左右手、左右脚。与工厂模式不同,建造者模式是在导向者的控制下一步一步构造产品的。建造
小人就是在控制下一步步构造出来的。创建者模式可以能更精细的控制构建过程,从而能更精细的
控制所得产品的内部结构。
2)适用性:
当同时满足以下情况的时候可以使用Builder模式
a. 当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式;
b. 当构造过程必须允许构造的对象有不同的表示;
3) Builder模式的核心思想
将一个“复杂对象的构建算法”与它的“部件及组装方式”分离,使得构件算法和组装方式可以独立应对
变化;复用同样的构建算法可以创建不同的表示,不同的构建过程可以复用相同的部件组装方式。
class Builder
{
public:
virtual void BuildHead() {}
virtual void BuildBody() {}
virtual void BuildLeftArm(){}
virtual void BuildRightArm() {}
virtual void BuildLeftLeg() {}
virtual void BuildRightLeg() {}
};
//构造瘦人
class ThinBuilder : public Builder
{
public:
void BuildHead() { cout<<"build thin body"<
//构造胖人
class FatBuilder : public Builder
{
public:
void BuildHead() { cout<<"build fat body"<
//构造的指挥官
class Director
{
private:
Builder *m_pBuilder;
public:
Director(Builder *builder) { m_pBuilder = builder; }
void Create(){
m_pBuilder->BuildHead();
m_pBuilder->BuildBody();
m_pBuilder->BuildLeftArm();
m_pBuilder->BuildRightArm();
m_pBuilder->BuildLeftLeg();
m_pBuilder->BuildRightLeg();
}
};
int main()
{
FatBuilder fat;
Director director(&fat);
director.Create();
ThinBuilder thin;
Director director(&thin);
director.Create();
return 0;
}
Builder模式和AbstractFactory模式在功能上很相似,因为都是用来创建大的复杂的对象,它们
的区别是:Builder模式强调的是一步步创建对象,并通过相同的创建过程可以获得不同的结果对
象,一般来说Builder模式中对象不是直接返回的。而在AbstractFactory模式中对象是直接返回
的,AbstractFactory模式强调的是为创建多个相互依赖的对象提供一个同一的接口。
<4>Prototype模式 (原型模式)
GOOD:从一个对象再创建另外一个可定制的对象,而无需知道任何创建的细节。
并能提高创建的性能。说白了就 COPY 技术,把一个对象完整的COPY出一份。
Prototype模式提供了一个通过已存在对象进行新对象创建的接口(Clone),
Clone()实现和具体的实现语言相关,在C++中我们将通过拷贝构造函数实现之。
实例:
#include
#include
#include
using namespace std;
class Prototype //抽象基类
{
private:
string m_strName;
public:
Prototype(string strName){ m_strName = strName; }
Prototype() { m_strName = " "; }
void Show()
{
cout<
virtual Prototype* Clone() = 0 ;
} ;
// class ConcretePrototype1
class ConcretePrototype1 : public Prototype
{
public:
ConcretePrototype1(string strName) : Prototype(strName){}
ConcretePrototype1(){}
virtual Prototype* Clone()
{
ConcretePrototype1 *p = new ConcretePrototype1() ;
*p = *this ; //复制对象
return p ;
}
} ;
// class ConcretePrototype2
class ConcretePrototype2 : public Prototype
{
public:
ConcretePrototype2(string strName) : Prototype(strName){}
ConcretePrototype2(){}
virtual Prototype* Clone()
{
ConcretePrototype2 *p = new ConcretePrototype2() ;
*p = *this ; //复制对象
return p ;
}
} ;
//客户端
int main()
{
ConcretePrototype1* test = new ConcretePrototype1("小王");
ConcretePrototype2* test2 = (ConcretePrototype2*)test>Clone();
test>
Show();
test2>
Show();
return 0;
}
Prototype模式通过复制原型(Prototype)而获得新对象创建的功能,这里Prototype本身就是“对象工厂”
(因为能够生产对象),实际上Prototype模式和Builder模式、AbstractFactory模式都是通过一个类(对象实例)
来专门负责对象的创建工作(工厂对象),它们之间的区别是:Builder模式重在复杂对象的一步步创建
(并不直接返回对象),AbstractFactory模式重在产生多个相互依赖类的对象,而Prototype模式重在从
自身复制自己创建新类。
//-----------------------------------------结构型模式-----------------------------------------
<1>Bridge模式--(桥接模式)
GOOD:将抽象部分与实现部分分离,使它们可以独立变化。
目的:将抽象与实现解耦。
动机:
1)当一种抽象类型可能有多种实现方式时,一般情况我们可以考虑使用继承来解决抽象类型的多种实现,在抽象类型
中定义接口,而子类负责接口的具体实现。但这种做法缺乏灵活性,由于抽象类型和子类之间紧紧地绑定在一起,
使得这种关系在运行时不能再修改,这使得它难以修改、扩展和重用不利于抽象和实现解耦,而且这也违背OOP原
则:“优先使用对象聚集,而不是继承”。
2)在软件系统中,某些类型由于自身的逻辑,它具有两个或两个以上的维度变化,那么如何应对这种“多维度的变化”呢?
如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度呢?这就是即将要
介绍的桥接模式(Bridge)。
1)这里说的意思不是让抽象基类与具体类分离,而是现实系统可能有多角度分类,
每一种分类都有可能变化,那么把这种多角度分离出来让它们独立变化,减少它们之间的耦
合性,即如果继承不能实现“开放-封闭原则”的话,就应该考虑用桥接模式。
实例:
//操作系统
class OS
{
public:
virtual void InstallOS_Imp() {}
};
class WindowOS: public OS
{
public:
void InstallOS_Imp() { cout<<"安装Window操作系统"<
class LinuxOS: public OS
{
public:
void InstallOS_Imp() { cout<<"安装Linux操作系统"<
class UnixOS: public OS
{
public:
void InstallOS_Imp() { cout<<"安装Unix操作系统"<
//计算机
class Computer
{
public:
virtual void InstallOS(OS *os) {}
};
class DellComputer: public Computer
{
public:
void InstallOS(OS *os) { os->InstallOS_Imp(); }
};
class AppleComputer: public Computer
{
public:
void InstallOS(OS *os) { os->InstallOS_Imp(); }
};
class HPComputer: public Computer
{
public:
void InstallOS(OS *os) { os->InstallOS_Imp(); }
};
int main()
{
OS *os1 = new WindowOS();
OS *os2 = new LinuxOS();
Computer *computer1 = new AppleComputer();
computer1->InstallOS(os1);
computer1->InstallOS(os2);
}
2)Bridge是设计模式中比较复杂和难理解的模式之一,也是OO开发与设计中经常会用到的模式之一。
使用组合(委托)的方式将抽象和实现彻底地解耦,这样的好处是抽象和实现可以分别独立地变化,
系统的耦合性也得到了很好的降低。
3)GoF在说明Bridge模式时,在意图中指出Bridge模式“将抽象部分与它的实现部分分离,使得它们可以独
立地变化”。这句话很简单,但是也很复杂,连Bruce Eckel在他的大作《Thinking in Patterns》中说
“Bridge模式是GoF所讲述得最不好(Poorly-described)的模式”,个人觉得也正是如此。原因就在于
GoF的那句话中的“实现”该怎么去理解:“实现”特别是和“抽象”放在一起的时候我们“默认”的理解是“实
现”就是“抽象”的具体子类的实现,但是这里GoF所谓的“实现”的含义不是指抽象基类的具体子类对抽象
基类中虚函数(接口)的实现,是和继承结合在一起的。而这里的“实现”的含义指的是怎么去实现用户
的需求,并且指的是通过组合(委托)的方式实现的,因此这里的实现不是指的继承基类、实现基类接
口,而是指的是通过对象组合实现用户的需求。理解了这一点也就理解了Bridge模式,理解了Bridge模
式,你的设计就会更加Elegant了.
4)总结
桥接模式(Bridge)优点:
将实现予以解耦,让它和界面之间不再永久绑定。
抽象和实现可以独立扩展,不会影响到对方。
对于具体实现的修改,不会影响到客户端。
桥接模式(Bridge)缺点:
增加了设计复杂度。
抽象类的修改影响到子类。
桥接模式(Bridge)用途:
适用在需要跨多平台的图形和窗口系统。
当需要用不同的方式改变接口和实现时。
通过上述的介绍,我们了解为什么需要桥接模式(Bridge)和如何使用桥接模式(Bridge),由于对象的多维度的
变化,使得难以决定变化时,我们可以把对象和变化抽象出来。
如果我们的对象依赖于抽象,对于具体的实现并不关心,我们可以通过对象组合,组合出我们想要的对象。桥接模
式符合OCP(对于扩展开发,对于修改关闭)设计模式的原则。
<2>Adapter(适配器模式)
GOOD:双方都不适合修改的时候,可以考虑使用适配器模式
1)Adapter模式的两种类别:类模式和对象模式。
2)适配器模式的特点及使用场景
(1)适配器模式的特点:
适配器模式主要解决的问题就是我们要调用的接口类型,无法满足我们新系统的使用需求,这时候,我们需要将
旧系统的接口,通过适配器进行转配,达到支持新接口调用的目的。对于这样的要求,我们通过适配器就可以完
成,当然如果有多个接口需要转配,那么我们就需要为每一个接口提供一个适配器去完成转换的工作。当然具体
的调用过程,我们可以进行相应的封装。达到比较通用的方式去调用适配器,完成适配服务。
(2)适配器模式的场景:
1、我们在使用第三方的类库,或者说第三方的API的时候,我们通过适配器转换来满足现有系统的使用需求。
2、我们的旧系统与新系统进行集成的时候,我们发现旧系统的数据无法满足新系统的需求,那么这个时候,
我们可能需要适配器,完成调用需求。
3、我们在使用不同数据库之间进行数据同步。(我这里只是分析的是通过程序来说实现的时候的情况。还有
其他的很多种方式[数据库同步])。
实例1----类适配器
#include
using namespace std;
class Target
{
public:
virtual void Request()
{
cout<<"普通的请求"<
};
class Adaptee
{
public:
void SpecificalRequest()
{
cout<<"特殊请求"<
};
class Adapter :public Target/*public继承获得接口继承*/, private Adaptee/*private继续获取实现继承*/
{
public:
virtual void Request()
{
Adaptee::SpecificalRequest();
Target::Request();
}
Adapter()
{
}
~Adapter()
{
}
};
客户端:
int main()
{
Adapter * ada=new Adapter();
ada->Request();
delete ada;
return 0;
}
//对象适配器
#include
using namespace std;
class Target
{
public:
virtual void Request()
{
cout<<"普通的请求"<
};
class Adaptee
{
public:
void SpecificalRequest()
{
cout<<"特殊请求"<
};
class Adapter :public Target
{
private:
Adaptee* ada;
public:
virtual void Request()
{
ada->SpecificalRequest();
Target::Request();
}
Adapter()
{
ada=new Adaptee();
}
~Adapter()
{
delete ada;
}
};
客户端:
int main()
{
Adapter * ada=new Adapter();
ada->Request();
delete ada;
return 0;
}
3)讨论:
在Adapter模式的两种模式中,有一个很重要的概念就是接口继承和实现继承的区别和联系。
接口继承和实现继承是面向对象领域的两个重要的概念,接口继承指的是通过继承,子类获
得了父类的接口,而实现继承指的是通过继承子类获得了父类的实现(并不提供接口)。在
C++中的public继承既是接口继承又是实现继承,因为子类在继承了父类后既可以对外提供父
类中的接口操作,又可以获得父类的接口实现。当然我们可以通过一定的方式和技术模拟单独
的接口继承和实现继承,例如我们可以通过private继承获得实现继承的效果(private继承后
,父类中的接口都变为private,当然只能是实现继承了。),通过纯抽象基类模拟接口继承
的效果,但是在C++中pure virtual function也可以提供默认实现。
4)适配器模式使用总结:
<3>Decorator模式(装饰模式)
GOOD:当你向旧的类中添加新代码时,一般是为了添加核心职责或主要行为。而当需
要加入的仅仅是一些特定情况下才会执行的特定的功能时(简单点就是不是核心应用的功
能),就会增加类的复杂度。装饰模式就是把要添加的附加功能分别放在单独的类中,并让
这个类包含它要装饰的对象,当需要执行时,客户端就可以有选择地、按顺序地使用装饰功
能包装对象。
1)为什么;
在OO设计和开发过程,可能会经常遇到以下的情况:我们需要为一个已经定义好的类添加新的职责
(操作),通常的情况我们会给定义一个新类继承自定义好的类,这样会带来一个问题(将在本模
式的讨论中给出)。通过继承的方式解决这样的情况还带来了系统的复杂性,因为继承的深度会变
得很深。
而Decorator提供了一种给类增加职责的方法,不是通过继承实现的,而是通过组合。
实例:
//公共抽象类
class Phone
{
public:
Phone() {}
virtual ~Phone() {}
virtual void ShowDecorate() {}
};
//具体的手机类
class iPhone : public Phone
{
private:
string m_name; //手机名称
public:
iPhone(string name): m_name(name){}
~iPhone() {}
void ShowDecorate() { cout<
//具体的手机类
class NokiaPhone : public Phone
{
private:
string m_name;
public:
NokiaPhone(string name): m_name(name){}
~NokiaPhone() {}
void ShowDecorate() { cout<
//装饰类
class DecoratorPhone : public Phone
{
private:
Phone *m_phone; //要装饰的手机
public:
DecoratorPhone(Phone *phone): m_phone(phone) {}
virtual void ShowDecorate() { m_phone->ShowDecorate(); }
};
//具体的装饰类
class DecoratorPhoneA : public DecoratorPhone
{
public:
DecoratorPhoneA(Phone *phone) : DecoratorPhone(phone) {}
void ShowDecorate() { DecoratorPhone::ShowDecorate(); AddDecorate(); }
private:
void AddDecorate() { cout<<"增加挂件"<
//具体的装饰类
class DecoratorPhoneB : public DecoratorPhone
{
public:
DecoratorPhoneB(Phone *phone) : DecoratorPhone(phone) {}
void ShowDecorate() { DecoratorPhone::ShowDecorate(); AddDecorate(); }
private:
void AddDecorate() { cout<<"屏幕贴膜"<
int main()
{
Phone *iphone = new NokiaPhone("6300");
Phone *dpa = new DecoratorPhoneA(iphone); //装饰,增加挂件
Phone *dpb = new DecoratorPhoneB(dpa); //装饰,屏幕贴膜
dpb->ShowDecorate();
delete dpa;
delete dpb;
delete iphone;
return 0;
}
装饰模式和桥接模式的侧重点稍微不同。装饰模式更侧重的是某个类型的功能经常的动态的增加的情况。
2)讨论:
Decorator模式和Composite模式有相似的结构图,其区别在Composite模式已经详细讨论过了,请参看
相应文档。另外GoF在《设计模式》中也讨论到Decorator和Proxy模式有很大程度上的相似,初学设计
模式可能实在看不出这之间的一个联系和相似,并且它们在结构图上也很不相似。实际上,在本文档
2.2节模式选择中分析到,让Decorator直接拥有一个ConcreteComponent的引用(指针)也可以达到修
饰的功能,大家再把这种方式的结构图画出来,就和Proxy很相似了!
Decorator模式和Proxy模式的相似的地方在于它们都拥有一个指向其他对象的引用(指针),即通过组
合的方式来为对象提供更多操作(或者Decorator模式)间接性(Proxy模式)。但是他们的区别是,Proxy
模式会提供使用其作为代理的对象一样接口,使用代理类将其操作都委托给Proxy直接进行。这里可以简
单理解为组合和委托之间的微妙的区别了。
Decorator模式除了采用组合的方式取得了比采用继承方式更好的效果,Decorator模式还给设计带来一种
“即用即付”的方式来添加职责。在OO设计和分析经常有这样一种情况:为了多态,通过父类指针指向其具
体子类,但是这就带来另外一个问题,当具体子类要添加新的职责,就必须向其父类添加一个这个职责的
抽象接口,否则是通过父类指针是调用不到这个方法了。这样处于高层的父类就承载了太多的特征(方法),
并且继承自这个父类的所有子类都不可避免继承了父类的这些接口,但是可能这并不是这个具体子类所需要
的。而在Decorator模式提供了一种较好的解决方法,当需要添加一个操作的时候就可以通过Decorator模
式来解决,你可以一步步添加新的职责。
3)装饰模式的特点:
装饰模式:装饰模式主要是解决了,我们以动态的,透明的形式为某个类型添加一个新的职责,客户程序
可以不知道我们具体添加的功能职责,而客户程序只是根据对象提供的方法进行调用即可。而具体职责操
作留给装饰对象去完成。
4)装饰模式的使用场景:
1、当我们需要为某个现有的对象,动态的增加一个新的功能或职责时,可以考虑使用装饰模式。
2、适应于某个对象的职责经常发生变化或者经常需要动态的增加职责,避免因为这种为了适应这样的变化,
而增加继承子类扩展的方式,因为这种方式会造成子类膨胀的速度过快,难以控制。
5)装饰模式使用总结
通过上面的讲述,我们知道装饰模式,特别适合对某个类型的对象,动态的增加新的职责,应用程序就像使
用原来的对象一样使用对象新增的装饰后的功能,装饰模式就好像是穿了一层层的外壳,这样的方式避免了
通过继承来为类型添加新的职责的形式可取,通过继承的方式容易造成子类的膨胀,但是当装饰类太多的时
候,也是个难以维护的问题,至少是在装饰对象的时候,我们可能需要多步操作来完成对象的装饰,这时候
我们可以同上面提出的改进的方案,来完成自动配置装饰模式,记录操作模式的状态,可以进行有效的回滚
操作,以完成撤销操作。
<4>composite(组合模式)
GOOD:整体和部分可以被一致对待(如 WORD 中复制一个文字、一段文字、一篇文章都是一样的操作)
1)在开发中,我们经常可能要递归构建树状的组合结构,Composite模式则提供了很好的解决方案。
2)组合模式就是把一些现有的对象或者元素,经过组合后组成新的对象,新的对象提供内部方法,可以让我们很方
便的完成这些元素或者内部对象的访问和操作。我们也可以把组合对象理解成一个容器,容器提供各种访问其内
部对象或者元素的API,我们只需要使用这些方法就可以操作它了。
实例:
例二
#include
#include
#include
using namespace std;
class Company
{
protected:
string m_strName;
public:
Company(string strName)
{
m_strName = strName;
}
virtual void Add(Company* c)=0;
virtual void Display(int nDepth)=0;
virtual void LineOfDuty()=0;
};
class ConcreteCompany: public Company
{
private:
vector
public:
ConcreteCompany(string strName):Company(strName){}
virtual void Add(Company* c)
{
m_company.push_back(c);
}
virtual void Display(int nDepth)
{
string strtemp;
for(int i=0; i < nDepth; i++)
{
strtemp += "";
}
strtemp +=m_strName;
cout<
while (p!=m_company.end())
{
(*p)->Display(nDepth+2);
p++;
}
}
virtual void LineOfDuty()
{
vector
while (p!=m_company.end())
{
(*p)->LineOfDuty();
p++;
}
}
};
class HrDepartment : public Company
{
public:
HrDepartment(string strname) : Company(strname){}
virtual void Display(int nDepth)
{
string strtemp;
for(int i = 0; i < nDepth; i++)
{
strtemp += "";
}
strtemp += m_strName;
cout<
virtual void Add(Company* c)
{
cout<<"error"<
virtual void LineOfDuty()
{
cout<
};
//客户端:
int main()
{
ConcreteCompany *pCom = new ConcreteCompany("清华大学");
pCom->Add(new HrDepartment("清华大学人才部"));
ConcreteCompany *pCom1 = new ConcreteCompany("数学系");
pCom1->Add(new HrDepartment("数学系人才部"));
ConcreteCompany *pCom2 = new ConcreteCompany("物理系");
pCom2->Add(new HrDepartment("物理系人才部"));
pCom->Add(pCom1);
pCom->Add(pCom2);
pCom->Display(1);
pCom->LineofDuty();
return 0;
}
Composite模式通过和Decorator模式有着类似的结构图,但是Composite模式旨在构造类,而Decorator
模式重在不生成子类即可给对象添加职责。Decorator模式重在修饰,而Composite模式重在表示。
3)组合模式的特点及使用场景:
1、组合模式是将一系列对象组合成树形结构用来表示整体和部分之间的关系,组合模式的主要目的是达到,访问
组合对象和访问单个对象具有一致性。这里的组合对象比较特殊,本身他可以是由其他的对象组合而成,同时,
这个组合对象又可以是组成更复杂对象的一个部分。我们来举个例子来说明吧,可能更直观。
2、我们一般在如下场景中使用组合模式比较方便:
(1)我们有的时候想用户使用一个复杂对象像使用简单对象一样的方式去访问,并且用户同意使用对象内部的所有
的对象时,我们可以考虑使用该模式。这个怎么理解呢?我们使用复杂对象像使用简单对象一样的方式去访问
的话,那么我们可以使用组合对象,我们把这些简单的对象进行组合,用组合对象进行包装,并且提供相应的
操作组合对象内部的方法。
(2)如果有的时候,我们希望用户不了解自己使用的对象有多复杂,并且组合对象可以的内部可以自由的变化和组
合,但是不会影响到客户应用程序使用这个组合对象,如果项目中需要新增一个组合对象的时候,客户调用的程
序还是一样,不会因为这个组合对象发生变更而发生变化。组合模式在解决整体和部分之间的问题应用很广泛,也
可以降低系统的复杂度,因为我们可以把复杂的组件看作另一个组件的组成部分来处理。
4)组合模式使用总结:
通过上面的简单讲解,我们知道了,组合模式意图是通过整体与局部之间的关系,通过树形结构的形式进行组织复杂对
象,屏蔽对象内部的细节,对外展现统一的方式来操作对象,是我们处理更复杂对象的一个手段和方式。
<5>Flyweight(享元模式)
GOOD:运用共享技术有效地支持大量细粒度的对象(对于 C++来说就是共用一个内存块啦,对象指针指向同一个地方.
1)如果一个应用程序使用了大量的对象,而这些对象造成了很大的存储开销就应该考虑使用。还有就是对象的大多数状态可
以外部状态,如果删除对象的外部状态,那么可以用较少的共享对象取代多组对象,此时可以考虑使用享元。
2)如果一个应用程序使用了太多的对象,就会造成很大的存储开销。特别是对于大量轻量级(细粒度)的对象,比如在文档
编辑器的设计过程中,我们如果为没有字母创建一个对象的话,系统可能会因为大量的对象而造成存储开销的浪费。例如一
个字母“a”在文档中出现了100000次,而实际上我们可以让这一万个字母“a”共享一个对象,当然因为在不同的位置可能字母
“a”有不同的显示效果(例如字体和大小等设置不同),在这种情况我们可以为将对象的状态分为“外部状态”和“内部状态”,
将可以被共享(不会变化)的状态作为内部状态存储在对象中,而外部对象(例如上面提到的字体、大小等)我们可以在适
当的时候将外部对象最为参数传递给对象(例如在显示的时候,将字体、大小等信息传递给对象)。
3)享元模式的特点是,复用我们内存中已存在的对象,降低系统创建对象实例的性能消耗。
4)享元模式一般是给出本地内存资源节省的一个方案,并不适合互联网上的分布式应用的情况,不过享元模式对于排他性的要
求资源的控制,是个不错的选择的。
实例:
#pragma once
#include "stdafx.h"
#include
#include
#include
using namespace std;
#ifndef _FLYWEIGHT_H_
#define _FLYWEIGHT_H_
enum PieceColor{BLACK, WHITE};
//位置
struct PiecePos{
int x;
int y;
PiecePos(int a, int b):x(a),y(b){};
};
//棋子
class Piece
{
private:
PieceColor m_color;
public:
Piece(PieceColor color):m_color(color){}
~Piece(){}
virtual void Draw(){}
};
class BlackPiece: public Piece
{
public:
BlackPiece(PieceColor color): Piece(color) {}
~BlackPiece() {}
void Draw() { cout<<"绘制一颗黑棋\n"; }
};
class WhitePiece: public Piece
{
public:
WhitePiece(PieceColor color): Piece(color) {}
~WhitePiece() {}
void Draw() { cout<<"绘制一颗白棋\n";}
};
class PieceBoard
{
private:
vector
Piece *m_blackPiece; //黑棋棋子
Piece *m_whitePiece; //白棋棋子
string m_blackName;
string m_whiteName;
public:
PieceBoard(string black, string white): m_blackName(black), m_whiteName(white)
{
m_blackPiece = NULL;
m_whitePiece = NULL;
}
~PieceBoard() { delete m_blackPiece; delete m_whitePiece;}
void SetPiece(PieceColor color, PiecePos pos)
{
if(color == BLACK)
{
if(m_blackPiece == NULL) //只有一颗黑棋
m_blackPiece = new BlackPiece(color);
cout<< m_blackName <<"在位置(" << pos.x << "," << pos.y <<")";
m_blackPiece->Draw();
}
else
{
if(m_whitePiece == NULL)
m_whitePiece = new WhitePiece(color);
cout<
}
m_vecPos.push_back(pos);
}
};
#endif
//Flyweight Test
cout << "--------------Flyweight Begin--------------" <
pieceBoard.SetPiece(BLACK, PiecePos(4, 4));
pieceBoard.SetPiece(WHITE, PiecePos(4, 16));
pieceBoard.SetPiece(BLACK, PiecePos(16, 4));
pieceBoard.SetPiece(WHITE, PiecePos(16, 16));
cout << "--------------Flyweight End--------------" <
//总结
享元模式(Flyweight):运用共享的技术有效地支持大量细粒度的对象。
抽象享元角色(Flyweight):此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。那些
需要外部状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类
实行共享,因此并非所有的享元对象都是可以共享的。
具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内部状态的话,必须负责为内部状态提供
存储空间。享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享
元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。
复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解
成为多个本身是单纯享元对象的组合。复合享元角色又称做不可共享的享元对象。这个角色一般很少使用。
享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。
当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果
已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就
应当创建一个新的合适的享元对象。
客户端(Client)角色:本角色还需要自行存储所有享元对象的外部状态。
内部状态与外部状态:在享元对象内部并且不会随着环境改变而改变的共享部分,可以称之为享元对象的内部状态,反
之随着环境改变而改变的,不可共享的状态称之为外部状态。
<6>Facade(外观模式)
1)GOOD:为子系统的一组接口提供一个一致的界面。使用户使用起来更加方便。
2)外观模式应该是用的很多的一种模式,特别是当一个系统很复杂时,系统提供给客户的是一个简单的对外接口,而把里面
复杂的结构都封装了起来。客户只需使用这些简单接口就能使用这个系统,而不需要关注内部复杂的结构。
实例:
#pragma once
#include "stdafx.h"
#include
#include
#include
using namespace std;
#ifndef _FACADE_H_
#define _FACADE_H_
class SubSysOne
{
public:
void MethodOne()
{
cout<<"方法一"<
};
class SubSysTwo
{
public:
void MethodTwo()
{
cout<<"方法二"<
};
class SubSysThree
{
public:
void MethodThree()
{
cout<<"方法三"<
};
//外观类
class Facade
{
private:
SubSysOne* sub1;
SubSysTwo* sub2;
SubSysThree* sub3;
public:
Facade()
{
sub1 = new SubSysOne();
sub2 = new SubSysTwo();
sub3 = new SubSysThree();
}
~Facade()
{
delete sub1;
delete sub2;
delete sub3;
}
void FacadeMethod()
{
sub1->MethodOne();
sub2->MethodTwo();
sub3->MethodThree();
}
};
#endif
//客户端
int main()
{
Facade* test = new Facade()?
test->FacadeMethod()?
return 0?
}
这就是外观模式,它有几个特点:
(1)它对客户屏蔽子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便.
(2)它实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的.
(3)如果应用需要,它并不限制它们使用子系统类.
<7>Proxy(代理模式)
GOOD:远程代理,可以隐藏一个对象在不同地址空间的事实虚拟代理:通过代理来存放需要很长时间实例化的对象
安全代理:用来控制真实对象的访问权限
智能引用:当调用真实对象时,代理处理另外一些事
Proxy模式最大的好处就是实现了逻辑和实现的彻底解耦。
(1)为其他对象提供一种代理以控制对这个对象的访问。有四种常用的情况:
(1)远程代理,(2)虚代理,(3)保护代理,(4)智能引用。
1)创建开销大的对象时候,比如显示一幅大的图片,我们将这个创建的过程交给代理去完成,GoF称之为虚代理
(Virtual Proxy);
2)为网络上的对象创建一个局部的本地代理,比如要操作一个网络上的一个对象(网络性能不好的时候,问题尤其
突出),我们将这个操纵的过程交给一个代理去完成,GoF称之为远程代理(Remote Proxy);
3)对对象进行控制访问的时候,比如在Jive论坛中不同权限的用户(如管理员、普通用户等)将获得不同层次的操
作权限,我们将这个工作交给一个代理去完成,GoF称之为保护代理(Protection Proxy)。
//虚代理实例
#pragma once
#include
#include
using namespace std;
#ifndef _PROXY_H_
#define _PROXY_H_
class Image
{
public:
Image(string name): m_imageName(name) {}
virtual ~Image() {}
virtual void Show() {}
protected:
string m_imageName;
};
class BigImage: public Image
{
public:
BigImage(string name):Image(name) {}
~BigImage() {}
void Show() { cout<<"Show big image : "<
class BigImageProxy: public Image
{
private:
BigImage *m_bigImage;
public:
BigImageProxy(string name):Image(name),m_bigImage(0) {}
~BigImageProxy() { delete m_bigImage; }
void Show()
{
if(m_bigImage == NULL)
m_bigImage = new BigImage(m_imageName);
m_bigImage->Show();
}
};
#endif
//智能指针实例
template
class smart_ptr
{
public:
smart_ptr(T *p = 0): pointee(p), count(new size_t(1)) { } //初始的计数值为1
smart_ptr(const smart_ptr &rhs): pointee(rhs.pointee), count(rhs.count) { ++*count; } //拷贝构造函数,计数加1
~smart_ptr() { decr_count(); } //析构,计数减1,减到0时进行垃圾回收,即释放空间
smart_ptr& operator= (const smart_ptr& rhs) //重载赋值操作符
{
//给自身赋值也对,因为如果自身赋值,计数器先减1,再加1,并未发生改变
++*count;
decr_count();
pointee = rhs.pointee;
count = rhs.count;
return *this;
}
//重载箭头操作符和解引用操作符,未提供指针的检查
T *operator->() { return pointee; }
const T *operator->() const { return pointee; }
T &operator*() { return *pointee; }
const T &operator*() const { return *pointee; }
size_t get_refcount() { return *count; } //获得引用计数器值
private:
T *pointee; //实际指针,被代理
size_t *count; //引用计数器
void decr_count() //计数器减1
{
if(--*count == 0)
{
delete pointee;
delete count;
}
}
};
//实例001
#include
#include
using namespace std?
//定义接口
class Interface
{
public:
virtual void Request()=0?
}?
//真实类
class RealClass : public Interface
{
public:
virtual void Request()
{
cout<<"真实的请求"<
};
//代理类
class ProxyClass : public Interface
{
private:
RealClass* m_realClass
public:
virtual void Request()
{
m_realClass= new RealClass();
m_realClass->Request();
delete m_realClass;
}
};
客户端:
int main()
{
ProxyClass* test=new ProxyClass()?
test->Request();
return 0?
}
//-----------------------------------------行为模式-----------------------------------------
<1> Template(模板模式)
GOOD:把不变的代码部分都转移到父类中,将可变的代码用virtual留到子类重写.
1)Template模式是采用继承的方式实现这一点:将逻辑(算法)框架放在抽象基类中,并定义好细节的接口,子类中实现细节。
2)Template模式实际上就是利用面向对象中多态的概念实现算法实现细节和高层接口的松耦合。可以看到Template模式采取的
是继承方式实现这一点的,由于继承是一种强约束性的条件,因此也给Template模式带来一些许多不方便的地方(有关这一点
将在讨论中展开)。
//实例
//模式举例: 用冒泡算法非别对整型数组、浮点数数组、日期数组实现排序。
//.h
#pragma once
#include
#include
#include
using namespace std;
#ifndef _TEMPLATE_H_
#define _TEMPLATE_H_
class Template
{
public:
Template(void);
~Template(void);
protected:
int DoSort();
virtual void Swap(int nIndex) = 0;
virtual bool OutOfOrder(int index) = 0;
private:
int m_operations;
protected:
int m_length;
};
class IntSort : protected Template
{
public:
IntSort();
~IntSort();
int Sort(int *nArray, int nLen);
protected:
int DoDort();
virtual void Swap(int nIndex);
virtual bool OutOfOrder(int index);
private:
int *m_Array;
};
#endif
//.cpp
#include "StdAfx.h"
#include "Template.h"
Template::Template(void):m_operations(0),m_length(0)
{
}
Template::~Template(void)
{
}
int Template::DoSort()
{
m_operations = 0;
if (m_length == 0)
{
return m_operations;
}
for (int nextToLast = m_length - 2; nextToLast >= 0; nextToLast--)
{
for (int index = 0; index <= nextToLast; index++)
{
if (OutOfOrder(index))
{
Swap(index);
}
m_operations++;
}
}
return m_operations;
}
//IntSort
IntSort::IntSort()
{
}
IntSort::~IntSort()
{
}
int IntSort::Sort(int *nArray, int nLen)
{
m_Array = nArray;
m_length = nLen;
// 调用冒泡算法
return DoSort();
}
void IntSort::Swap(int nIndex)
{
int temp = m_Array[nIndex];
m_Array[nIndex] = m_Array[nIndex + 1];
m_Array[nIndex + 1] = temp;
} <2> Strategy(策略模式) class FIFO_ReplaceAlgorithm : public ReplaceAlgorithm 总结: protected: void Attach(Observer *observer) private: protected: blog->SetStatus(" 更新博客"); //更改内容 blog->Remove(observer2); <5>Memento() #ifndef _MEMENTO_H_ //需保存的信息 <6>Mediator(中介者模式) class Mediator; 实例: void MakeMutton() protected: class BakeMuttonCmd : public Command void SetCmd(Command *command) <8>Visitor(访问者模式) #ifndef _VISITOR_H_ public: public: public: //Visitor public: { // 假定要为Shape的子类增加一个填充(Fill)操作,那么就为Visitor增加一个子类,即FillVisitor } public: class AddTextVisitor : public Visitor } public: Shape *circle = new Circle(); Visitor *visitor2 = new AddTextVisitor(); 实例: private: // 具体处理类 //ConcreteDirectorHandler(Handler *next_handler) : Handler(next_handler) string Get_Name() private: // 增加一个元素 // 获取一个指定位置的元素 T get_element_at(int i) // 获取集合中元素的个数 { // 获取迭代器(智能指针) public: // 是为了进行严格的类型检查。一般而言,向下转型时不安全的,因此要使 if(index < collection->get_size()) { { T Next() <11>Interpreter(解释器模式) 实例: public: int interpreter(Context context) }; class SubtractNonterminalExpression : public AbstractExpression public: int interpreter(Context context) }; public : int interpreter(Context context) cout<< addValue->interpreter(context)<
bool IntSort::OutOfOrder(int index)
{
return (m_Array[index] > m_Array[index + 1]);
}
//Test
//Template
cout << "--------------Template Begin--------------" <
IntSort *tmp = new IntSort();
tmp->Sort(nTest, 10);
cout<<"Sort: "<
cout << "--------------Template End--------------" <
总结:
Template模式获得一种反向控制结构效果,这也是面向对象系统的分析和设计中一个原则DIP(依赖倒置:Dependency
Inversion Principles)。其含义就是父类调用子类的操作(高层模块调用低层模块的操作),低层模块实现高层模块
声明的接口。这样控制权在父类(高层模块),低层模块反而要依赖高层模块。
优点:
1.模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
2.子类实现算法的某些细节,有助于算法的扩展。
3.通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
缺点:
1.每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。
适用性:
1.在某些类的算法中,用了相同的方法,造成代码的重复。
2.控制子类扩展,子类必须遵守算法规则。
定义算法家族,分别封装起来,让它们之间可以互相替换,让算法变化,不会影响到用户.
GOOD:适合类中的成员以方法为主,算法经常变动;简化了单元测试(因为每个算法都有自己的类,可以通过自己的接口单独
测试。策略模式和简单工厂基本相同,但简单工厂模式只能解决对象创建问题,对于经常变动的算法应使用策略模式。
BUG:客户端要做出判断.
1)策略模式是指定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户
而变化。也就是说这些算法所完成的功能一样,对外的接口一样,只是各自实现上存在差异。用策略模式来封装算法,效果
比较好。
实例:
//抽象接口
class ReplaceAlgorithm
{
public:
virtual void Replace() = 0;
};
//三种具体的替换算法
class LRU_ReplaceAlgorithm : public ReplaceAlgorithm
{
public:
void Replace() { cout<<"Least Recently Used replace algorithm"<
{
public:
void Replace() { cout<<"First in First out replace algorithm"<
class Random_ReplaceAlgorithm: public ReplaceAlgorithm
{
public:
void Replace() { cout<<"Random replace algorithm"<
//方式一:直接通过参数指定,传入一个特定算法的指针。
class Cache
{
private:
ReplaceAlgorithm *m_ra;
public:
Cache(ReplaceAlgorithm *ra) { m_ra = ra; }
~Cache() { delete m_ra; }
void Replace() { m_ra->Replace(); }
};
//test--只能以下面这种方式使用,可以看到暴露了太多的细节。
int main()
{
Cache cache(new LRU_ReplaceAlgorithm()); //暴露了算法的定义
cache.Replace();
return 0;
}
//
//方式二:也是直接通过参数指定,只不过不是传入指针,而是一个标签。这样客户只要知道算法的相应标签即可,而不需
//要知道算法的具体定义。
enum RA {LRU, FIFO, RANDOM}; //标签
class Cache
{
private:
ReplaceAlgorithm *m_ra;
public:
Cache(enum RA ra)
{
if(ra == LRU)
m_ra = new LRU_ReplaceAlgorithm();
else if(ra == FIFO)
m_ra = new FIFO_ReplaceAlgorithm();
else if(ra == RANDOM)
m_ra = new Random_ReplaceAlgorithm();
else
m_ra = NULL;
}
~Cache() { delete m_ra; }
void Replace() { m_ra->Replace(); }
};
//test--相比方式一,这种方式用起来方便多了。其实这种方式将简单工厂模式与策略模式结合在一起,算法的定义使用了策
//略模式,而Cache的定义其实使用了简单工厂模式。
int main()
{
Cache cache(LRU); //指定标签即可
cache.Replace();
return 0;
}
//方式三:利用模板实现。算法通过模板的实参指定。当然了,还是使用了参数,只不过不是构造函数的参数。在策略模式中,
//参数的传递难以避免,客户必须指定某种算法。
template
class Cache
{
private:
RA m_ra;
public:
Cache() { }
~Cache() { }
void Replace() { m_ra.Replace(); }
};
//test
int main()
{
Cache
cache.Replace();
return 0;
}
1)Strategy模式和Template模式要解决的问题是相同(类似)的,都是为了给业务逻辑(算法)具体实现和抽象接口之间的
解耦。Strategy模式将逻辑(算法)封装到一个类(Context)里面,通过组合的方式将具体算法的实现在组合对象中实现,
再通过委托的方式将抽象接口的实现委托给组合对象实现。State模式也有类似的功能,他们之间的区别将在讨论中给出。
2)Strategy模式和Template模式实际是实现一个抽象接口的两种方式:继承和组合之间的区别。要实现一个抽象接口,继承是
一种方式:我们将抽象接口声明在基类中,将具体的实现放在具体子类中。组合(委托)是另外一种方式:我们将接口的实现
放在被组合对象中,将抽象接口放在组合类中。这两种方式各有优缺点,先列出来:
1.继承:
GOOD:易于修改和扩展那些被复用的实现。
BUG:破坏了封装性,继承中父类的实现细节暴露给子类了; “白盒”复用,原因在1)中;当父类的实现更改时,其所
有子类将不得不随之改变;从父类继承而来的实现在运行期间不能改变(编译期间就已经确定了)。
2.组合:
GOOD:“黑盒”复用,因为被包含对象的内部细节对外是不可见的;封装性好,原因为1); 实现和抽象的依赖性很小
(组合对象和被组合对象之间的依赖性小);可以在运行期间动态定义实现(通过一个指向相同类型的指针,典型
的是抽象基类的指针)。
BUG:系统中对象过多。
从上面对比中我们可以看出,组合相比继承可以取得更好的效果,因此在面向对象的设计中的有一条很重要的原则就是:
优先使用(对象)组合,而非(类)继承(Favor Composition Over Inheritance)。
<3> State(状态模式)
GOOD:当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,可考虑用到状态模式。
1)允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。它有两种使用情况:(1)一个对象的行
为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。(2)一个操作中含有庞大的多分支的条件语句,且这
些分支依赖于该对象的状态。
实例:
class War;
class State
{
public:
virtual void Prophase() {}
virtual void Metaphase() {}
virtual void Anaphase() {}
virtual void End() {}
virtual void CurrentState(War *war) {}
};
//战争
class War
{
private:
State *m_state; //目前状态
int m_days; //战争持续时间
public:
War(State *state): m_state(state), m_days(0) {}
~War() { delete m_state; }
int GetDays() { return m_days; }
void SetDays(int days) { m_days = days; }
void SetState(State *state) { delete m_state; m_state = state; }
void GetState() { m_state->CurrentState(this); }
};
//战争结束
class EndState: public State
{
public:
void End(War *war) //结束阶段的具体行为
{
cout<<"战争结束"<
void CurrentState(War *war) { End(war); }
};
//后期
class AnaphaseState: public State
{
public:
void Anaphase(War *war) //后期的具体行为
{
if(war->GetDays() < 30)
cout<<"第"<
{
war->SetState(new EndState());
war->GetState();
}
}
void CurrentState(War *war) { Anaphase(war); }
};
//中期
class MetaphaseState: public State
{
public:
void Metaphase(War *war) //中期的具体行为
{
if(war->GetDays() < 20)
cout<<"第"<
{
war->SetState(new AnaphaseState());
war->GetState();
}
}
void CurrentState(War *war) { Metaphase(war); }
};
//前期
class ProphaseState: public State
{
public:
void Prophase(War *war) //前期的具体行为
{
if(war->GetDays() < 10)
cout<<"第"<
{
war->SetState(new MetaphaseState());
war->GetState();
}
}
void CurrentState(War *war) { Prophase(war); }
};
//测试案例
int main()
{
War *war = new War(new ProphaseState());
for(int i = 1; i < 40;i += 5)
{
war->SetDays(i);
war->GetState();
}
delete war;
return 0;
}
总结:
1)State模式在实现中,有两个关键点:
将State声明为Context的友元类(friend class),其作用是让State模式访问Context的protected接口ChangeSate()。
State及其子类中的操作都将Context*传入作为参数,其主要目的是State类可以通过这个指针调用Context中的方法
(在本示例代码中没有体现)。这也是State模式和Strategy模式的最大区别所在。
2)State模式很好地实现了对象的状态逻辑和动作实现的分离,状态逻辑分布在State的派生类中实现,而动作实现则可以放在
Context类中实现(这也是为什么State派生类需要拥有一个指向Context的指针)。这使得两者的变化相互独立,改变State
的状态逻辑可以很容易复用Context的动作,也可以在不影响State派生类的前提下创建Context的子类来更改或替换动作实现。
3)State模式问题主要是逻辑分散化,状态逻辑分布到了很多的State的子类中,很难看到整个的状态逻辑图,这也带来了代码
的维护问题。
<4>Observer(观察者模式)
GOOD:定义了一种一对多的关系,让多个观察对象(公司员工)同时监听一个.
1)观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自
动更新。它还有两个别名,依赖(Dependents),发布-订阅(Publish-Subsrcibe)。
#pragma once
#ifndef _OBSERVER_H_
#define _OBSERVER_H_
#include
using namespace std;
class Observer
{
public:
Observer(void){};
~Observer(){};
virtual void Update(){};
private:
};
class Blog
{
public:
Blog(){};
~Blog(){};
{
m_Observer.push_back(observer);
}
void Remove(Observer *observer)
{
m_Observer.remove(observer);
}
void Notify()
{
list
for( ; iter != m_Observer.end(); iter++)
{
(*iter)->Update();
}
}
virtual void SetStatus(string s) { m_status = s; } //设置状态
virtual string GetStatus() { return m_status; } //获得状态
list
string m_status;
};
//具体博客类
class BlogCSDN : public Blog
{
private:
string m_name; //博主名称
public:
BlogCSDN(string name): m_name(name) {}
~BlogCSDN() {}
void SetStatus(string s) { m_status = "CSDN通知 : " + m_name + s; } //具体设置状态信息
string GetStatus() { return m_status; }
};
//具体观察者
class ObserverBlog : public Observer
{
private:
string m_name; //观察者名称
Blog *m_blog; //观察的博客,当然以链表形式更好,就可以观察多个博客
public:
ObserverBlog(string name,Blog *blog): m_name(name), m_blog(blog) {}
~ObserverBlog() {}
void Update() //获得更新状态
{
string status = m_blog->GetStatus();
cout<
};
class ObserverBlog2 : public Observer
{
private:
string m_name; //观察者名称
Blog *m_blog; //观察的博客,当然以链表形式更好,就可以观察多个博客
public:
ObserverBlog2(string name,Blog *blog): m_name(name), m_blog(blog) {}
~ObserverBlog2() {}
void Update() //获得更新状态
{
string status = m_blog->GetStatus();
cout<
};
#endif
//Test
//Observer
cout << "\n--------------Observer Begin--------------" <
Observer *observer1 = new ObserverBlog("test", blog);
Observer *observer2 = new ObserverBlog2("test01", blog);
blog->Attach(observer1);
blog->Attach(observer2);
blog->SetStatus(" 发表设计模式C++实现(15)——观察者模式"); //更改内容
blog->Notify(); //通知所有观察者
blog->Notify(); //通知所有观察者
blog->Notify();
delete blog; delete observer1;
cout << "--------------Observer End--------------" <
1)观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,
会通知所有观察者对象,使它们能够自动更新自己.
2)何时使用:
1.当一个对象的改变需要同时改变其他对象的时候,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。
2.观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都
不会影响另一边的变化。
GOOD:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样就可以将以后的对象状态恢
复到先前保存的状态。适用于功能比较复杂的,但需要记录或维护属性历史的类;或者需要保存的属性只是众多属性中的
一小部分时Originator可以根据保存的Memo还原到前一状态。
1)Memento模式的关键就是要在不破坏封装行的前提下,捕获并保存一个类的内部状态,这样就可以利用该保存的状态实施恢
复操作。为了达到这个目标,可以在后面的实现中看到我们采取了一定语言支持的技术。
2)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保
存的状态[DP]。举个简单的例子,我们玩游戏时都会保存进度,所保存的进度以文件的形式存在。这样下次就可以继续玩,
而不用从头开始。这里的进度其实就是游戏的内部状态,而这里的文件相当于是在游戏之外保存状态。这样,下次就可以从
文件中读入保存的进度,从而恢复到原来的状态。这就是备忘录模式。
实例:
#pragma once
#define _MEMENTO_H_
class Memento
{
public:
int m_vitality; //生命值
int m_attack; //进攻值
int m_defense; //防守值
public:
Memento(int vitality, int attack, int defense) : m_vitality(vitality), m_attack(attack), m_defense(defense){}
Memento& operator=(const Memento &memento)
{
m_vitality = memento.m_vitality;
m_attack = memento.m_attack;
m_defense = memento.m_defense;
return *this;
}
};
//游戏角色
class GameRole
{
private:
int m_vitality;
int m_attack;
int m_defense;
public:
GameRole(): m_vitality(100),m_attack(100),m_defense(100) {}
Memento Save() //保存进度,只与Memento对象交互,并不牵涉到Caretake
{
Memento memento(m_vitality, m_attack, m_defense);
return memento;
}
void Load(Memento memento) //载入进度,只与Memento对象交互,并不牵涉到Caretake
{
m_vitality = memento.m_vitality;
m_attack = memento.m_attack;
m_defense = memento.m_defense;
}
void Show() { cout<<"vitality : "<< m_vitality<<", attack : "<< m_attack<<", defense : "<< m_defense<
};
//保存的进度库
class Caretake
{
public:
Caretake() {}
void Save(Memento menento) { m_vecMemento.push_back(menento); }
Memento Load(int state) { return m_vecMemento[state]; }
private:
vector
};
#endif
//Test
cout << "\n--------------Memento Begin--------------" <
GameRole role;
role.Show(); //初始值
caretake.Save(role.Save()); //保存状态
role.Attack();
role.Show(); //进攻后
role.Load(caretake.Load(0)); //载入状态
role.Show(); //恢复到状态0
cout << "--------------Memento End--------------" <
2)Memento模式的关键就是friend class Originator;我们可以看到,Memento的接口都声明为private,而将Originator声明
为Memento的友元类。我们将Originator的状态保存在Memento类中,而将Memento接口private起来,也就达到了封装的功效。
在Originator类中我们提供了方法让用户后悔:RestoreToMemento(Memento* mt);我们可以通过这个接口让用户后悔。在
测试程序中,我们演示了这一点:Originator的状态由old变为new最后又回到了old。
GOOD:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显示的相互引用,从而降低耦合;而且可以独立地改
变它们之间的交互。
1)Mediator模式的实现关键就是将对象Colleague之间的通信封装到一个类种单独处理.
2)用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改
变它们之间的交互。中介者模式的例子很多,大到联合国安理会,小到房屋中介,都扮演了中间者的角色,协调各方利益。
实例:
#pragma once
#ifndef _MEDIATOR_H_
#define _MEDIATOR_H_
//抽象人
class Person
{
protected:
Mediator *m_mediator; //中介
public:
virtual void SetMediator(Mediator *mediator){} //设置中介
virtual void SendMessage(string message) {} //向中介发送信息
virtual void GetMessage(string message) {} //从中介获取信息
};
//抽象中介机构
class Mediator
{
public:
virtual void Send(string message, Person *person) {}
virtual void SetA(Person *A) {} //设置其中一方
virtual void SetB(Person *B) {}
};
//租房者
class Renter: public Person
{
public:
void SetMediator(Mediator *mediator) { m_mediator = mediator; }
void SendMessage(string message) { m_mediator->Send(message, this); }
void GetMessage(string message) { cout<<"租房者收到信息"<
//房东
class Landlord: public Person
{
public:
void SetMediator(Mediator *mediator) { m_mediator = mediator; }
void SendMessage(string message) { m_mediator->Send(message, this); }
void GetMessage(string message) { cout<<"房东收到信息:"<
//房屋中介
class HouseMediator : public Mediator
{
private:
Person *m_A; //租房者
Person *m_B; //房东
public:
HouseMediator(): m_A(0), m_B(0) {}
void SetA(Person *A) { m_A = A; }
void SetB(Person *B) { m_B = B; }
void Send(string message, Person *person)
{
if(person == m_A) //租房者给房东发信息
m_B->GetMessage(message); //房东收到信息
else
m_A->GetMessage(message);
}
};
#endif
//Test
cout << "\n--------------Mediator Begin--------------" <
Person *person1 = new Renter(); //租房者
Person *person2 = new Landlord(); //房东
mediator->SetA(person1);
mediator->SetB(person2);
person1->SetMediator(mediator);
person2->SetMediator(mediator);
person1->SendMessage("我想在南京路附近租套房子,价格800元一个月\n");
person2->SendMessage("出租房子:南京路100号,70平米,1000元一个月\n");
delete person1; delete person2; delete mediator;
cout << "--------------Mediator End--------------" <
总结:
Mediator模式是一种很有用并且很常用的模式,它通过将对象间的通信封装到一个类中,将多对多的通信转化为一对多的
通信,降低了系统的复杂性。Mediator还获得系统解耦的特性,通过Mediator,各个Colleague就不必维护各自通信的对象
和通信协议,降低了系统的耦合性,Mediator和各个Colleague就可以相互独立地修改了。
<7>Command(命令模式)
GOOD:一、建立命令队列;
二、可以将命令记入日志;
三、接收请求的一方可以拒绝;
四、添加一个新命令类不影响其它类;命令模式把请求一个操作的对象与知道怎么操行一个操作的对象分开
1)Command模式通过将请求封装到一个对象(Command)中,并将请求的接受者存放到具体的ConcreteCommand类中(Receiver)
中,从而实现调用操作的对象和操作的具体实现者之间的解耦。
总结:
实际上,Command模式关键就是提供一个抽象的Command类,并将执行操作封装到Command类接口中,Command类中一般就是
只是一些接口的集合,并不包含任何的数据属性(当然在示例代码中,我们的Command类有一个处理操作的Receiver类的引
用,但是其作用也仅仅就是为了实现这个Command的Excute接口)。这种方式在是纯正的面向对象设计者最为鄙视的设计方
式,就像OO设计新手做系统设计的时候,仅仅将Class作为一个关键字,将C种的全局函数找一个类封装起来就以为是完成了
面向对象的设计。
1.Command模式将调用操作的对象和知道如何实现该操作的对象解耦。在上面Command的结构图中,Invoker对象根本就不
知道具体的是那个对象在处理Excute操作(当然要知道是Command类别的对象,也仅此而已)。
2.在Command要增加新的处理操作对象很容易,我们可以通过创建新的继承自Command的子类来实现这一点。
3.Command模式可以和Memento模式结合起来,支持取消的操作。
#pragma once
#include
#include
#include
using namespace std;
#ifndef _COMMAND_H_
#define _COMMAND_H_
class Barbucer
{
public:
Barbucer(){;};
{
cout << "烤羊肉" << endl;
}
void MakeChickenWing()
{
cout << "烤鸡翅" << endl;
}
};
//Command
class Command
{
public:
Command(Barbucer *barbucer)
{
m_Barbucer = barbucer;
}
virtual void Execute() = 0;
Barbucer *m_Barbucer;
};
{
public:
BakeMuttonCmd(Barbucer *barbucer) : Command(barbucer){};
virtual void Execute()
{
m_Barbucer->MakeMutton();
}
};
class MakeChickenWingCmd : public Command
{
public:
MakeChickenWingCmd(Barbucer *barbucer) : Command(barbucer){};
virtual void Execute()
{
m_Barbucer->MakeChickenWing();
}
};
//Waiter
class Waiter
{
public:
Waiter(){};
{
m_Command.push_back(command);
cout<<"增加定单"<
void Notify()
{
vector
while(iter != m_Command.end())
{
(*iter)->Execute();
iter++;
}
}
private:
vector
};
#endif
//Test
//Command
cout << "Command Begin" <
Command *cmd = new BakeMuttonCmd(barbucer);
Command *cmd2 =new MakeChickenWingCmd(barbucer);
Waiter *girl = new Waiter();
//点菜
girl->SetCmd(cmd);
girl->SetCmd(cmd2);
//服务员通知
girl->Notify();
cout << "Command End" <
1.Command模式在实现的实现和思想都很简单,其关键就是将一个请求封装到一个类中
(Command),再提供处理对象(barbucer),最后Command命令由Waiter激活。另外,
我们可以将请求接收者的处理抽象出来作为参数传给Command对象,实际也就是回调的机
制(Callback)来实现这一点,也就是说将处理操作方法地址(在对象内部)通过参数传递给
Command对象,Command对象在适当的时候(Invoke激活的时候)再调用该函数。
1)Command模式将调用操作的对象和知道如何实现该操作的对象解耦。在上面Command的结构图
中,Waiter对象根本就不知道具体的是那个对象在处理Excute操作(当然要知道是Command
类别的对象,也仅此而已)。
2)在Command要增加新的处理操作对象很容易,我们可以通过创建新的继承自Command的子类来
实现这一点。
3)Command模式可以和Memento模式结合起来,支持取消的操作。
http://blog.163.com/prevBlogPerma.do?host=patmusing&srl=1358349602010150249596&mode=prev
GOOD:适用于数据结构稳定的系统。它把数据结构和作用于数据结构上的操作分离开,使得操作
集合.
优点:新增加操作很容易,因为增加新操作就相当于增加一个访问者,访问者模式将有关的行为
集中到一个访问者对象中
1)将更新(变更)封装到一个类中(访问操作),并由待更改类提供一个接收接口,则可达到效果。
2)类层次结构中可能经常由于引入新的操作,从而将类型变得很脆弱。比如假定三个类Rectangle、
Triangle和Line继承自抽象类Shape,现在由于某种原因需要在抽象类Shape中增加一个abstract
(或者pure virtual)方法,如果使用通常的方法,那么它所有的子类,即Rectangle、Triangle和
Line都需要修改。
3)Visitor设计模式要解决的问题:
1. 类层次结构不变,但类的方法需要增加;
2. 预料到基类有更改,但不知道是何种具体更改。
要增加一个方法,实际上就是增加一个Visitor的子类。
实例:
#pragma once
#include
#include
#include
using namespace std;
#define _VISITOR_H_
class Visitor;
class Shape
{
public:
Shape(){};
~Shape(){};
// draw为已经确知需要的方法
virtual void Draw() = 0;
// 1. 预料将来可能会引入新的方法,因此在这里预留一个accept方法
// 2. 可以通过下面的accept方法,为Shape的子类增加一个或多个方法
virtual void Accept(Visitor *visitor) = 0;
};
//Rectangle
class Rectangle : public Shape
{
public:
Rectangle(){};
~Rectangle(){};
virtual void Draw()
{
cout << "Draw Rectangle" <
virtual void Accept(Visitor *visitor);
{
visitor->visit(this); // 转发到visitor的visit方法,并将this作为参数传递过去
}
};
//Rectangle
class Circle : public Shape
{
public:
Circle(){};
~Circle(){};
virtual void Draw()
{
cout << "Draw Circle" << endl;
}
virtual void Accept(Visitor *visitor);
{
visitor->visit(this); // 转发到visitor的visit方法,并将this作为参数传递过去
}
};
class Visitor
{
public:
// 注意此处参数为具体类,而非抽象类Shape。
// Shape有多少个子类,就需要在此处重载多少次visit方法。
// 除非使用RTTI,则可以只有一个visit方法,并且该方法的参数类型为抽象的Shape*,同时需要在visit方法中确定,
//传入的参数到底是Shape的哪个具体的子类。
virtual void visit(Rectangle *shape) = 0;
virtual void visit(Circle *shape) = 0;
virtual ~Visitor()
cout << "in the destructor of Visitor..." << endl;
}
int n;
};
class FillVisitor : public Visitor
{
public:
void visit(Rectangle *shape)
{
// 为类Rectangle增加一个方法,由于实现这个新方法可能需要用到原来对象中的属性或者方法,因此需要
// 将自身(this)传到这里。理论上而言,如果不用到原来对象的原有的属性或者方法,那么visit方法是可以
// 没有参数的;另外一种情况就是,如果必要,visit方法也可以拥有多个参数
cout << "Here is the newly added Fill() method for class Rectangle." << endl;
void visit(Circle *shape)
{
// 为类Circle增加一个方法,由于实现这个新方法可能需要用到原来对象中的属性或者方法,因此需要
// 将自身(this)传到这里。理论上而言,如果不用到原来对象的原有的属性或者方法,那么visit方法是可以
// 没有参数的;另外一种情况就是,如果必要,visit方法也可以拥有多个参数
cout << "Here is the newly added Fill() method for class Circle." << endl;
}
~FillVisitor()
{
cout << "in the destructor of FillVisitor..." << endl;
}
};
{
public:
void visit(Rectangle *shape)
{
// 为类Rectangle增加一个方法,由于实现这个新方法可能需要用到原来对象中的属性或者方法,因此需要
// 将自身(this)传到这里。理论上而言,如果不用到原来对象的原有的属性或者方法,那么visit方法是可以
// 没有参数的;另外一种情况就是,如果必要,visit方法也可以拥有多个参数
cout << "Here is the newly added AddText() method for class Rectangle." << endl;
void visit(Circle *shape)
{
// 为类Circle增加一个方法,由于实现这个新方法可能需要用到原来对象中的属性或者方法,因此需要
// 将自身(this)传到这里。理论上而言,如果不用到原来对象的原有的属性或者方法,那么visit方法是可以
// 没有参数的;另外一种情况就是,如果必要,visit方法也可以拥有多个参数
cout << "Here is the newly added AddText() method for class Circle." << endl;
}
~AddTextVisitor()
{
cout << "in the destructor of AddTextVisitor..." << endl;
}
};
#endif
//Test
//Visitor
cout << "Visitor Begin" <
Shape *rectangle = new Rectangle();
// 调用该形状已经实现的方法
rectangle->Draw();
//通过visitor1调用新增加的方法
rectangle->Accept(visitor1);
// 调用该形状已经实现的方法
rectangle->Draw();
//通过visitor1调用新增加的方法
circle->Accept(visitor1);
//通过visitor2调用新增加的方法
rectangle->Accept(visitor2);
// 通过visitor2调用新增加的方法
circle->Accept(visitor2);
delete visitor1;
delete visitor2;
delete rectangle;
delete circle;
cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << endl;
//总结:
由上面的代码可以很清楚地知道,我们如果要为Rectangle或Circle增加新方法,只需要多增加
一个Visitor的子类即可,而Rectangle或Circle这两个类本身的代码,没无任何改动。
1)双向分发(Double Dispatch)
accept(Visitor *visitor)方法在Shape的子类 (如Rectangle)中;
visit(Rectangle *rectangle)等visit方法在Visitor的子类 (如FillVisitor)中;
正是这两处多态特性的使用,就形成了所谓的双向分发(Double Dispatch)机制,即首先将visitor
作为参数传递给Shape的子类(在本例中即Rectangle和Circle);然后在Shape子类的accept方法中
调用visitor的visit方法,而且visit方法的参数是this,这样Shape子类就将自己传递到了visitor
类的visit方法中,然后在visitor的visit方法中为Shape的子类增加需要的新的行为。这就是双向分
发的核心思想,即visitor被传递(分发)了一次,即visitor被传递到Shape子类中一次,Shape的子类
也被传递(分发)了一次,即Shape的子类被传递到visitor中一次。
<9>Chain of Responsibility(责任链模式)
GOOD:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个
对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理为止。
1)在软件构建构成中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接收者
,如果显示指定,将必不可少地带来请求发送者与接收者的紧密耦合。COR(Chain of Reposibility)
设计模式可以使请求的发送者不需要指定具体的接收者,让请求的接收者自己在运行时决定来处理请
求,从而使两者解耦。
2)C++示例代码如下:
说明:
-经理级别可以决定是否采购500RMB以下的物品;
-总监级别可以决定是否采购5000RMB以下的物品;
-总裁级别可以决定是否采购任何物品。
#pragma once
#include
#include
#include
using namespace std;
#ifndef _COR_H_
#define _COR_H_
class CORMessage
{
public:
CORMessage(double price) : m_price(price)
{
;
}
double Get_Price()
{
return m_price;
}
double m_price;
};
//Handle
class Handler
{
public:
Handler(auto_ptr
{
;
}
virtual void handleMessage(CORMessage cor_message) = 0;
virtual bool canHandleMessage(CORMessage cor_message) = 0;
virtual ~Handler()
{
;
}
protected:
auto_ptr
};
//
class ConcreteManagerHandler : public Handler
{
public:
ConcreteManagerHandler(auto_ptr
{
}
~ConcreteManagerHandler()
{
cout << "in the destructor of ConcreteManagerHandler..." << endl;
}
public:
void handleMessage(CORMessage cor_message)
{
if(canHandleMessage(cor_message))
{
cout << "MESSAGE IS HANDLED SUCCESSFULLY! -- ConcreteManagerHandler" << endl;
}
else
{
next_handler->handleMessage(cor_message);
}
}
bool canHandleMessage(CORMessage cor_message)
{
if(cor_message.Get_Price() < 500.0)
return true;
else
return false;
}
};
class ConcreteDirectorHandler : public Handler
{
public:
ConcreteDirectorHandler(auto_ptr
{
}
~ConcreteDirectorHandler()
{
cout << "in the destructor of ConcreteDirectorHandler..." << endl;
}
public:
void handleMessage(CORMessage cor_message)
{
if(canHandleMessage(cor_message))
{
cout << "MESSAGE IS HANDLED SUCCESSFULLY! -- ConcreteDirectorHandler" << endl;
}
else
{
next_handler->handleMessage(cor_message);
}
}
bool canHandleMessage(CORMessage cor_message)
{
if(cor_message.Get_Price() < 5000.0)
return true;
else
return false;
}
};
// 具体处理类
class ConcreteOfficerHandler : public Handler
{
public:
//ConcreteOfficerHandler(Handler *next_handler) : Handler(next_handler)
ConcreteOfficerHandler(auto_ptr
{
}
~ConcreteOfficerHandler()
{
cout << "in the destructor of ConcreteOfficerHandler..." << endl;
}
public:
void handleMessage(CORMessage cor_message)
{
if(canHandleMessage(cor_message))
{
cout << "MESSAGE IS HANDLED SUCCESSFULLY! -- ConcreteOfficerHandler" << endl;
}
else
{
next_handler->handleMessage(cor_message);
}
}
bool canHandleMessage(CORMessage cor_message)
{
return true; // 总裁级别可以决定购买任何价格的物品
}
};
#endif
//Test
cout << "~~~~~~~~~~~~~Cor Begin~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << endl;
auto_ptr
auto_ptr
auto_ptr
manager_handler->handleMessage(CORMessage(350.0));
manager_handler->handleMessage(CORMessage(600.0));
manager_handler->handleMessage(CORMessage(10000.11));
cout << "~~~~~~~~~~~~~Cor Ebd~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << endl;
//
从输出结果可见,
购买350.0RMB的物品被ConcreteManagerHandler处理了;
购买600.0RMB的物品被ConcreteDirectorHandler处理了;
购买10000.11RMB的物品被ConcreteOfficerHandler处理了。
//总结:
1)Chain of Responsibility模式描述其实就是这样一类问题将可能处理一个请求的对象链接成一
个链,并将请求在这个链上传递,直到有对象处理该请求.
2)Chain of Responsibility模式的最大的一个有点就是给系统降低了耦合性,请求的发送者完全
不必知道该请求会被哪个应答对象处理,极大地降低了系统的耦合性。
<10>Iterator(迭代器模式)
GOOD:提供一种方法顺序访问一个聚敛对象的各个元素,而又不暴露该对象的内部表示。为遍历
不同的聚集结构提供如开始,下一个,是否结束,当前一项等统一接口。
实例:
#pragma once
#include
#include
#include
using namespace std;
#ifndef _ITERATOR_H_
#define _ITERATOR_H_
class Person
{
public:
Person(){};
Person(const string name, const string mp)
{
m_Name = name;
m_Mp = mp;
}
{
return m_Name + "\t" + m_Mp;
}
string m_Name;
string m_Mp;
};
//Iterator
template
class Iterator
{
public:
Iterator(){};
virtual T Next() = 0;
virtual bool Has_Next() = 0;
};
// 定义IIterable接口,所有的欲使用迭代器的集合类,都必须继承它
template
class Iterable
{
public:
virtual auto_ptr
public:
virtual ~Iterable()
{
cout << "in the destructor of IIterable..." << endl;
}
};
//具体集合类
template
class ConcreteCollection : public Iterable
{
public:
ConcreteCollection()
{
m_test = new T[100];
m_nIndex = 0;
}
~ConcreteCollection()
{
delete []m_test;
}
void add_element(T obj)
{
m_test[m_nIndex++] = obj;
}
{
return m_test[i];
}
int get_size()
return m_nIndex;
}
auto_ptr
{
auto_ptr
return temp;
}
private:
T *m_test;
int m_nIndex;
};
// 具体迭代器类
template
class ConcreteIterator : public Iterator
{
private:
int index;
ConcreteCollection
ConcreteIterator(Iterable
{
index = 0;
collection = dynamic_cast
//向下转型,即将基类的指针转换为派生类的指针,此处使用dynamic_cast
// 用dynamic_cast
}
bool Has_Next()
{
if(0 == collection->get_size())
{
return false;
}
return true;
}
else
return false;
}
}
{
return collection->get_element_at(index++);
}
public:
~ConcreteIterator()
{
cout << "in the destructor of ConcreteIterator..." << endl;
}
};
#endif
//Text
//Iterator
cout << "~~~~~~~~~~~~~Iterator Begin~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << endl;
ConcreteCollection
collection->add_element(Person("玄机逸士", "13800000001"));
collection->add_element(Person("上官天野", "13800000002"));
collection->add_element(Person("黄药师 ", "13800000003"));
collection->add_element(Person("欧阳锋 ", "13800000004"));
collection->add_element(Person("段王爷 ", "13800000005"));
collection->add_element(Person("洪七公 ", "13800000006"));
auto_ptr
while(iter->Has_Next())
{
cout << (iter->Next().Get_Name()) << endl;
}
delete collection;
cout << "~~~~~~~~~~~~~Iterator Begin~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << endl;
//Iterator模式实现需注意的要点:
1)两个重要接口,即IIterable和IIterator,它们分别对应的具体类ConcreteCollection和
ConcreteIterator。本质上而言ConcreteCollection中存放的是数据,ConcreteIterator中实
现的是遍历的方法。
2)ConcreteCollection中的getIterator()方式的实现技巧。即
return new ConcreteIterator(this);
ConcreteCollection将自己(当然也包括ConcreteCollection中包含的数据)通过ConcreteIterator
的构造函数,传递给ConcreteIterator,然后ConcreteIterator利用已实现的方法对其进行遍历。
GOOD:通常当一个语言需要解释执行,并且你可以将该语言中的句子表示成为一个抽象的语法树时,
可以使用解释器模式。
1)Interpreter模式提供了一个实现语法解释器的框架,其目的就是使用一个解释器为用户提供一个
一门定义语言语法表示的解释器,并且通过这个解释器来解释语言中的句子。
#include
#include
void addValue(string key,int value)
{
valueMap.insert(std::pair
}
int getValue(string key)
{
return valueMap[key];
}
};
class AbstractExpression
{
public :
virtual int interpreter(Context context) = 0;
};
class AddNonterminalExpression : public AbstractExpression
{
private :
AbstractExpression *left;
AbstractExpression *right;
public:
AddNonterminalExpression(AbstractExpression *left, AbstractExpression *right)
{
this->left = left;
this->right = right;
}
{
return this->left->interpreter(context) + this->right->interpreter(context);
}
{
private :
AbstractExpression *left;
AbstractExpression *right;
SubtractNonterminalExpression(AbstractExpression *left, AbstractExpression *right)
{
this->left = left;
this->right = right;
}
{
return this->left->interpreter(context) - this->right->interpreter(context);
}
class TerminalExpression : public AbstractExpression
{
private :
int i;
TerminalExpression(int i)
{
this->i = i;
}
{
return this->i;
}
};
//Test
//Interpreter
cout << "~~~~~~~~~~~~~Interpreter Begin~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << endl;
Context context;
context.addValue("a", 7);
context.addValue("b", 8);
context.addValue("c", 2);
SubtractNonterminalExpression *subtractValue = new SubtractNonterminalExpression(new TerminalExpression(context.getValue("a")), new TerminalExpression(context.getValue("b")));
AddNonterminalExpression *addValue = new AddNonterminalExpression(subtractValue, new TerminalExpression(context.getValue("c")));
总结:
适用性:
在以下情况下可以考虑使用解释器模式:
(1)可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
(2)一些重复出现的问题可以用一种简单的语言来进行表达。
(3)一个语言的文法较为简单。
(4)执行效率不是关键问题。(注:高效的解释器通常不是通过直接解释抽象语法树来实现的,
而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。)
优缺点:
优点:
(1)易于改变和扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继
承等机制来改变或扩展文法。
(2)每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
(3)实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的
代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。
(4)增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的
终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合“开闭原则”。
缺点:
(1)对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个
语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使
用语法分析程序等方式来取代解释器模式。
(2)执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子
时其速度很慢,而且代码的调试过程也比较麻烦。
总论:
尽量不要在重要模块中使用解释器模式,因为维护困难。在项目中,可以使用脚本语言来代
替解释器模式。
http://blog.csdn.net/wuzhekai1985/article/category/859763
http://blog.csdn.net/wuzhekai1985
??