2014年9月19日~2014年9月24日(用设计模式处理变化)
- 本篇中,另一个案例。作者将描述一个当前正在工作的系统,当新的需求到来而被迫修改代码时,如何找到一个最好的途径。
- 第十四章 Strategy(策略)模式
- 选择短期最简单的途径会在最后使问题严重复杂化。。。(人的懒惰所致)
- 我们不会忽略每过一段时间为汽车更换机油。的确,我们可以不必每3000英里就更换。但也不会每30000英里再更换。。。(精辟)
- 还有一个例子,我们很多人把办公桌面当作文件柜。。。日久之后,你可懂??
- 导致项目难以维护的原因: “我们真的无法断定新的需求将怎样变化。” “如果我们视图发现事情将怎样变化,我们将永远停留在分析阶段。。。。
- 为变化而进行设计
- 预料变化将会发生,并观察将发生在哪里。。。
- “针对接口编程,而非针对实现编程” “优先考虑使用对象组合,而非类继承” “考虑你的设计中哪些是可变的”
- 案一个例来了:
- 假设我正在编写一个支持美国的销售业务的电子零售系统。大概的体系结构有一个处理销售请求的控制器对象。当用户请求一个销售订单时,这个对象做出确认,并将请求转发给SalesOrder处理。
![]()
- SalesOrder对象功能:允许GUI填写订单。。。处理税费的计算。。。处理订单。打印售货收据。。。
- 编写完程序之后,我们接收到一个新的需求:修改我现有的处理税费的方法。。。(美国之外啊)
- 容易想到解决方法:
![]()
- 这种处理变化点的方法是继承。。。违背了设计模式的基本原则哈。。。(上面有)
- 税收会变对吧? 封装它奶奶的。。。
![]()
- TaskController可以决定,SalesOrder也可以决定使用哪个税费对象。。。
![]()
- 在一个抽象类(CalcTax)中封装一个算法并交替使用这些算法。。。这就是Strategy模式(和抽象工厂方法有区别哦。。。Strategy是一次性实例化所有)
- Strategy模式:关键特征
- 意图:让你可以使用不同的业务规则或算法---缺觉与它们出现的场景。
- 问题:需要根据发出请求的客户或被处理的数据对算法做出选择。。。
- 解决方案:将算法和算法的实现相分离。。。
- 参与者与协作者:。。。
- 效果:Strategy模式定义了一系列的算法;switch语句或条件语句得到了避免;你必须以相同的方式调用所有算法。。。
- 实现:。。。
![]()
- Strategy模式唯一的缺点:我们必须创建的类的数量增加了。。。。
- 第15章 Decorator(装饰)模式
- 新的需求:为打印的销售票据添加抬头、脚注信息。。。(o(︶︿︶)o 唉,需求变过去,变过来还要不要人活?)
![]()
- 如果我们必须处理很多不同类型的抬头和脚注,每次只打印一种类型,那么我们可以考虑为抬头使用一次Strategy模式,为脚注再使用一次Strategy模式。。。
- 如果我们必须一次打印不止一个抬头或脚注。。。如果抬头或脚注的顺序需要改变呢?。。。组合的数量会迅速地膨胀。。。
- Decorator模式按照所需的合适顺序将所需的功能串在一起,从而实现对它们的控制。。。Decorator模式将“功能链”的动态构造与使用功能链的客户---本例中的SalesOrder对象---相分离。。
- Decorator意图:动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。。。
- Decorator效果:让我可以创建以Decorator对象---负责新的功能的对象---开始的一条对象“链”,并结束于最初的对象。。。
![]()
- Decorator模式就是一条对象链。。。
![]()
- 在这个案例中,SalesTicket对象就是ConcreteComponent。具体的装饰者则是抬头和脚注。。。
![]()
- 每个Dec对象都对其后的对象封装自己的新功能。。。每个Decorator对象在被装饰的功能之前(对于抬头)或之后(对于脚注)执行自己的附加功能。。。
-
1 class SalesTicket : public Component{ 2 public: 3 void prtTicket(); 4 } 5 void SalesTicket :: prtTicket(){ 6 // 7 } 8 9 class Decorator : public Component{ 10 public: 11 virtual void prtTicket(); 12 Decorator(Component *myC); 13 private: 14 Component *myComp; 15 } 16 Decorator ::Decorator(Component *myC){ 17 myComp = myC; 18 } 19 void Decorator :: prtTicket(){ 20 myComp->prtTicket(); 21 } 22 23 class Heaeder1 : public Decorator { 24 public: 25 Header1(Component *myC); 26 void prtTicket(); 27 } 28 Header1 :: Header1 (Component *myC) : Decorator(myC){} 29 void Header1 :: prtTicket() { 30 //printing header1 code 31 Decorator::prtTicket(); 32 } 33 34 35 class Heaeder2 : public Decorator { 36 public: 37 Header2(Component *myC); 38 void prtTicket(); 39 } 40 Header2 :: Header2 (Component *myC) : Decorator(myC){} 41 void Header2 :: prtTicket() { 42 //printing header2 code 43 Decorator::prtTicket(); 44 } 45 46 class Footer1 : public Decorator { 47 public: 48 Footer1(Component *myC); 49 void prtTicket(); 50 } 51 Footer1 :: Footer1 (Component *myC) : Decorator(myC){} 52 void Footer1 :: prtTicket() { 53 54 Decorator::prtTicket();//printing Footer1 code
55 } 56 57 class Footer2 : public Decorator { 58 public: 59 Footer2(Component *myC); 60 void prtTicket(); 61 } 62 Footer2 :: Footer2 (Component *myC) : Decorator(myC){} 63 void Footer2 :: prtTicket() { 64 65 Decorator::prtTicket();//printing Footer2 code
66 } 67 68 void SalesOrder::prtTicket(){
Component *myST; 69 myST = Configuration.GetSalesTicket(); 70 myST->prtTicket(); 71 } - 如果我需要这样一张销售票据:
- HEADER1
- HEADER2
- SALES TICKET
- FOOTER 1
- 那么 Configuration.GetSalesTicket()返回值为:
- return (new Header1(new Header2(new Footer1(new SalesTicket))));
- Decorator模式:关键特征
- 意图:为一个对象动态链接附加的职责。。。
- 问题:你需要使用的对象执行你要求的基本功能。。。Java基础类在I/O处理中广泛使用了Decorator模式。。
- 解决方案:允许拓展一个对象的功能,而不必借助于子类型化。。。
- 参与者与协作者:。。。。
- 效果:被添加的功能属于小的对象。好处是可以在ConcreteComponent对象的功能前或后动态添加功能。。。
- 实现:创建一个抽象类来表示原始的类和要添加到这个子类上的新功能。在装饰着类中,将“对新功能的调用”放在“紧随其后对象的调用”之前或其后。。
![]()
- 第十六章 Singleton(单件)模式和Double-Checked Locking(双重检查锁定)模式
- 只能说作者太牛逼了。。。因为本章会继续讨论电子零售案例。。。
- 保证一个类仅有一个实例,并提供一个访问它的全局访问点。。。
- Singleton模式的工作方式是:拥有一个特定的方法,这个方法备用于实例化需要的对象。。。
- 这个方法检查对象是否已经被实例化。。。返回引用
- 为了确保这是创建这个类型的对象的唯一途径,我们可以将构造函数定义为protected或private。。。
- 作者想:只对税收计算策略进行一次实例化,并且只在需要的时候。。。
-
1 #include "iostream" 2 3 using namespace std; 4 5 class A 6 { 7 private: 8 A(); 9 static A* instance; 10 public: 11 ~A(); 12 public: 13 static A* GetInstance(); 14 }; 15 16 A* A::instance = NULL; 17 18 A::A() 19 { 20 cout<<"A()\n"; 21 } 22 23 A::~A() 24 { 25 cout<<"delete ~A()\n"; 26 } 27 28 A* A::GetInstance() 29 { 30 if (instance == NULL) 31 { 32 instance = new A; //这种会调用构造函数(private A()) 33 } 34 return instance; 35 } 36 37 int main() 38 { 39 // A a; 这种会调用构造函数(public A())。。。所以失败 40 A* p=A::GetInstance(); 41 delete p; 42 return 0; 43 }
- Singleton模式:关键特征
- 意图:你只希望拥有一个对象,但不用全局对象来控制对象的实例化。。。
- 问题:几个不同的客户对象需要引用同样的对象,你希望确保自己拥有的这种对象不超过一个。。。
- 解决方案:确保一个实例
- 参与者与协作者:。。。
- 效果:客户对象不需要关心是否已经有实例存在。。。
- 实现:。。。
![]()
- 一种变体:Double-Checked Locking模式
- 这种模式仅仅应用于多线程程序中。。。
- 看起来我们需要同步。。。唯一的问题:这种同步会造成一个严重的瓶颈,因为所有的线程都必须等待检查对象是否存在。。。
-
1 #include "iostream" 2 3 using namespace std; 4 5 class A 6 { 7 private: 8 A(); 9 static A* instance; 10 public: 11 ~A(); 12 public: 13 static A* GetInstance(); 14 }; 15 16 A* A::instance = NULL; 17 18 A::A() 19 { 20 cout<<"A()\n"; 21 } 22 23 A::~A() 24 { 25 cout<<"delete ~A()\n"; 26 } 27 28 A* A::GetInstance() 29 { 30 if (instance == NULL) 31 { 32 //do sync here 33 if (instance == NULL) 34 { 35 instance = new A; //这种会调用构造函数(private A()) 36 } 37 } 38 return instance; 39 } 40 41 int main() 42 { 43 // A a; 这种会调用构造函数(public A())。。。所以失败 44 A* p=A::GetInstance(); 45 delete p; 46 return 0; 47 }
- 解决方案是:在NULL检查之后进行同步,然后再次检查成员变量以确保实例尚未被创建。。。。(为何不把同步放在第一个判定之前呢? 这种的话不就成为了瓶颈了吗?)。。。这被称为double-checked locking 模式。。。其意图是将非必须的锁定优化掉。这里的同步检查最多发生一次,因此它不会成为瓶颈。。。
- 第十七章 Observer(观察者)模式
- 作者近乎超神了。。。因为本章继续对电子零售案例继续进行研究。。。
![]()
![]()
- 第四个类别:分离型模式(大部分都是四人团中的行为性模式)---主要目的是将一个对象与另一个对象分离。动机之一是可伸缩性或增加灵活性。。。
- 好的好的好的。。。新的需求又来了。。。(没错,需求总是在不断地变化中的)。。。新的需求如下:对于新加入的消费者采取行动(发一封表示欢迎的电子邮件;按照邮局查证消费者的地址。。。)
![]()
- 硬编码??? 你可懂“需求总是在变化的”???举个例子,我们可能必须支持不同公司的欢迎信。。。
- Observer模式的意图是“定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并被自动更新”。。。
- 寻找变化点:不同类型的对象---当一个状态发生变化时,有一系列对象需要得到通知。这些对象往往属于不同的类。 不同的接口---因为这些对象属于不同的类,所以它们通常接口不同。。。
![]()
- attach(Observer)---将给定的观察者对象添加到自己的观察者列表中。。。
- detach(Observer)---从自己的观察者列表中移除给定的观察者对象。。。
- 注释:Observer对象(希望得到通知的对象);subject对象(触发事件的对象,比如:customer对象);这个案例中,attach和detach都是静态的,因为观察者希望得到所有新的Customer对象的通知。。。
- notify---遍历自己的Observer对象列表,调用其中每个其中每个Observer对象的update方法(update方法应该包含处理事件的方法)。。。
- 仅仅“通知每个观察者”还不够。。。对于事件,一个恶观察者可能需要更多的信息,而不仅仅是知道事件发生了。。。
- Observer对象在实例化的时候将自己注册到Customer类上。如果Oberver对象需要从目标对象那里获得更多的信息,update方法就必须传回调用者的一个引用。
- getState---每个Observer对象都将调用新加入的Customer对象的getState方法以获取新对象的信息,从而决定自己需要做什么。
-
#include <iostream> #include <string> #include <vector> using namespace std; class Observer; class Customer; typedef void (* Tach)(Observer *o); class Observer { public: Observer(); ~Observer(); virtual void update(Customer *mycust) = 0; void Link(Tach at,Tach dt); public: Tach attach; Tach detach; }; Observer::Observer() { attach = detach = NULL; } Observer::~Observer() { } void Observer::Link(Tach at,Tach dt) { attach = at; detach = dt; cout<<"Observer 函数绑定成功\n"; } class Customer { public: Customer(); ~Customer(); static void attach(Observer *o); static void detach(Observer *o); string getState(); void notifyObs(); private: static vector<Observer *> myObs; }; vector<Observer *> Customer::myObs; Customer::Customer() { } Customer::~Customer() { } void Customer::attach(Observer *o) { myObs.push_back(o); } void Customer::detach(Observer *o) { vector<Observer*>::iterator it; for (it=myObs.begin();it!=myObs.end();++it) { if (*it==o) { myObs.erase(it); break; } } } string Customer::getState() { string str; str=""; return str; } void Customer::notifyObs() { vector<Observer*>::iterator it; for (it=myObs.begin();it!=myObs.end();++it) { (*it)->update(this); } } class AddVerification : public Observer { public: AddVerification(); ~AddVerification(); void update(Customer *mycust); }; AddVerification::AddVerification() { } AddVerification::~AddVerification() { } void AddVerification::update(Customer *mycust) { //do cout<<"AddVerification update \n"; } class WelcomeLeter : public Observer { public: WelcomeLeter(); ~WelcomeLeter(); void update(Customer *mycust); }; WelcomeLeter::WelcomeLeter() { } WelcomeLeter::~WelcomeLeter() { } void WelcomeLeter::update(Customer *mycust) { //do cout<<"WelcomeLeter update \n"; } int main() { Customer cu; AddVerification ad; WelcomeLeter we; ad.Link(Customer::attach,Customer::detach); we.Link(Customer::attach,Customer::detach); we.attach(&we); ad.attach(&ad); cu.notifyObs(); cout<<"detach ad之后。。\n"; ad.detach(&ad); cu.notifyObs(); return 0; }
-
![]()
-
Observer模式:关键特征
-
意图:在对象之间定义一种一对多的依赖关系,这样当一个对象的状态改变时,所有依赖于它的对象都将得到通知并立即更新
-
问题:当某个事件发生时,你需要向一系列对象发出通知,而这个对象的列表是不断变化的
-
解决方案:Observer将监视某个事件的责任委托给一个中心对象:Subject
-
参与者与协作者:Observer有责任将自己注册到Subject上,有责任在得到通知时从Subject处获取更多的信息。
-
效果:如果某些Observer只对全部事件的一个子集感兴趣,那么Subject可能会Observer它们不需要知道的时间。如果在Subject通知Observer之后,Observer又转过来请求附加的信息,就可能需要额外的通信。
- 实现:。。。
-
![]()
-
不是所有的依赖关系都用到Observer模式
-
当依赖关系固定(或几乎固定)时,加入一个Observer模式可能只会增加复杂性
-
如果需要得到某个时间的通知的对象列表是变化的(或由某种条件确定的),那么Observer模式的价值就更大了。需求的变化或需要被通知的对象列表的变化都可能引起上述变化。如果系统运行在不同的环境下或由不同的用户来运行 -- -每种情况都有不同的观察者列表,Observer模式也会很有用
-
可以用Strategy模式过滤事件;还可以用Strategy过滤Observer得到的信息。。
- 第十八章 Template Method(模版方法)模式
-
我擦勒、、、本章继续对带脑子零售案例进行研究,迄今为止我们14~~17章都讨论了这个案例、。。。
-
新的需求:支持Oracle和SQL Server数据库。但是这两个系统的细节部分仍然有差别。。。
Template Method是用以帮助从不同的步骤中抽象出一个通用的过程的模式
-
四人团认为Template Method模式意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。。
-
换句话说,Oracle和SQL Server有不同的连接、查询方法,但它们都有相同的概念上的过程。。
![]()
-
Template Method模式:关键特征
-
意图:定义一个操作中的骨架,而将一些步骤延迟到子类中。Template Method模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
-
问题:需要遵循一个过程或一些列步骤,它们在某个具体层次上保持一致,但单个步骤在更详细的层次上可能有不同的实现。
-
解决方案:允许定义不同的子步骤,同时维护基本过程的一致性。
-
参与者与协作者:。。。
-
效果:。。。
-
实现:。。。
-
![]()
- 第十九章 Factory Method(工厂方法)
- 还是继续对点子零售系统案例进行研究。。。
- 在第十八章中,我们遗留了一个问题:如何实例化需要的数据库对象。。。
- 按照四人团所说,FactoryMethod意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类。FactoryMethod使一个类的实例化延迟到其子类。
- AbstractFactory模式可以由一系列FactoryMethod模式来实现。。
- 有时候有必要创建与现存的某个类结构相对应的一个分层类结构,并让新的体系包含某些代理的责任。在这种情况下,对于原来的体系中的对象很重要的是:它们需要讷讷感实例化这个分层体系中合适的对象。我们可以用FactoryMethod模式来达到这个目的。。。
- FactoryMethod被用于模式架中
- FactoryMethod:关键特征
- 意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类。将实例化延迟到其子类。
- 问题:一个类需要实例化另一个类的派生类,但不知道具体是哪个派生类。FactoryMethod允许让派生类来作出决定。
- 解决方案:一个派生类对“实例化哪个类、如何实例化”的问题作出决定
- 参与者与协作者:Product是FactoryMethod模式创建的对象的类型接口。Creator是定义工厂方法的接口。。。
- 效果:客户将需要Creator的子类创建一个特定的ConcreteProduct对象。
- 实现:。。。
![]()
- 第二十章 分析矩阵
- 这章是对14---19章做一个总结。。。
- 我们目前为止,讨论一系列的独立模式,我们发现软件开发中最大的问题:处理问题领域中的变化。
![]()
- 作者与客户打交道的经验:
- 客户通常非常了解它们的问题领域(我们永远达不到的了解程度)
- 一般情况下,客户不会像开发者那样在概念层次上表达事物。相反,他们会谈论特殊的情况。
- 当他们想表达“通常”的意思时,总喜欢“总是”这个词
- “很少”变成“绝不”
- 客户经常会说已经告诉了我们关于这种情况的所有方面,但实际上他,他们只告诉了我通常会发生的情况。。。
![]()
![]()
- 概念的变化是分析者会面对的最大的挑战之一。。。作者向我们展示了一种简单的分析工具,帮助我们理解这些变化的意义。。。
























浙公网安备 33010602011771号