参考:
http://blog.csdn.net/column/details/design.html
http://blog.csdn.net/zhengzhb/article/details/7278174
http://blog.csdn.net/column/details/mydesignpattern.html
依赖: 一个对象作为另一个对象方法的方法参数,局部变量等.
关联、聚合和组合 ,即对象包含另外一个对象
泛化,即继承.
一个类就完成一个或者一类功能
子类可以扩展父类的功能,但不能改变父类原有的功能
依赖倒置原则的核心思想是面向接口编程
应用层的逻辑不发生改变,改变其依赖的底层组件(接口),从而改变特定功能
某逻辑依赖某个组建(BASE抽象类,接口类,仅有方法).
个对象应该对其他对象保持最少的了解,尽量降低类与类之间的耦合
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭
定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。
解决方案:将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则
为何要设计模式,可扩展性,大型程序构建。
尴尬的是有时引入了设计模式去解决扩展性A,结果A没解决,需要解决的扩展性B没解决,代码反而冗余了,慎用。
1-4属于创建类模式。
5-x属于行为类模式。
解决问题: 某些全局功能的类,仅需要一个实体就好。
饿汉模式 开始就生成好
懒汉模式 真正要用的时候才new
//m_instane和getInstance都是static成员
timerRunner* timerRunner::m_instance = NULL;
timerRunner* timerRunner::getInstance(void)
{
if(NULL == m_instance)
{
pthread_mutex_lock(&mutex);
if(NULL == m_instance)
{
m_instance = new timerRunner(128);
}
pthread_mutex_unlock(&mutex);
}
return m_instance;
}
工厂就是类对象的生产器,管理复杂的对象生成,将具体实例化封装起来,所有对象的new由它来做,而不使用具体类去new各自对象。使用时,先new出工厂,工厂再生产出需要的类。
例子:
A 面食
A1 面条
AA1 儿童面条
B1 饺子
C 西餐
牛排
沙拉
简单工厂模式:
面食仅用一个工厂来生产.用choice选择不同面食,增加面食时,需要修改工厂
class Factory {
Mianshi* produceMianshi(int choice) {
if(choice == Miantiao)
if(choice == 饺子)
}
工厂方法模式:
有一个抽象厂,new出不同厂生产不同的面食,需要新的面食就new新的工厂,不修改原来的工厂。
class MianshiFactory {
virtual Mianshi* produceMianshi() = 0;
}
class MiantiaoFactory : pubcic MianshiFactory {
Miantiao* produceMianshi() {return new Miantiao();};
}
class JiaoziFactory : pubcic MianshiFactory {
Jiaozi* produceMianshi() {return new Jiaozi();};
}
抽象工厂模式
扩展抽象工厂.不但可以产面,还可以产西餐,就看派生工厂怎么用了
class SyanticFactory {
virtual Mianshi* produceMianshi() = 0;
virtual Mianshi* produceXican() = 0;
}
组合工厂模式
定义新的抽象工厂,比扩展抽象工厂更灵活。增加个西餐抽象厂 ,厂功能自由组合,比如厂既可以生产饺子,又可以生产牛排.有可以只生产一样。
(遵循职责单一,接口隔离原则)
class BestFactory: public MianshiFacotory , public XicanFactory {...}
封装的含义:做面条要用到水,面,调料,这些new都封装在new Miantiao()里。
Maintiao* produceMaintiao()
{
Shui * s = new Shui();
Mian * m= new Mian();
Liao * l= new Liao();
}
和工厂模式极为类似。
感觉比抽象工厂就多了个Director
原来是new新的工厂,现在Director来接管和管理工厂的new,再封装一层。
4要素:
Product
AbsBuilder
Builder
Director
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。其中有一个词很重要,那就是拷贝。可以说,拷贝是原型模式的精髓所在。
原型模式实现,java里关键就是实现Cloneable接口,才能用clone()函数,深拷贝造成的。
java性能也有区别?大的对象来说,clone比直接new一个性能更好。
对于C++来说,赋值本身就是值语义,其实就是拷贝构造函数,需实现深拷贝,没必要写个clone()函数.
Class a;
Class b = a;//b是a 的拷贝,注意深拷贝,也就是创建了新的实例b,java里的clone()函数
A B 相互影响:
A<-------->B
通过中介类隔离影响,解耦
A<----Meditor---->B
Meditor可以更多的ABCDEFG之间的中介
感觉路由器是个很好的例子:
来源N多,出口N多,通过路由程序,选择合适的路径
避免同事类之间的过度耦合。
使用中介者模式可以将对象间一对多的关联转变为一对一的关联,使对象间的关系易于理解和维护。
使用中介者模式可以将对象的行为和协作进行抽象,能够比较灵活的处理对象间的相互作用。
再举个实际例子:
A:数据来源端 AOA(usb) ADB(socket) IOS(usbmuxd-socket) 发送数据给Meditor
Meditor: Cache
B 应用处理端 只从Meditor接收数据各种应用
Cache仅仅是一个数据转发器,并没有绑定AOA到某个具体应用app1,说中介者模式有点牵强
如果应用是AOA数据只能到app1 app1的数据又要发送到IOS,由cacha绑定路径,
AOA--->APP1 -->processing--->APP2--->IOS
这种cache更符合中介模式中的中介者的角色
定义:对象间的一种一对多的关联关系,当一个对象的状态发生改变时,所有依赖于它的对象做出相应的变化.
(观察者,回调发生的对象)对象A监听对象B(被观察者),只要B的状态发生改变,A得到通知(回调).
回调又是通过多态实现。B含有A的基类指针,调用base->upadte()时,实际调用的是A的子类的具体实现…
叫做“发布-订阅者模式”会不会更好理解.
例子:
1 博客更新,订阅者收到.发布-订阅(Publish-Subsrcibe)
2 mqtt 订阅者无数,发布者一发布,订阅者都收的到,中间件肯定维护了一个订阅者的链表。
链表里元素逐个通知。
这样的结构必然有,若设置优先级,还要用到链表数组.
MSG1- SUB1->SUB2->SUB3
MSG2- SUB1->SUB3->SUB5
MSG3[pri=1] SUB1->SUB3...
MSG3[pri=2] SUB2->SUB4...
角色关系:
被观察者 ,就是上面存SUB链表的类,中间件,发生变化时调用notify();逐一调用SUB的update();
观察者 ,就是各种SUB,实现了一个update()回调.
被访问者或者叫元素类提供一个方法accept,接受所谓的访问者visit,实际又是调用
visit->visit(this).即把自己的信息给访问者,访问者再调用自己的某些方法。
这个某些方法是封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,才需要改变访问者来改变操作.
核心是元素间接改变自己已实现的操作,只要是需要改变操作又不想直接调用的场景,都适用.
void accept(Visitor* v) {
v->visit(this); //执行访问者的动作,一般是this传入,也就是访问者来访时,是携带了自己的信息的
}
#include
using namespace std;
//定义两种访问者,分别访问不同的元素
class Element;
class Visitor {
public:
//定义可能要访问哪些元素
virtual void visit(Element * v) = 0;
virtual ~Visitor(){}
};
class Element {
public:
virtual ~Element(){}
//定义可能有哪些访问者来访
virtual void accept(Visitor* v) {
v->visit(this);}
virtual void jobA() = 0;
virtual void jobB() = 0;
virtual void jobC() = 0;
//这是模板模式,叫框架模式更好
void job_process1() { jobA();jobB();jobC();}
void job_process2() { jobC();jobB();jobA();}
};
/** @brief ElementA
* 某个元素的具体类,
* 有3个工作,但是不想直接调用.通过访问者来间接使用,这样改变访问者,也改变了功能
* */
class ElementA :public Element {
void jobA() {cout << __func__ << endl;}
void jobB() {cout << __func__ << endl;}
void jobC() {cout << __func__ << endl;}
};
class ElementB :public Element {
void jobA() {cout << __func__ << "for ElementB\n" << endl;}
void jobB() {cout << __func__ << "for ElementB\n" << endl;}
void jobC() {cout << __func__ << "for ElementB\n" << endl;}
};
class VisitorA :public Visitor{
void visit(Element* e) {
e->job_process1();
}
};
class VisitorB :public Visitor{
void visit(Element* e) {
e->job_process2();
}
};
int main(int argc , char* argv[])
{
Element* ea = new ElementA();
Element* eb = new ElementB();
Visitor* va = new VisitorA();
Visitor* vb = new VisitorB();
ea->accept(va); //实际调用了继承的job_process1,用访问者做了一层封装
ea->accept(vb);
eb->accept(va);
eb->accept(vb);
return 0;
}
命令模式的精髓所在:把命令的调用者与执行者分开,使双方不必关心对方是如何操作的。
本意是是要执行:
receive->do_sth();
现在通过各种封装.
并且invoker可以选择action不同command, command可以excute不同的do_sth.
更有层次感,有莫有?
invoker->action() ===> command->excute() ===>receiver->do_sth();
其实c里面一个函数就搞定了
process_command(int cmd) { //invoker
switch(cmd) { //commander
case 1: job1();break; //receiver1
case 2: job2();break; //receiver2
case 3: job3();break;
}
}
多态的直接应用
基类实现里,已经定义了虚函数vfun1(),vfun2(),vfun3()…
现在通过一个fun把虚函数组合起来,子类直接实现虚函数后,直接调用func().
fun定义了一些动作的骨架,派生类实现这些骨架即可,不用管骨.
有点像设计师,把框架定义好,流程定义好,具体流程怎么做,交给不同的施工队实现。
这个模式用的其实很多,就是可复用框架的概念,主框架相同,细节不同.
1 gstreamer的pipeline!组合框架,选择pipeline再选择各个组件
2 图像陪准,平滑-二值-分割-校准 每个步骤都可选不同的实现
3 主逻辑,A->B->C->D ABCD细节各不同
fun(){
vfunc1();
vfunc2();
vfunc3();
}
Base * b = new Son();
b->fun();
Base * b = new GrandSon();
b->fun();
策略模式与模板方法模式非常像。
模板方法里的调用算法框架的方法是在父类里实现,也就是上面的fun
,可以理解为策略。
策略模式把这个策略单独拿出来定义为抽象策略类,另外一个Context类包含一个指向策略基类的指针,用户不直接使用策略类,而是从context里选择策略.
#include
using namespace std;
//定义两种访问者,分别访问不同的元素
class Element;
class Visitor {
public:
//定义可能要访问哪些元素
virtual void visit(Element * v) = 0;
virtual ~Visitor(){}
};
class Element {
public:
virtual ~Element(){}
//定义可能有哪些访问者来访
virtual void accept(Visitor* v) {
v->visit(this);}
virtual void jobA() = 0;
virtual void jobB() = 0;
virtual void jobC() = 0;
//父类定义框架,模板模式,叫框架模式更好
//void job_process1() { jobA();jobB();jobC();}
//void job_process2() { jobC();jobB();jobA();}
};
class Strategy {
public:
Strategy(){}
Strategy(Element* e):elem(e){}
virtual void chooseStrategy(){}
virtual ~Strategy(){}
Element* getElem(){return elem;}
private:
Element* elem;
};
class StrategyA : public Strategy {
public:
StrategyA(Element* e):Strategy(e) {}
void chooseStrategy(){
getElem()->jobA();
getElem()->jobB();
getElem()->jobC();
}
StrategyA(){}
virtual ~StrategyA(){}
};
class StrategyB : public Strategy {
public:
StrategyB(Element* e):Strategy(e) {}
void chooseStrategy(){
getElem()->jobC();
getElem()->jobB();
getElem()->jobA();
}
virtual ~StrategyB(){}
};
class Context {
public:
Context(){}
~Context(){}
Context(Strategy * s):st(s) {}
void setStrategy(Strategy * s) {st = s;}
Strategy* getStrategy() {return st; }
void excute(){st->chooseStrategy();}
private:
Strategy * st;
};
/** @brief ElementA
* 某个元素的具体类,
* 有3个工作,但是不想直接调用.通过访问者来间接使用,这样改变访问者,也改变了功能
* */
class ElementA :public Element {
void jobA() {cout << __func__ << "for ElementA\n" << endl;}
void jobB() {cout << __func__ << "for ElementA\n" << endl;}
void jobC() {cout << __func__ << "for ElementA\n" << endl;}
};
class ElementB :public Element {
void jobA() {cout << __func__ << "for ElementB\n" << endl;}
void jobB() {cout << __func__ << "for ElementB\n" << endl;}
void jobC() {cout << __func__ << "for ElementB\n" << endl;}
};
int main(int argc , char* argv[])
{
Element* ea = new ElementA();
Element* eb = new ElementB();
Context * context = new Context();
StrategyA* sta = new StrategyA(ea);
context->setStrategy(sta);
context->excute();
StrategyB* stb = new StrategyB(eb);
context->setStrategy(stb);
context->excute();
return 0;
}
又是多态,继承的直接应用。
职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
感觉很像MFC里的类关系,基类里一定有个基类指针.MFC里好像是static指针.继承者把这个基类指针base指向自己的上一层次的类.虚函数vfun()通过下面的方式实现调用链:
base可以是同一个层次的也可以是不同层次的,怎么链接对象。
vfunc() { xxx; return base->func();}
例子:
#include
using namespace std;
class Ojbect {
public:
virtual void click() = 0;
virtual int show() = 0;
virtual void process(int lev) = 0;
virtual ~Ojbect(){}
Ojbect* base;
void setUpperPtr(Ojbect * b) {base = b;}
};
class View :public Ojbect{
public:
void click(){cout << "View click\n";}
int show(){cout << "View show\n"; return 0;}
void process(int lev) {
if(lev < 5) {
cout << "View handle process lvl < 5\n";
}
}
virtual ~View(){}
};
class MessageBox :public View{
public:
void click(){cout << "MessageBox click\n"; base->click();}
int show(){cout << "MessageBox show\n"; return View::show();}
void process(int lev) {
if(lev > 5 && lev < 10) {
cout << "MessageBox handle process 5 < lvl < 10\n";
} else {
//View::process(lev);
base->process(lev);
}
}
virtual ~MessageBox(){}
};
class PushMessageBox :public MessageBox{
public:
void click(){cout << "PushMessageBox click\n";
if(base) base->click();}
int show(){cout << "PushMessageBox show\n"; return MessageBox::show();}
void process(int lev) {
if(lev > 10 && lev < 100) {
cout << "PushMessageBox handle process 10 < lvl < 100\n";
} else {
//MessageBox::process(lev);
base->process(lev);
}
}
virtual ~PushMessageBox(){}
};
int main(int argc , char* argv[])
{
PushMessageBox pushBox;
MessageBox msgBox;
View view;
//指针可灵活,也可以跳这层次的指向view
pushBox.setUpperPtr(&msgBox);
msgBox.setUpperPtr(&view);
pushBox.click();
//也可以指向同一层次的类
PushMessageBox pb1,pb2,pb3,pb4;
pb1.setUpperPtr(&pb2);
pb2.setUpperPtr(&pb3);
pb3.setUpperPtr(&pb4);
//循环成链...
//pb4.setUpperPtr(&pb1);
pb1.click();
//强制层次关系,跟基类指针无关
pushBox.show();
//把判断条件放在虚函数里
pushBox.process(20);
pushBox.process(8);
pushBox.process(3);
return 0;
}
这个用的比较多了,STL的组件之一。
基本类型
template<class Item>
class IteratorBase
{
public:
typedef size_type unsigned int;
virtual void begin()=0;
virtual void end()=0;
virtual void is_end()=0;
virtual void next()=0;
virtual ~Iterator(){}
};
这么好像没啥用 暂时忽略
redo undo时临时保存,游戏存档等。就是把临时状态用getXXX()的方式保存到专门的备忘录类,恢复时用setXXX()的方式恢复原来的状态。
最典型的用法
bind函数
STL容器里的stack queue priority_queue等就是dqueue list等的适配器模式实现
在遗留代码复用、类库迁移等方面非常有用。
2种实现:
类适配器
对象适配器(推荐)
#include
using namespace std;
class Target {
public:
virtual int sendData(const char* data) = 0;
virtual ~Target(){}
};
class Adaptee {
public:
int sendCommandData(const char* cmd, const char* data){
cout << "cmd = " << cmd << endl;
cout << "data = " << data << endl;
return 0;
}
};
class Adapter : public Target {
public:
Adapter(Adaptee* t):tee(t) {}
//适配Adaptee里的SendCommandData
int sendData(const char* data) {
return tee->sendCommandData("default",data);
}
private:
Adaptee* tee;
};
int main(int argc, char* argv[])
{
//将Target里的接口和Adaptee里的接口用新的Adapter来统一,复用。
Adapter* adapter = new Adapter(new Adaptee());
adapter->sendData("Adapter mode is running");
return 0 ;
}
http://www.cnblogs.com/houleixx/archive/2008/02/23/1078877.html
如果有单个变化,通过继承类+虚函数实现。当有多个变化时,变得复杂不好维护。将继承关系变为耦合关系,Bridge模式。
#include
#include
using std::string;
using std::cout;
//
class Encoder {
public:
virtual void setEncoder(const string & s) = 0;
virtual ~Encoder(){}
};
class Channel {
public:
virtual void connect() = 0;
virtual void send() = 0;
virtual ~Channel(){}
};
class JpegEncoder : public Encoder {
public:
void setEncoder(const string & s) {cout << "setup jpeg encoder " << s << "\n";}
virtual ~JpegEncoder(){}
};
class H264Encoder : public Encoder {
public:
void setEncoder(const string & s) {cout << "setup h264 encoder " << s << "\n";}
virtual ~H264Encoder(){}
};
class WifiChannel : public Channel {
public:
void connect() {cout << "Wifi connect\n";}
void send() {cout << "Wifi send\n";}
virtual ~WifiChannel(){}
};
class UsbChannel : public Channel {
public:
void connect() {cout << "Usb connect\n";}
void send() {cout << "Usb send\n";}
virtual ~UsbChannel(){}
};
class Mirror {
public:
Mirror(Channel* c, Encoder* e):chn(c),encoder(e) {}
void openChannel() { chn->connect();}
void initEncoder(const string & s) {encoder->setEncoder(s);}
private:
//bridge的核心在这,通过基类指针指向不同的派生类,抽象了具体实现,
//而且不同的变化间独立,可用不同的基类指针
Channel* chn;
Encoder* encoder;
};
int main(int argc, char* argv[])
{
Channel* c = new WifiChannel();
Encoder* e = new H264Encoder();
Mirror m(c, e);
m.openChannel();
m.initEncoder("ex1");
Channel* c1 = new UsbChannel();
Encoder* e1 = new JpegEncoder();
Mirror m1(c1, e1);
m1.openChannel();
m1.initEncoder("ex2");
return 0;
}
为子系统中的一组接口提供一个一致的界面, 外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
1.对客户屏蔽子系统组件,简化客户端调用。
2.客户端与子系统松耦合,而子系统内部的功能组件往往是紧耦合的。
有点像模板方法模式,不过那个模板方法是定义好了实现在基类里,调用的内容是虚函数。
这个外观对象或方法封装的是子系统,子系统可以是不同类,也可以是同一类的不同方法。
例子:(集团公司的例子)
http://blog.csdn.net/wuzhekai1985/article/details/6667564
实现要点:
1.组合模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
核心实现:compositBase类的容器对象如
vector< Base*>
在Add or Remove可以操作单个子结点,也可以操作子树。
2.将“客户代码与复杂的对象容器结构”解耦是组合模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复内部实现结构——发生依赖关系,从而更能“应对变化”。
3.组合模式中,是将“Add和Remove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。这里有可能违背面向对象的“单一职责原则”,但是对于这种特殊结构,这又是必须付出的代价。
透明方式:add,remove等在Component中实现
安全方式,add,remove等在Composit中实现
4.组合模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。
5. 客户端尽量不要直接调用树叶类的方法,而是借助其父类(Component)的多态性完成调用,这样可以增加代码的复用性。
动态的给对象增加额外的职责.还是基于基类指针。
所谓增加额外的职责,是继承了基类后,增加额外的成员变量,或者丰富虚函数的实现,形成新的具体类或者抽象类,后者叫装饰器。
基类指针仍然能指向装饰器,调用装饰器的方法。
跟单例模式里的懒汉模式非常像,当代理的对象new出来很复杂时,先new出代理,真正用的时候才new出真正的对象。
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();
}
};
int main()
{
Image *image = new BigImageProxy("proxy.jpg"); //代理
image->Show(); //需要时由代理负责打开
delete image;
return 0;
}
使得客户端程序可以访问在远程主机上的对象,远程主机可能具有更好的计算性能与处理速度,可以快速响应并处理客户端的请求。远程代理可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户端完全可以认为被代理的远程业务对象是在本地而不是在远程,而远程代理对象承担了大部分的网络通信工作,并负责对远程业务方法的调用
WebService
WebService的工作调用原理:对客户端而言,我们给这各类WebService客户端API传递wsdl文件的url地址,这些API就会创建出底层的代理类,我调用这些代理,就可以访问到webservice服务
智能指针就是经典的代理
缓冲代理(Cache Proxy)也是一种较为常用的代理模式,它为某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,从而可以避免某些方法的重复执行,优化系统性能。