设计模式之模板方法模式(Template
Method)摘录
Template Method:(1)、意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template
Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
(2)、适用性:A、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。B、各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。C、控制子类扩展。
(3)、效果:模板方法是一种代码复用的基本技术。它们在类库中尤为重要,它们提取了类库中的公共行为。
(4)、注意事项:A、使用C++访问控制:在C++中,一个模板方法调用的原语操作可以被定义为保护成员。这保证它们只被模板方法调用。必须重定义的原语操作须定义为纯虚函数。模板方法自身不需被重定义;因此可以将模板方法定义为一个非虚成员函数。B、尽量减少原语操作:定义模板方法的一个重要目的是尽量减少一个子类具体实现该算法时必须重定义的那些原语操作的数目。需要重定义的操作越多,客户程序就越冗长。C、命名约定:可以给应被重定义的那些操作的名字加上一个前缀以识别它们。
(5)、相关模式:A、FactoryMethod模式常被模板方法调用。B、模板方法使用继承来改变算法的一部分。Strategy使用委托来改变整个算法。
(6)、TemplateMethod:把不变的代码部分都转移到父类中,将可变的代码用virtual留给子类重写。
(7)、Template Method模式实际上就是利用面向对象中多态的概念实现算法实现细节和高层接口的松耦合。可以看到Template
Method模式采取的是继承方式实现这一点的,由于继承是一种强约束性的条件,因此也给Template Method模式带来一些许多不方便的地方。其关键是将通用算法(逻辑)封装起来,而将算法细节让子类实现(多态)。
Template Method模式获得一种反向控制结构效果,这也是面向对象系统的分析和设计中一个原则DIP(依赖倒置:Dependency
Inversion Principles)。其含义就是父类调用子类的操作(高层模块调用低层模块的操作),低层模块实现高层模块声明的接口。这样控制权在父类(高层模块),低层模块反而要依赖高层模块。
示例代码1:
#include <iostream> #include <vector> #include <string> using namespace std; class AbstractClass { public: void Show() { cout<<"我是"<<GetName()<<endl; } protected: virtual string GetName() = 0; }; class Naruto : public AbstractClass { protected: virtual string GetName() { return "火影史上最帅的六代目----一鸣惊人naruto"; } }; class OnePice : public AbstractClass { protected: virtual string GetName() { return "我是无恶不作的大海贼----路飞"; } }; //客户端 int main() { Naruto* man = new Naruto(); man->Show(); OnePice* man2 = new OnePice(); man2->Show(); /*result 我是火影史上最帅的六代目----一鸣惊人naruto 我是我是无恶不作的大海贼----路飞 */ return 0; } |
示例代码2:
Template.h:
#ifndef _TEMPLATE_H_ #define _TEMPLATE_H_ class AbstractClass { public: virtual ~AbstractClass(); void TemplateMethod(); protected: virtual void PrimitiveOperation1() = 0; virtual void PrimitiveOperation2() = 0; AbstractClass(); private: }; class ConcreteClass1 : public AbstractClass { public: ConcreteClass1(); ~ConcreteClass1(); protected: void PrimitiveOperation1(); void PrimitiveOperation2(); private: }; class ConcreteClass2 : public AbstractClass { public: ConcreteClass2(); ~ConcreteClass2(); protected: void PrimitiveOperation1(); void PrimitiveOperation2(); private: }; #endif//~_TEMPLATE_H_ |
Template.cpp :
#include "Template.h" #include <iostream> using namespace std; AbstractClass::AbstractClass() { } AbstractClass::~AbstractClass() { } void AbstractClass::TemplateMethod() { this->PrimitiveOperation1(); this->PrimitiveOperation2(); } ConcreteClass1::ConcreteClass1() { } ConcreteClass1::~ConcreteClass1() { } void ConcreteClass1::PrimitiveOperation1() { cout<<"ConcreteClass1 ... PrimitiveOperation1"<<endl; } void ConcreteClass1::PrimitiveOperation2() { cout<<"ConcreteClass1 ... PrimitiveOperation2"<<endl; } ConcreteClass2::ConcreteClass2() { } ConcreteClass2::~ConcreteClass2() { } void ConcreteClass2::PrimitiveOperation1() { cout<<"ConcreteClass2 ... PrimitiveOperation1"<<endl; } void ConcreteClass2::PrimitiveOperation2() { cout<<"ConcreteClass2 ... PrimitiveOperation2"<<endl; } |
main.cpp:
#include "Template.h" #include <iostream> using namespace std; int main() { AbstractClass* p1 = new ConcreteClass1(); AbstractClass* p2 = new ConcreteClass2(); p1->TemplateMethod(); p2->TemplateMethod(); /*result ConcreteClass1 ... PrimitiveOperation1 ConcreteClass1 ... PrimitiveOperation2 ConcreteClass2 ... PrimitiveOperation1 ConcreteClass2 ... PrimitiveOperation2 */ return 0; } |
模板方法模式结构图:
设计模式之策略模式(Strategy)摘录
Strategy:(1)、意图: 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
(2)、适用性:
A、许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
B、需要使用一个算法的不同变体。
C、算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
D、一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
(3)、优缺点:
A、相关算法系列:Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。
B、一个替代继承的方法:继承提供了另一种支持多种算法或行为的方法。你可以直接生成一个Context类的子类,从而给它以不同的行为。但这会将行为硬行编制到Context中,而将算法的实现与Context的实现混合起来,从而使Context难以理解、难以维护和难以扩展,而且还不能动态地改变算法。最后你得到一堆相关的类,它们之间的唯一差别是它们所使用的算法或行为。将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展。
C、消除了一些条件语句:Strategy模式提供了用条件语句选择所需的行为以外的另一种选择。当不同的行为堆砌在一个类中时,很难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的Strategy类中消除了这些条件语句。
D、实现的选择:Strategy模式可以提供相同行为的不同实现。客户可以根据不同时间/空间权衡取舍要求从不同策略中进行选择。
E、客户必须了解不同的Strategy:本模式有一个潜在的缺点,就是一个客户要选择一个合适的Strategy就必须知道这些Strategy到底有何不同。此时可能不得不向客户暴露具体的实现问题。因此仅当这些不同行为变体与客户相关的行为时,才需要使用Strategy模式。
F、Strategy和Context之间的通信开销:无论各个ConcreteStrategy实现的算法是简单还是复杂,它们都共享Strategy定义的接口。因此很可能某些ConcreteStrategy不会都用到所有通过这个接口传递给它们的信息;简单的ConcreteStrategy可能不使用其中的任何信息。这就意味着有时Context会创建和初始化一些永远不会用到的参数。如果存在这样问题,那么将需要在Strategy和Context之间更进行紧密的耦合。
H、增加了对象的数目:Strategy增加了一个应用中的对象的数目。有时你可以将Strategy实现为可供各Context共享的无状态的对象来减少这一开销。任何其余的状态都由Context维护。Context在每一次对Strategy对象的请求中都将这个状态传递过去。共享的Strategy不应在各次调用之间维护状态。
(4)、Strategy:定义算法家族,分别封装起来,让它们之间可以互相替换,让算法变化,不会影响到用户。适合类中的成员以方法为主,算法经常变动;简化了单元测试(因为每个算法都有自己的类,可以通过自己的接口单独测试)。策略模式和简单工厂基本相同,但简单工厂模式只能解决对象创建问题,对于经常变动的算法应使用策略模式。
(5)、Strategy模式和Template模式要解决的问题是相同(类似)的,都是为了给业务逻辑(算法)具体实现和抽象接口之间的解耦。Strategy模式将逻辑(算法)封装到一个类(Context)里面,通过组合的方式将具体算法的实现在组合对象中实现,再通过委托的方式将抽象接口的实现委托给组合对象实现。State模式也有类似的功能。Strategy模式和Template模式实际是实现一个抽象接口的两种方式:继承和组合之间的区别。
要实现一个抽象接口,继承是一种方式:我们将抽象接口声明在基类中,将具体的实现放在具体子类中。组合(委托)是另外一种方式:我们将接口的实现放在被组合对象中,将抽象接口放在组合类中。这两种方式各有优缺点:
A、继承:优点:易于修改和扩展那些被复用的实现。缺点:破坏了封装性,继承中父类的实现细节暴露给子类了;”白盒”复用;当父类的实现更改时,其所有子类将不得不随之改变;从父类继承而来的实现在运行期间不能改变(编译期间就已经确定了)。
B、组合:优点:“黑盒”复用,因为被包含对象的内部细节对外是不可见的;封装性好;实现和抽象的依赖性很小(组合对象和被组合对象之间的依赖性小);可以在运行期间动态定义实现(通过一个指向相同类型的指针,典型的是抽象基类的指针)。缺点:系统中对象过多。
(6)、Strategy模式和State模式也有相似之处,但是State模式注重的对象在不同的状态下不同的操作。两者之间的区别就是State模式中具体实现类中一个指向Context的引用,而Strategy模式则没有。
示例代码1:
#include <iostream> using namespace std; //策略基类 class COperation { public: int m_nFirst; int m_nSecond; virtual double GetResult() { double dResult = 0; return dResult; } }; //策略具体类----加法类 class AddOperation : public COperation { public: AddOperation(int a, int b) { m_nFirst = a; m_nSecond = b; } virtual double GetResult() { return m_nFirst + m_nSecond; } }; class Context { private: COperation* op; public: Context(COperation* temp) { op = temp; } double GetResult() { return op->GetResult(); } }; //客户端 int main() { int a, b; char c; cin>>a>>b; cout<<"请输入运算符:"; cin>>c; switch (c) { case '+': { Context* context = new Context(new AddOperation(a, b)); cout<<context->GetResult()<<endl; break; } default: break; } /*result 5 8 请输入运算符:+ 13 */ return 0; } |
示例代码2:
//策略与工厂结合:客户端只需访问Context类,而不用知道其它任何类信息,实现了低耦合。 #include <iostream> using namespace std; //策略基类 class COperation { public: int m_nFirst; int m_nSecond; virtual double GetResult() { double dResult = 0; return dResult; } }; //策略具体类----加法类 class AddOperation : public COperation { public: AddOperation(int a, int b) { m_nFirst = a; m_nSecond = b; } virtual double GetResult() { return m_nFirst + m_nSecond; } }; class Context { private: COperation* op; public: Context(char cType) { switch (cType) { case '+': op = new AddOperation(5, 8); break; default: break; } } double GetResult() { return op->GetResult(); } }; //客户端 int main() { int a, b; cin>>a>>b; Context* test = new Context('+'); cout<<test->GetResult()<<endl; /*result 2 5 13 */ return 0; } |
示例代码3:
Strategy.h:
#ifndef _STRATEGY_H_ #define _STRATEGY_H_ class Strategy { public: Strategy(); virtual ~Strategy(); virtual void AlgrithmInterface() = 0; protected: private: }; class ConcreteStrategyA : public Strategy { public: ConcreteStrategyA(); virtual ~ConcreteStrategyA(); void AlgrithmInterface(); protected: private: }; class ConcreteStrategyB : public Strategy { public: ConcreteStrategyB(); virtual ~ConcreteStrategyB(); void AlgrithmInterface(); protected: private: }; #endif//~_STRATEGY_H_ |
Strategy.cpp:
#include "Strategy.h" #include <iostream> using namespace std; Strategy::Strategy() { } Strategy::~Strategy() { cout<<"~Strategy ..."<<endl; } void Strategy::AlgrithmInterface() { } ConcreteStrategyA::ConcreteStrategyA() { } ConcreteStrategyA::~ConcreteStrategyA() { cout<<"~ConcreteStrategy ..."<<endl; } void ConcreteStrategyA::AlgrithmInterface() { cout<<"test ConcreteStrategyA ..."<<endl; } ConcreteStrategyB::ConcreteStrategyB() { } ConcreteStrategyB::~ConcreteStrategyB() { cout<<"~ConcreteStrategyB ..."<<endl; } void ConcreteStrategyB::AlgrithmInterface() { cout<<"test ConcreteStrategyB ..."<<endl; } |
Context.h:
#ifndef _CONTEXT_H_ #define _CONTEXT_H_ class Strategy; /* 这个类是Strategy模式的关键,也是Strategy模式和Template模式的根本区别所在。 Strategy通过"组合"(委托)方式实现算法(实现)的异构,而Template模式则采取的是继承的方式。 这两个模式的区别也是继承和组合两种实现接口重用的方式的区别 */ class Context { public: Context(Strategy* stg); ~Context(); void DoAction(); protected: private: Strategy* _stg; }; #endif//~_CONTEXT_H_ |
Context.cpp:
#include "Context.h" #include "Strategy.h" #include <iostream> using namespace std; Context::Context(Strategy* stg) { _stg = stg; } Context::~Context() { if (!_stg) delete _stg; } void Context::DoAction() { _stg->AlgrithmInterface(); }
|
main.cpp:
#include "Context.h" #include "Strategy.h" #include <iostream> using namespace std; int main() { Strategy* ps = new ConcreteStrategyA(); Context* pc = new Context(ps); pc->DoAction(); if (NULL != pc) delete pc; /*result test ConcreteStrategyA ... */ return 0; }
|
策略模式结构图:
|