C++ 编程:代码模式设计
软件设计模式是帮助构建系统设计的抽象。虽然不是新的,因为这个概念已经被克里斯托弗·亚历山大在他的建筑理论中描述过,它只是由于设计模式:可重用面向对象软件的元素一书在 1994 年 10 月由Erich Gamma、Richard Helm、Ralph Johnson 和John Vlissides出版,被称为四人帮 (GoF),该书识别并描述了 23 种经典软件设计模式。
设计模式既不是静态解决方案,也不是算法。模式是一种描述和命名重复解决方案或方法来解决常见设计问题的方法,也就是说,一种解决通用问题的常见方法(模式的通用性或特定性取决于目标的限制程度)。模式可以自发产生或设计产生。这就是为什么设计模式作为实现的抽象和设计阶段的帮助而有用。有了这个概念,提供了一种更容易促进设计选择沟通的方法,作为规范化技术,以便每个人都可以分享设计理念。
根据它们解决的设计问题,设计模式可以分为不同的类别,其中主要类别是
模式通常存在于面向对象的编程语言(如 C++ 或 Java)中。它们可以被视为解决在许多不同情况或应用程序中出现的问题的模板。它不是代码重用,因为它通常不指定代码,但代码可以很容易地从设计模式创建。面向对象的设计模式通常显示类或对象之间的关系和交互,而不指定最终涉及的应用程序类或对象。
每个设计模式都包含以下部分
- 问题/需求
- 要使用设计模式,我们需要进行一个可能被编码以测试解决方案的迷你分析设计。本节说明我们要解决的问题的需求。这通常是一个常见问题,将在多个应用程序中出现。
- 力量
- 本节说明技术边界,这些边界有助于并指导解决方案的创建。
- 解决方案
- 本节描述如何编写代码来解决上述问题。这是设计模式的设计部分。它可能包含类图、时序图,或任何其他描述如何编码解决方案所需的内容。
设计模式可以被认为是解决特定设计问题的普遍认可的最佳实践的标准化。应该将它们理解为在应用程序中实施良好设计模式的方法。这样做将减少使用低效和模糊的解决方案。使用设计模式可以加快您的设计速度,并帮助您将其传达给其他程序员。
在软件工程中,创建设计模式是处理对象创建机制的设计模式,试图以适合情况的方式创建对象。对象的创建基本形式会导致设计问题或增加设计复杂性。创建设计模式通过某种方式控制此对象创建来解决此问题。
在本节中,我们假设读者已经熟悉函数、全局变量、栈与堆、类、指针和静态成员函数,这些内容在之前已经介绍过。
正如我们将会看到的那样,有几种创建设计模式,它们都将处理特定的实现任务,这将为代码库创建更高的抽象级别,现在我们将分别介绍每一种。
建造者创建模式用于将复杂对象的构建与其表示分离,以便同一个构建过程可以创建不同的对象表示。
- 问题
- 我们想要构建一个复杂的对象,但是我们不想有一个复杂的构造函数成员,或者一个需要很多参数的构造函数成员。
- 解决方案
- 定义一个中间对象,其成员函数在对象可供客户端使用之前按部分定义所需的对象。建造者模式允许我们在指定所有创建选项之前推迟对象的构建。
#include <string>
#include <iostream>
#include <memory>
using namespace std;
// "Product"
class Pizza
{
public:
void setDough(const string& dough)
{
m_dough = dough;
}
void setSauce(const string& sauce)
{
m_sauce = sauce;
}
void setTopping(const string& topping)
{
m_topping = topping;
}
void open() const
{
cout << "Pizza with " << m_dough << " dough, " << m_sauce << " sauce and "
<< m_topping << " topping. Mmm." << endl;
}
private:
string m_dough;
string m_sauce;
string m_topping;
};
// "Abstract Builder"
class PizzaBuilder
{
public:
virtual ~PizzaBuilder() {};
Pizza* getPizza()
{
return m_pizza.get();
}
void createNewPizzaProduct()
{
m_pizza = make_unique<Pizza>();
}
virtual void buildDough() = 0;
virtual void buildSauce() = 0;
virtual void buildTopping() = 0;
protected:
unique_ptr<Pizza> m_pizza;
};
//----------------------------------------------------------------
class HawaiianPizzaBuilder : public PizzaBuilder
{
public:
virtual ~HawaiianPizzaBuilder() {};
virtual void buildDough()
{
m_pizza->setDough("cross");
}
virtual void buildSauce()
{
m_pizza->setSauce("mild");
}
virtual void buildTopping()
{
m_pizza->setTopping("ham+pineapple");
}
};
class SpicyPizzaBuilder : public PizzaBuilder
{
public:
virtual ~SpicyPizzaBuilder() {};
virtual void buildDough()
{
m_pizza->setDough("pan baked");
}
virtual void buildSauce()
{
m_pizza->setSauce("hot");
}
virtual void buildTopping()
{
m_pizza->setTopping("pepperoni+salami");
}
};
//----------------------------------------------------------------
class Cook
{
public:
void openPizza()
{
m_pizzaBuilder->getPizza()->open();
}
void makePizza(PizzaBuilder* pb)
{
m_pizzaBuilder = pb;
m_pizzaBuilder->createNewPizzaProduct();
m_pizzaBuilder->buildDough();
m_pizzaBuilder->buildSauce();
m_pizzaBuilder->buildTopping();
}
private:
PizzaBuilder* m_pizzaBuilder;
};
int main()
{
Cook cook;
HawaiianPizzaBuilder hawaiianPizzaBuilder;
SpicyPizzaBuilder spicyPizzaBuilder;
cook.makePizza(&hawaiianPizzaBuilder);
cook.openPizza();
cook.makePizza(&spicyPizzaBuilder);
cook.openPizza();
}
你也可以使用最新的 c++17 标准
#include <iostream>
#include <memory>
class Pizza{
public:
void setDough(const std::string& dough){
m_dough = dough;
}
void setSauce(const std::string& sauce){
m_sauce = sauce;
}
void setTopping(const std::string& topping){
m_topping = topping;
}
void open() const {
std::cout<<"The Pizza have "<<
m_dough<<" dough, "<<
m_sauce<<" sauce, "<<
m_topping<<" topping."<<
std::endl;
}
private:
std::string m_dough;
std::string m_sauce;
std::string m_topping;
};
class PizzaBuilder{
public:
virtual ~PizzaBuilder() = default;
void createNewPizza(){
m_pizza = std::make_unique<Pizza>();
}
Pizza* getPizza() {
return m_pizza.release();
}
virtual void buildDough() = 0;
virtual void buildSauce() = 0;
virtual void buildTopping() = 0;
protected:
std::unique_ptr<Pizza> m_pizza;
};
class HawaiianPizzaBuilder:public PizzaBuilder{
public:
~HawaiianPizzaBuilder() override = default;
void buildDough() override {
m_pizza->setDough("Hawaiian dough");
}
void buildSauce() override {
m_pizza->setSauce("Hawaiian sauce");
}
void buildTopping() override {
m_pizza->setTopping("Hawaiian topping");
}
};
class SpicyPizzaBuilder:public PizzaBuilder{
public:
~SpicyPizzaBuilder() override = default;
void buildDough() override {
m_pizza->setDough("Spicy dough");
}
void buildSauce() override {
m_pizza->setSauce("Spicy sauce");
}
void buildTopping() override {
m_pizza->setTopping("Spicy topping");
}
};
class Cook{
public:
void openPizza() const {
m_pizzaBuilder->getPizza()->open();
}
void createPizza(PizzaBuilder* pizzaBuilder){
m_pizzaBuilder = pizzaBuilder;
m_pizzaBuilder->createNewPizza();
m_pizzaBuilder->buildDough();
m_pizzaBuilder->buildSauce();
m_pizzaBuilder->buildTopping();
}
private:
PizzaBuilder* m_pizzaBuilder;
};
int main(){
Cook cook{};
HawaiianPizzaBuilder hawaiianPizzaBuilder;
cook.createPizza(&hawaiianPizzaBuilder);
cook.openPizza();
SpicyPizzaBuilder spicyPizzaBuilder;
cook.createPizza(&spicyPizzaBuilder);
cook.openPizza();
}
//console output
//The Pizza have Hawaiian dough dough, Hawaiian sauce sauce, Hawaiian topping topping.
//The Pizza have Spicy dough dough, Spicy sauce sauce, Spicy topping topping.
定义:一个实用类,从派生类族中创建一个类的实例。
定义:一个实用类,创建多个类族实例。它还可以返回特定组的工厂。
工厂设计模式在需要创建许多不同类型对象(所有对象都从一个共同的基类型派生)的情况下很有用。工厂方法定义了一个创建对象的方法,子类可以覆盖该方法以指定要创建的派生类型。因此,在运行时,工厂方法可以传递所需对象的描述(例如,从用户输入中读取的字符串)并返回指向该对象的新实例的基类指针。当为基类使用精心设计接口时,该模式效果最佳,因此无需强制转换返回的对象。
- 问题
- 我们想要在运行时根据某些配置或应用程序参数决定要创建哪个对象。当我们编写代码时,我们不知道应该实例化哪个类。
- 解决方案
- 定义一个用于创建对象的接口,但让子类决定要实例化哪个类。工厂方法让一个类将实例化推迟到子类。
在以下示例中,工厂方法用于在运行时创建笔记本电脑或台式电脑对象。
让我们从定义Computer
开始,这是一个抽象基类(接口)及其派生类:Laptop
和Desktop
。
class Computer
{
public:
virtual void Run() = 0;
virtual void Stop() = 0;
virtual ~Computer() {}; /* without this, you do not call Laptop or Desktop destructor in this example! */
};
class Laptop: public Computer
{
public:
void Run() override {mHibernating = false;};
void Stop() override {mHibernating = true;};
virtual ~Laptop() {}; /* because we have virtual functions, we need virtual destructor */
private:
bool mHibernating; // Whether or not the machine is hibernating
};
class Desktop: public Computer
{
public:
void Run() override {mOn = true;};
void Stop() override {mOn = false;};
virtual ~Desktop() {};
private:
bool mOn; // Whether or not the machine has been turned on
};
实际的ComputerFactory
类返回一个Computer
,给定对象的现实世界描述。
class ComputerFactory
{
public:
static Computer *NewComputer(const std::string &description)
{
if(description == "laptop")
return new Laptop;
if(description == "desktop")
return new Desktop;
return nullptr;
}
};
让我们分析一下这种设计的优点。首先,有一个编译方面的优点。如果我们将接口Computer
与工厂一起移动到一个单独的头文件中,我们就可以将NewComputer()
函数的实现移动到一个单独的实现文件中。现在NewComputer()
的实现文件是唯一需要了解派生类的文件。因此,如果对Computer
的任何派生类进行了更改,或者添加了一个新的Computer
子类型,则只需要重新编译NewComputer()
的实现文件。所有使用工厂的人只关心接口,而接口应该在应用程序的整个生命周期中保持一致。
此外,如果需要添加一个类,并且用户通过用户界面请求对象,那么可能不需要更改调用工厂的任何代码来支持额外的计算机类型。使用工厂的代码只需将新字符串传递给工厂,并让工厂完全处理新的类型。
想象一下,你正在编写一个电子游戏,你希望将来添加新的敌人类型,每个敌人都有不同的 AI 功能,并且可以以不同的方式更新。通过使用工厂方法,程序的控制器可以调用工厂来创建敌人,而无需依赖或了解敌人的实际类型。现在,未来的开发人员可以创建新的敌人,使用新的 AI 控制和新的绘图成员函数,将其添加到工厂,并创建一个调用工厂的关卡,并按名称请求敌人。将此方法与XML关卡描述结合起来,开发人员可以创建新关卡,而无需重新编译他们的程序。所有这一切,都要归功于将对象的创建与对象的用法分离。
另一个例子
#include <stdexcept>
#include <iostream>
#include <memory>
using namespace std;
class Pizza {
public:
virtual int getPrice() const = 0;
virtual ~Pizza() {}; /* without this, no destructor for derived Pizza's will be called. */
};
class HamAndMushroomPizza : public Pizza {
public:
virtual int getPrice() const { return 850; };
virtual ~HamAndMushroomPizza() {};
};
class DeluxePizza : public Pizza {
public:
virtual int getPrice() const { return 1050; };
virtual ~DeluxePizza() {};
};
class HawaiianPizza : public Pizza {
public:
virtual int getPrice() const { return 1150; };
virtual ~HawaiianPizza() {};
};
class PizzaFactory {
public:
enum PizzaType {
HamMushroom,
Deluxe,
Hawaiian
};
static unique_ptr<Pizza> createPizza(PizzaType pizzaType) {
switch (pizzaType) {
case HamMushroom: return make_unique<HamAndMushroomPizza>();
case Deluxe: return make_unique<DeluxePizza>();
case Hawaiian: return make_unique<HawaiianPizza>();
}
throw "invalid pizza type.";
}
};
/*
* Create all available pizzas and print their prices
*/
void pizza_information(PizzaFactory::PizzaType pizzatype)
{
unique_ptr<Pizza> pizza = PizzaFactory::createPizza(pizzatype);
cout << "Price of " << pizzatype << " is " << pizza->getPrice() << std::endl;
}
int main()
{
pizza_information(PizzaFactory::HamMushroom);
pizza_information(PizzaFactory::Deluxe);
pizza_information(PizzaFactory::Hawaiian);
}
原型
[edit | edit source]当要创建的对象类型由原型实例确定时,原型模式用于软件开发中,原型实例被克隆以生成新对象。例如,当以标准方式(例如,使用new
关键字)创建新对象的固有成本对于给定应用程序来说过高时,使用此模式。
实现:声明一个抽象基类,该类指定一个纯虚拟clone()
方法。任何需要“多态构造函数”功能的类都从抽象基类派生,并实现clone()
操作。
这里,客户端代码首先调用工厂方法。此工厂方法根据参数找出具体的类。在这个具体的类上,调用clone()
方法,并由工厂方法返回对象。
- 这是一个原型方法的示例实现。这里我们对所有组件都有详细的描述。
/** Implementation of Prototype Method **/
#include <iostream>
#include <unordered_map>
#include <string>
#include <memory>
using namespace std;
/** Record is the base Prototype */
class Record
{
public:
virtual ~Record() {}
virtual void print() = 0;
virtual unique_ptr<Record> clone() = 0;
};
/** CarRecord is a Concrete Prototype */
class CarRecord : public Record
{
private:
string m_carName;
int m_ID;
public:
CarRecord(string carName, int ID) : m_carName(carName), m_ID(ID)
{
}
void print() override
{
cout << "Car Record" << endl
<< "Name : " << m_carName << endl
<< "Number: " << m_ID << endl << endl;
}
unique_ptr<Record> clone() override
{
return make_unique<CarRecord>(*this);
}
};
/** BikeRecord is the Concrete Prototype */
class BikeRecord : public Record
{
private:
string m_bikeName;
int m_ID;
public:
BikeRecord(string bikeName, int ID) : m_bikeName(bikeName), m_ID(ID)
{
}
void print() override
{
cout << "Bike Record" << endl
<< "Name : " << m_bikeName << endl
<< "Number: " << m_ID << endl << endl;
}
unique_ptr<Record> clone() override
{
return make_unique<BikeRecord>(*this);
}
};
/** PersonRecord is the Concrete Prototype */
class PersonRecord : public Record
{
private:
string m_personName;
int m_age;
public:
PersonRecord(string personName, int age) : m_personName(personName), m_age(age)
{
}
void print() override
{
cout << "Person Record" << endl
<< "Name : " << m_personName << endl
<< "Age : " << m_age << endl << endl;
}
unique_ptr<Record> clone() override
{
return make_unique<PersonRecord>(*this);
}
};
/** Opaque record type, avoids exposing concrete implementations */
enum RecordType
{
CAR,
BIKE,
PERSON
};
/** RecordFactory is the client */
class RecordFactory
{
private:
unordered_map<RecordType, unique_ptr<Record>, hash<int> > m_records;
public:
RecordFactory()
{
m_records[CAR] = make_unique<CarRecord>("Ferrari", 5050);
m_records[BIKE] = make_unique<BikeRecord>("Yamaha", 2525);
m_records[PERSON] = make_unique<PersonRecord>("Tom", 25);
}
unique_ptr<Record> createRecord(RecordType recordType)
{
return m_records[recordType]->clone();
}
};
int main()
{
RecordFactory recordFactory;
auto record = recordFactory.createRecord(CAR);
record->print();
record = recordFactory.createRecord(BIKE);
record->print();
record = recordFactory.createRecord(PERSON);
record->print();
}
另一个例子
要实现该模式,请声明一个抽象基类,该类指定一个纯虚拟clone()
成员函数。任何需要“多态构造函数”功能的类都从抽象基类派生,并实现clone()
操作。
客户端,而不是编写调用new
运算符的代码来处理硬编码的类名,而是调用原型上的clone()
成员函数,调用带有指定所需特定具体派生类的参数的工厂成员函数,或通过另一个设计模式提供的某些机制来调用clone()
成员函数。
class CPrototypeMonster
{
protected:
CString _name;
public:
CPrototypeMonster();
CPrototypeMonster( const CPrototypeMonster& copy );
virtual ~CPrototypeMonster();
virtual CPrototypeMonster* Clone() const=0; // This forces every derived class to provide an override for this function.
void Name( CString name );
CString Name() const;
};
class CGreenMonster : public CPrototypeMonster
{
protected:
int _numberOfArms;
double _slimeAvailable;
public:
CGreenMonster();
CGreenMonster( const CGreenMonster& copy );
~CGreenMonster();
virtual CPrototypeMonster* Clone() const;
void NumberOfArms( int numberOfArms );
void SlimeAvailable( double slimeAvailable );
int NumberOfArms() const;
double SlimeAvailable() const;
};
class CPurpleMonster : public CPrototypeMonster
{
protected:
int _intensityOfBadBreath;
double _lengthOfWhiplikeAntenna;
public:
CPurpleMonster();
CPurpleMonster( const CPurpleMonster& copy );
~CPurpleMonster();
virtual CPrototypeMonster* Clone() const;
void IntensityOfBadBreath( int intensityOfBadBreath );
void LengthOfWhiplikeAntenna( double lengthOfWhiplikeAntenna );
int IntensityOfBadBreath() const;
double LengthOfWhiplikeAntenna() const;
};
class CBellyMonster : public CPrototypeMonster
{
protected:
double _roomAvailableInBelly;
public:
CBellyMonster();
CBellyMonster( const CBellyMonster& copy );
~CBellyMonster();
virtual CPrototypeMonster* Clone() const;
void RoomAvailableInBelly( double roomAvailableInBelly );
double RoomAvailableInBelly() const;
};
CPrototypeMonster* CGreenMonster::Clone() const
{
return new CGreenMonster(*this);
}
CPrototypeMonster* CPurpleMonster::Clone() const
{
return new CPurpleMonster(*this);
}
CPrototypeMonster* CBellyMonster::Clone() const
{
return new CBellyMonster(*this);
}
一个具体怪物类别的客户端只需要一个对CPrototypeMonster
类对象的引用(指针)即可调用“Clone”函数并创建该对象的副本。下面的函数演示了这个概念
void DoSomeStuffWithAMonster( const CPrototypeMonster* originalMonster )
{
CPrototypeMonster* newMonster = originalMonster->Clone();
ASSERT( newMonster );
newMonster->Name("MyOwnMonster");
// Add code doing all sorts of cool stuff with the monster.
delete newMonster;
}
现在,originalMonster可以作为指向CGreenMonster、CPurpleMonster或CBellyMonster的指针传递。
单例
[edit | edit source]单例模式确保一个类只有一个实例,并提供对该实例的全局访问点。它以单例集命名,单例集被定义为包含一个元素的集合。这在需要一个对象来协调整个系统的操作时很有用。
检查清单
- 在“单例”类中定义一个私有静态属性。
- 在类中定义一个公有静态访问器函数。
- 在访问器函数中执行“延迟初始化”(首次使用时创建)。
- 定义所有构造函数为受保护或私有。
- 客户端只能使用访问器函数来操作单例。
让我们看看单例与其他变量类型的区别。
与全局变量一样,单例存在于任何函数的范围之外。传统的实现使用单例类的静态成员函数,该函数将在第一次调用时创建一个单例类的单个实例,并永远返回该实例。下面的代码示例说明了 C++ 单例类的元素,它只是存储一个字符串。
class StringSingleton
{
public:
// Some accessor functions for the class, itself
std::string GetString() const
{return mString;}
void SetString(const std::string &newStr)
{mString = newStr;}
// The magic function, which allows access to the class from anywhere
// To get the value of the instance of the class, call:
// StringSingleton::Instance().GetString();
static StringSingleton &Instance()
{
// This line only runs once, thus creating the only instance in existence
static std::auto_ptr<StringSingleton> instance( new StringSingleton );
// dereferencing the variable here, saves the caller from having to use
// the arrow operator, and removes temptation to try and delete the
// returned instance.
return *instance; // always returns the same instance
}
private:
// We need to make some given functions private to finish the definition of the singleton
StringSingleton(){} // default constructor available only to members or friends of this class
// Note that the next two functions are not given bodies, thus any attempt
// to call them implicitly will return as compiler errors. This prevents
// accidental copying of the only instance of the class.
StringSingleton(const StringSingleton &old); // disallow copy constructor
const StringSingleton &operator=(const StringSingleton &old); //disallow assignment operator
// Note that although this should be allowed,
// some compilers may not implement private destructors
// This prevents others from deleting our one single instance, which was otherwise created on the heap
~StringSingleton(){}
private: // private data for an instance of this class
std::string mString;
};
单例的变体
单例类的应用
单例设计模式的一种常见用法是用于应用程序配置。配置可能需要全局访问,并且可能需要对应用程序配置进行未来的扩展。C 的最接近的替代方案是创建一个全局struct
。这缺乏关于此对象在何处实例化的清晰度,并且不保证对象的存在。
例如,考虑另一个开发人员在他们对象的构造函数中使用你的单例的情况。然后,另一个开发人员决定在全局范围内创建一个第二个类的实例。如果你只是使用了一个全局变量,那么链接的顺序就会很重要。由于你的全局变量将被访问,可能在主函数开始执行之前,没有关于全局变量是否被初始化或第二个类的构造函数是否首先被调用的定义。这种行为可能会随着对代码其他区域的细微修改而改变,这将改变全局代码执行的顺序。这种错误很难调试。但是,使用单例,第一次访问对象时,也会创建对象。现在你有一个对象,它将始终存在,与被使用相关,并且如果从未使用过,它将永远不存在。
此类的第二个常见用途是在更新旧代码以在新的体系结构中工作。由于开发人员可能广泛使用了全局变量,因此将它们移入一个类并使其成为单例,可以作为将程序与更强大的面向对象结构保持一致的中间步骤。
另一个例子
#include <iostream>
using namespace std;
/* Place holder for thread synchronization mutex */
class Mutex
{ /* placeholder for code to create, use, and free a mutex */
};
/* Place holder for thread synchronization lock */
class Lock
{ public:
Lock(Mutex& m) : mutex(m) { /* placeholder code to acquire the mutex */ }
~Lock() { /* placeholder code to release the mutex */ }
private:
Mutex & mutex;
};
class Singleton
{ public:
static Singleton* GetInstance();
int a;
~Singleton() { cout << "In Destructor" << endl; }
private:
Singleton(int _a) : a(_a) { cout << "In Constructor" << endl; }
static Mutex mutex;
// Not defined, to prevent copying
Singleton(const Singleton& );
Singleton& operator =(const Singleton& other);
};
Mutex Singleton::mutex;
Singleton* Singleton::GetInstance()
{
Lock lock(mutex);
cout << "Get Instance" << endl;
// Initialized during first access
static Singleton inst(1);
return &inst;
}
int main()
{
Singleton* singleton = Singleton::GetInstance();
cout << "The value of the singleton: " << singleton->a << endl;
return 0;
}
结构模式
[edit | edit source]适配器
[edit | edit source]将一个类的接口转换为客户端期望的另一个接口。适配器使那些由于接口不兼容而无法协同工作的类能够协同工作。
#include <iostream>
class Dog { // Abstract Target
public:
virtual ~Dog() = default;
virtual void performsConversion() const = 0;
};
class DogFemale : public Dog { // Concrete Target
public:
virtual void performsConversion() const override { std::cout << "Dog female performs conversion." << std::endl; }
};
class Cat { // Abstract Adaptee
public:
virtual ~Cat() = default;
virtual void performsConversion() const = 0;
};
class CatFemale : public Cat { // Concrete Adaptee
public:
virtual void performsConversion() const override { std::cout << "Cat female performs conversion." << std::endl; }
};
class DogNature {
public:
void carryOutNature(Dog* dog) {
std::cout << "On with the Dog nature!" << std::endl;
dog->performsConversion();
}
};
class ConversionAdapter : public Dog { // Adapter
private:
Cat* cat;
public:
ConversionAdapter(Cat* c) : cat(c) {}
virtual void performsConversion() const override { cat->performsConversion(); }
};
int main() { // Client code
DogFemale* dogFemale = new DogFemale;
CatFemale* catFemale = new CatFemale;
DogNature dogNature;
// dogNature.carryOutNature (catFemale); // Will not compile of course since the parameter must be of type Dog*.
ConversionAdapter* adaptedCat = new ConversionAdapter(catFemale); // catFemale has adapted to become a Dog!
dogNature.carryOutNature(dogFemale);
dogNature.carryOutNature(adaptedCat); // So now catFemale, in the form of adaptedCat, participates in the dogNature!
// Note that catFemale is carrying out her own type of nature in dogNature though.
delete adaptedCat; // adaptedCat is not needed anymore
delete catFemale; // catFemale is not needed anymore
delete dogFemale; // dogFemale is not needed anymore, too
return 0;
}
桥接
[edit | edit source]桥接模式用于将接口与其实现分离。这样做提供了灵活性,使两者都可以独立变化。
以下示例将输出
API1.circle at 1:2 7.5 API2.circle at 5:7 27.5
#include <iostream>
using namespace std;
/* Implementor*/
class DrawingAPI {
public:
virtual void drawCircle(double x, double y, double radius) = 0;
virtual ~DrawingAPI() {}
};
/* Concrete ImplementorA*/
class DrawingAPI1 : public DrawingAPI {
public:
void drawCircle(double x, double y, double radius) {
cout << "API1.circle at " << x << ':' << y << ' ' << radius << endl;
}
};
/* Concrete ImplementorB*/
class DrawingAPI2 : public DrawingAPI {
public:
void drawCircle(double x, double y, double radius) {
cout << "API2.circle at " << x << ':' << y << ' ' << radius << endl;
}
};
/* Abstraction*/
class Shape {
public:
virtual ~Shape() {}
virtual void draw() = 0;
virtual void resizeByPercentage(double pct) = 0;
};
/* Refined Abstraction*/
class CircleShape : public Shape {
public:
CircleShape(double x, double y,double radius, DrawingAPI *drawingAPI) :
m_x(x), m_y(y), m_radius(radius), m_drawingAPI(drawingAPI)
{}
void draw() {
m_drawingAPI->drawCircle(m_x, m_y, m_radius);
}
void resizeByPercentage(double pct) {
m_radius *= pct;
}
private:
double m_x, m_y, m_radius;
DrawingAPI *m_drawingAPI;
};
int main(void) {
CircleShape circle1(1,2,3,new DrawingAPI1());
CircleShape circle2(5,7,11,new DrawingAPI2());
circle1.resizeByPercentage(2.5);
circle2.resizeByPercentage(2.5);
circle1.draw();
circle2.draw();
return 0;
}
组合
[edit | edit source]组合使客户端可以统一地对待单个对象和对象的组合。组合模式可以表示这两种情况。在这个模式中,可以开发树结构来表示部分-整体层次结构。
#include <vector>
#include <iostream> // std::cout
#include <memory> // std::auto_ptr
#include <algorithm> // std::for_each
using namespace std;
class Graphic
{
public:
virtual void print() const = 0;
virtual ~Graphic() {}
};
class Ellipse : public Graphic
{
public:
void print() const {
cout << "Ellipse \n";
}
};
class CompositeGraphic : public Graphic
{
public:
void print() const {
for(Graphic * a: graphicList_) {
a->print();
}
}
void add(Graphic *aGraphic) {
graphicList_.push_back(aGraphic);
}
private:
vector<Graphic*> graphicList_;
};
int main()
{
// Initialize four ellipses
const auto_ptr<Ellipse> ellipse1(new Ellipse());
const auto_ptr<Ellipse> ellipse2(new Ellipse());
const auto_ptr<Ellipse> ellipse3(new Ellipse());
const auto_ptr<Ellipse> ellipse4(new Ellipse());
// Initialize three composite graphics
const auto_ptr<CompositeGraphic> graphic(new CompositeGraphic());
const auto_ptr<CompositeGraphic> graphic1(new CompositeGraphic());
const auto_ptr<CompositeGraphic> graphic2(new CompositeGraphic());
// Composes the graphics
graphic1->add(ellipse1.get());
graphic1->add(ellipse2.get());
graphic1->add(ellipse3.get());
graphic2->add(ellipse4.get());
graphic->add(graphic1.get());
graphic->add(graphic2.get());
// Prints the complete graphic (four times the string "Ellipse")
graphic->print();
return 0;
}
装饰器
[edit | edit source]装饰器模式有助于动态地向对象附加额外的行为或职责。装饰器为扩展功能提供了一种灵活的替代子类化的方案。这也称为“包装器”。如果你的应用程序进行某种过滤,那么装饰器可能是考虑用于此任务的好模式。
#include <string>
#include <iostream>
using namespace std;
class Car //Our Abstract base class
{
protected:
string _str;
public:
Car()
{
_str = "Unknown Car";
}
virtual string getDescription()
{
return _str;
}
virtual double getCost() = 0;
virtual ~Car()
{
cout << "~Car()\n";
}
};
class OptionsDecorator : public Car //Decorator Base class
{
public:
virtual string getDescription() = 0;
virtual ~OptionsDecorator()
{
cout<<"~OptionsDecorator()\n";
}
};
class CarModel1 : public Car
{
public:
CarModel1()
{
_str = "CarModel1";
}
virtual double getCost()
{
return 31000.23;
}
~CarModel1()
{
cout<<"~CarModel1()\n";
}
};
class Navigation: public OptionsDecorator
{
Car *_b;
public:
Navigation(Car *b)
{
_b = b;
}
string getDescription()
{
return _b->getDescription() + ", Navigation";
}
double getCost()
{
return 300.56 + _b->getCost();
}
~Navigation()
{
cout << "~Navigation()\n";
delete _b;
}
};
class PremiumSoundSystem: public OptionsDecorator
{
Car *_b;
public:
PremiumSoundSystem(Car *b)
{
_b = b;
}
string getDescription()
{
return _b->getDescription() + ", PremiumSoundSystem";
}
double getCost()
{
return 0.30 + _b->getCost();
}
~PremiumSoundSystem()
{
cout << "~PremiumSoundSystem()\n";
delete _b;
}
};
class ManualTransmission: public OptionsDecorator
{
Car *_b;
public:
ManualTransmission(Car *b)
{
_b = b;
}
string getDescription()
{
return _b->getDescription()+ ", ManualTransmission";
}
double getCost()
{
return 0.30 + _b->getCost();
}
~ManualTransmission()
{
cout << "~ManualTransmission()\n";
delete _b;
}
};
int main()
{
//Create our Car that we want to buy
Car *b = new CarModel1();
cout << "Base model of " << b->getDescription() << " costs $" << b->getCost() << "\n";
//Who wants base model let's add some more features
b = new Navigation(b);
cout << b->getDescription() << " will cost you $" << b->getCost() << "\n";
b = new PremiumSoundSystem(b);
b = new ManualTransmission(b);
cout << b->getDescription() << " will cost you $" << b->getCost() << "\n";
// WARNING! Here we leak the CarModel1, Navigation and PremiumSoundSystem objects!
// Either we delete them explicitly or rewrite the Decorators to take
// ownership and delete their Cars when destroyed.
delete b;
return 0;
}
上面程序的输出是
Base model of CarModel1 costs $31000.2
CarModel1, Navigation will cost you $31300.8
CarModel1, Navigation, PremiumSoundSystem, ManualTransmission will cost you $31301.4
~ManualTransmission
~PremiumSoundSystem()
~Navigation()
~CarModel1
~Car()
~OptionsDecorator()
~Car()
~OptionsDecorator()
~Car()
~OptionsDecorator()
~Car()
另一个例子(C++14)
#include <iostream>
#include <string>
#include <memory>
class Interface {
public:
virtual ~Interface() { }
virtual void write (std::string&) = 0;
};
class Core : public Interface {
public:
~Core() {std::cout << "Core destructor called.\n";}
virtual void write (std::string& text) override {}; // Do nothing.
};
class Decorator : public Interface {
private:
std::unique_ptr<Interface> interface;
public:
Decorator (std::unique_ptr<Interface> c) {interface = std::move(c);}
virtual void write (std::string& text) override {interface->write(text);}
};
class MessengerWithSalutation : public Decorator {
private:
std::string salutation;
public:
MessengerWithSalutation (std::unique_ptr<Interface> c, const std::string& str) : Decorator(std::move(c)), salutation(str) {}
~MessengerWithSalutation() {std::cout << "Messenger destructor called.\n";}
virtual void write (std::string& text) override {
text = salutation + "\n\n" + text;
Decorator::write(text);
}
};
class MessengerWithValediction : public Decorator {
private:
std::string valediction;
public:
MessengerWithValediction (std::unique_ptr<Interface> c, const std::string& str) : Decorator(std::move(c)), valediction(str) {}
~MessengerWithValediction() {std::cout << "MessengerWithValediction destructor called.\n";}
virtual void write (std::string& text) override {
Decorator::write(text);
text += "\n\n" + valediction;
}
};
int main() {
const std::string salutation = "Greetings,";
const std::string valediction = "Sincerly, Andy";
std::string message1 = "This message is not decorated.";
std::string message2 = "This message is decorated with a salutation.";
std::string message3 = "This message is decorated with a valediction.";
std::string message4 = "This message is decorated with a salutation and a valediction.";
std::unique_ptr<Interface> messenger1 = std::make_unique<Core>();
std::unique_ptr<Interface> messenger2 = std::make_unique<MessengerWithSalutation> (std::make_unique<Core>(), salutation);
std::unique_ptr<Interface> messenger3 = std::make_unique<MessengerWithValediction> (std::make_unique<Core>(), valediction);
std::unique_ptr<Interface> messenger4 = std::make_unique<MessengerWithValediction> (std::make_unique<MessengerWithSalutation>
(std::make_unique<Core>(), salutation), valediction);
messenger1->write(message1);
std::cout << message1 << '\n';
std::cout << "\n------------------------------\n\n";
messenger2->write(message2);
std::cout << message2 << '\n';
std::cout << "\n------------------------------\n\n";
messenger3->write(message3);
std::cout << message3 << '\n';
std::cout << "\n------------------------------\n\n";
messenger4->write(message4);
std::cout << message4 << '\n';
std::cout << "\n------------------------------\n\n";
}
上面程序的输出是
This message is not decorated.
------------------------------
Greetings,
This message is decorated with a salutation.
------------------------------
This message is decorated with a valediction.
Sincerly, Andy
------------------------------
Greetings,
This message is decorated with a salutation and a valediction.
Sincerly, Andy
------------------------------
MessengerWithValediction destructor called.
Messenger destructor called.
Core destructor called.
MessengerWithValediction destructor called.
Core destructor called.
Messenger destructor called.
Core destructor called.
Core destructor called.
外观
[edit | edit source]外观模式通过提供一个统一的接口,隐藏了系统内部的复杂性,允许客户端通过该接口访问系统。外观模式定义了一个更高层次的接口,使得子系统更容易使用。例如,通过调用多个其他类的方法,将一个复杂的过程封装到一个类方法中。
/*Facade is one of the easiest patterns I think... And this is very simple example.
Imagine you set up a smart house where everything is on remote. So to turn the lights on you push lights on button - And same for TV,
AC, Alarm, Music, etc...
When you leave a house you would need to push a 100 buttons to make sure everything is off and are good to go which could be little
annoying if you are lazy like me
so I defined a Facade for leaving and coming back. (Facade functions represent buttons...) So when I come and leave I just make one
call and it takes care of everything...
*/
#include <string>
#include <iostream>
using namespace std;
class Alarm
{
public:
void alarmOn()
{
cout << "Alarm is on and house is secured"<<endl;
}
void alarmOff()
{
cout << "Alarm is off and you can go into the house"<<endl;
}
};
class Ac
{
public:
void acOn()
{
cout << "Ac is on"<<endl;
}
void acOff()
{
cout << "AC is off"<<endl;
}
};
class Tv
{
public:
void tvOn()
{
cout << "Tv is on"<<endl;
}
void tvOff()
{
cout << "TV is off"<<endl;
}
};
class HouseFacade
{
Alarm alarm;
Ac ac;
Tv tv;
public:
HouseFacade(){}
void goToWork()
{
ac.acOff();
tv.tvOff();
alarm.alarmOn();
}
void comeHome()
{
alarm.alarmOff();
ac.acOn();
tv.tvOn();
}
};
int main()
{
HouseFacade hf;
//Rather than calling 100 different on and off functions thanks to facade I only have 2 functions...
hf.goToWork();
hf.comeHome();
}
上面程序的输出是
AC is off TV is off Alarm is on and house is secured Alarm is off and you can go into the house Ac is on Tv is on
这种模式主要用于通过共享对象的属性来节省内存。想象一下,大量的相似对象,它们的大部分属性都相同。很自然地,我们将这些属性从这些对象中移到一个外部数据结构中,并为每个对象提供指向该数据结构的链接。
#include <iostream>
#include <string>
#include <vector>
#define NUMBER_OF_SAME_TYPE_CHARS 3;
/* Actual flyweight objects class (declaration) */
class FlyweightCharacter;
/*
FlyweightCharacterAbstractBuilder is a class holding the properties which are shared by
many objects. So instead of keeping these properties in those objects we keep them externally, making
objects flyweight. See more details in the comments of main function.
*/
class FlyweightCharacterAbstractBuilder {
FlyweightCharacterAbstractBuilder() {}
~FlyweightCharacterAbstractBuilder() {}
public:
static std::vector<float> fontSizes; // lets imagine that sizes may be of floating point type
static std::vector<std::string> fontNames; // font name may be of variable length (lets take 6 bytes is average)
static void setFontsAndNames();
static FlyweightCharacter createFlyweightCharacter(unsigned short fontSizeIndex,
unsigned short fontNameIndex,
unsigned short positionInStream);
};
std::vector<float> FlyweightCharacterAbstractBuilder::fontSizes(3);
std::vector<std::string> FlyweightCharacterAbstractBuilder::fontNames(3);
void FlyweightCharacterAbstractBuilder::setFontsAndNames() {
fontSizes[0] = 1.0;
fontSizes[1] = 1.5;
fontSizes[2] = 2.0;
fontNames[0] = "first_font";
fontNames[1] = "second_font";
fontNames[2] = "third_font";
}
class FlyweightCharacter {
unsigned short fontSizeIndex; // index instead of actual font size
unsigned short fontNameIndex; // index instead of font name
unsigned positionInStream;
public:
FlyweightCharacter(unsigned short fontSizeIndex, unsigned short fontNameIndex, unsigned short positionInStream):
fontSizeIndex(fontSizeIndex), fontNameIndex(fontNameIndex), positionInStream(positionInStream) {}
void print() {
std::cout << "Font Size: " << FlyweightCharacterAbstractBuilder::fontSizes[fontSizeIndex]
<< ", font Name: " << FlyweightCharacterAbstractBuilder::fontNames[fontNameIndex]
<< ", character stream position: " << positionInStream << std::endl;
}
~FlyweightCharacter() {}
};
FlyweightCharacter FlyweightCharacterAbstractBuilder::createFlyweightCharacter(unsigned short fontSizeIndex, unsigned short fontNameIndex, unsigned short positionInStream) {
FlyweightCharacter fc(fontSizeIndex, fontNameIndex, positionInStream);
return fc;
}
int main(int argc, char** argv) {
std::vector<FlyweightCharacter> chars;
FlyweightCharacterAbstractBuilder::setFontsAndNames();
unsigned short limit = NUMBER_OF_SAME_TYPE_CHARS;
for (unsigned short i = 0; i < limit; i++) {
chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(0, 0, i));
chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(1, 1, i + 1 * limit));
chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(2, 2, i + 2 * limit));
}
/*
Each char stores links to its fontName and fontSize so what we get is:
each object instead of allocating 6 bytes (convention above) for string
and 4 bytes for float allocates 2 bytes for fontNameIndex and fontSizeIndex.
That means for each char we save 6 + 4 - 2 - 2 = 6 bytes.
Now imagine we have NUMBER_OF_SAME_TYPE_CHARS = 1000 i.e. with our code
we will have 3 groups of chars with 1000 chars in each group which will save
3 * 1000 * 6 - (3 * 6 + 3 * 4) = 17970 saved bytes.
3 * 6 + 3 * 4 is a number of bytes allocated by FlyweightCharacterAbstractBuilder.
So the idea of the pattern is to move properties shared by many objects to some
external container. The objects in that case don't store the data themselves they
store only links to the data which saves memory and make the objects lighter.
The data size of properties stored externally may be significant which will save REALLY
huge amount of memory and will make each object super light in comparison to its counterpart.
That's where the name of the pattern comes from: flyweight (i.e. very light).
*/
for (unsigned short i = 0; i < chars.size(); i++) {
chars[i].print();
}
std::cin.get(); return 0;
}
代理模式为另一个对象提供一个替代者或占位符,以控制对该对象的访问。它用于用更简单的对象表示一个复杂的对象。如果对象的创建成本很高,可以将其推迟到真正需要的时候,在此期间,一个更简单的对象可以充当占位符。这个占位符对象被称为复杂对象的“代理”。
#include <iostream>
#include <memory>
class ICar {
public:
virtual ~ICar() { std::cout << "ICar destructor!" << std::endl; }
virtual void DriveCar() = 0;
};
class Car : public ICar {
public:
void DriveCar() override { std::cout << "Car has been driven!" << std::endl; }
};
class ProxyCar : public ICar {
public:
ProxyCar(int driver_age) : driver_age_(driver_age) {}
void DriveCar() override {
if (driver_age_ > 16) {
real_car_->DriveCar();
} else {
std::cout << "Sorry, the driver is too young to drive." << std::endl;
}
}
private:
std::unique_ptr<ICar> real_car_ = std::make_unique<Car>();
int driver_age_;
};
int main() {
std::unique_ptr<ICar> car = std::make_unique<ProxyCar>(16);
car->DriveCar();
car = std::make_unique<ProxyCar>(25);
car->DriveCar();
return 0;
}
这种技术更广泛地称为 Mixin。混合类在文献中被描述为表达抽象的强大工具[需要引用]。
基于接口的编程与模块化编程和面向对象编程密切相关,它将应用程序定义为一组相互耦合的模块(相互连接,并通过接口相互连接)。模块可以被分离、替换或升级,而无需影响其他模块的内容。
整个系统的复杂性大大降低。基于接口的编程在模块化编程的基础上更进一步,它要求在这些模块中添加接口。因此,整个系统被视为组件,而接口则帮助它们协同工作。
基于接口的编程提高了应用程序的模块化,从而提高了它在后期开发周期中的可维护性,尤其是在每个模块需要由不同的团队开发的情况下。这是一种久负盛名的方法,它已经存在很长时间了,并且是 CORBA 等框架背后的核心技术[需要引用]。
当第三方为已建立的系统开发额外的组件时,这一点特别方便。他们只需要开发满足父应用程序供应商指定的接口的组件即可。
因此,接口的发布者保证他不会更改接口,而订阅者同意完全实现接口,没有任何偏差。因此,接口被认为是一种契约协议,基于此的编程范式被称为“基于接口的编程”。
责任链模式的目的是通过让多个对象有机会处理请求来避免将请求的发送者与接收者耦合。它将接收对象链接起来,并将请求沿着链传递,直到某个对象处理它。
#include <iostream>
using namespace std;
class Handler {
protected:
Handler *next;
public:
Handler() {
next = NULL;
}
virtual ~Handler() { }
virtual void request(int value) = 0;
void setNextHandler(Handler *nextInLine) {
next = nextInLine;
}
};
class SpecialHandler : public Handler {
private:
int myLimit;
int myId;
public:
SpecialHandler(int limit, int id) {
myLimit = limit;
myId = id;
}
~SpecialHandler() { }
void request(int value) {
if(value < myLimit) {
cout << "Handler " << myId << " handled the request with a limit of " << myLimit << endl;
} else if(next != NULL) {
next->request(value);
} else {
cout << "Sorry, I am the last handler (" << myId << ") and I can't handle the request." << endl;
}
}
};
int main () {
Handler *h1 = new SpecialHandler(10, 1);
Handler *h2 = new SpecialHandler(20, 2);
Handler *h3 = new SpecialHandler(30, 3);
h1->setNextHandler(h2);
h2->setNextHandler(h3);
h1->request(18);
h1->request(40);
delete h1;
delete h2;
delete h3;
return 0;
}
命令模式是一种对象行为模式,它通过将请求封装为一个对象来解耦发送者和接收者,从而允许您用不同的请求参数化客户端,对请求进行排队或记录,并支持可撤消的操作。它也可以被认为是回调方法的面向对象等效方法。
回调:它是一个在用户操作后在稍后时间注册的函数。
#include <iostream>
using namespace std;
/*the Command interface*/
class Command
{
public:
virtual void execute()=0;
};
/*Receiver class*/
class Light {
public:
Light() { }
void turnOn()
{
cout << "The light is on" << endl;
}
void turnOff()
{
cout << "The light is off" << endl;
}
};
/*the Command for turning on the light*/
class FlipUpCommand: public Command
{
public:
FlipUpCommand(Light& light):theLight(light)
{
}
virtual void execute()
{
theLight.turnOn();
}
private:
Light& theLight;
};
/*the Command for turning off the light*/
class FlipDownCommand: public Command
{
public:
FlipDownCommand(Light& light) :theLight(light)
{
}
virtual void execute()
{
theLight.turnOff();
}
private:
Light& theLight;
};
class Switch {
public:
Switch(Command& flipUpCmd, Command& flipDownCmd)
:flipUpCommand(flipUpCmd),flipDownCommand(flipDownCmd)
{
}
void flipUp()
{
flipUpCommand.execute();
}
void flipDown()
{
flipDownCommand.execute();
}
private:
Command& flipUpCommand;
Command& flipDownCommand;
};
/*The test class or client*/
int main()
{
Light lamp;
FlipUpCommand switchUp(lamp);
FlipDownCommand switchDown(lamp);
Switch s(switchUp, switchDown);
s.flipUp();
s.flipDown();
}
给定一种语言,定义其语法的表示形式,以及使用该表示形式来解释该语言中的句子的解释器。
#include <iostream>
#include <string>
#include <map>
#include <list>
namespace wikibooks_design_patterns
{
// based on the Java sample around here
typedef std::string String;
struct Expression;
typedef std::map<String,Expression*> Map;
typedef std::list<Expression*> Stack;
struct Expression {
virtual int interpret(Map variables) = 0;
virtual ~Expression() {}
};
class Number : public Expression {
private:
int number;
public:
Number(int number) { this->number = number; }
int interpret(Map variables) { return number; }
};
class Plus : public Expression {
Expression* leftOperand;
Expression* rightOperand;
public:
Plus(Expression* left, Expression* right) {
leftOperand = left;
rightOperand = right;
}
~Plus(){
delete leftOperand;
delete rightOperand;
}
int interpret(Map variables) {
return leftOperand->interpret(variables) + rightOperand->interpret(variables);
}
};
class Minus : public Expression {
Expression* leftOperand;
Expression* rightOperand;
public:
Minus(Expression* left, Expression* right) {
leftOperand = left;
rightOperand = right;
}
~Minus(){
delete leftOperand;
delete rightOperand;
}
int interpret(Map variables) {
return leftOperand->interpret(variables) - rightOperand->interpret(variables);
}
};
class Variable : public Expression {
String name;
public:
Variable(String name) { this->name = name; }
int interpret(Map variables) {
if(variables.end() == variables.find(name)) return 0;
return variables[name]->interpret(variables);
}
};
// While the interpreter pattern does not address parsing, a parser is provided for completeness.
class Evaluator : public Expression {
Expression* syntaxTree;
public:
Evaluator(String expression){
Stack expressionStack;
size_t last = 0;
for (size_t next = 0; String::npos != last; last = (String::npos == next) ? next : (1+next)) {
next = expression.find(' ', last);
String token( expression.substr(last, (String::npos == next) ? (expression.length()-last) : (next-last)));
if (token == "+") {
Expression* right = expressionStack.back(); expressionStack.pop_back();
Expression* left = expressionStack.back(); expressionStack.pop_back();
Expression* subExpression = new Plus(right, left);
expressionStack.push_back( subExpression );
}
else if (token == "-") {
// it's necessary remove first the right operand from the stack
Expression* right = expressionStack.back(); expressionStack.pop_back();
// ..and after the left one
Expression* left = expressionStack.back(); expressionStack.pop_back();
Expression* subExpression = new Minus(left, right);
expressionStack.push_back( subExpression );
}
else
expressionStack.push_back( new Variable(token) );
}
syntaxTree = expressionStack.back(); expressionStack.pop_back();
}
~Evaluator() {
delete syntaxTree;
}
int interpret(Map context) {
return syntaxTree->interpret(context);
}
};
}
void main()
{
using namespace wikibooks_design_patterns;
Evaluator sentence("w x z - +");
static
const int sequences[][3] = {
{5, 10, 42}, {1, 3, 2}, {7, 9, -5},
};
for (size_t i = 0; sizeof(sequences)/sizeof(sequences[0]) > i; ++i) {
Map variables;
variables["w"] = new Number(sequences[i][0]);
variables["x"] = new Number(sequences[i][1]);
variables["z"] = new Number(sequences[i][2]);
int result = sentence.interpret(variables);
for (Map::iterator it = variables.begin(); variables.end() != it; ++it) delete it->second;
std::cout<<"Interpreter result: "<<result<<std::endl;
}
}
“迭代器”设计模式在 STL 中被大量使用,用于遍历各种容器。充分理解它将使开发人员能够创建高度可重用且易于理解的[需要引用]数据容器。
迭代器的基本思想是它允许遍历容器(就像指针在数组中移动一样)。但是,要访问容器的下一个元素,您不需要知道容器是如何构建的。这就是迭代器的作用。通过简单地使用迭代器提供的成员函数,您可以按照容器的预期顺序从第一个元素移动到最后一个元素。
让我们首先考虑一个传统的单维数组,用指针从头到尾移动。此示例假设您了解指针运算。请注意,以下的“it”或“itr”是“迭代器”的简写。
const int ARRAY_LEN = 42;
int *myArray = new int[ARRAY_LEN];
// Set the iterator to point to the first memory location of the array
int *arrayItr = myArray;
// Move through each element of the array, setting it equal to its position in the array
for(int i = 0; i < ARRAY_LEN; ++i)
{
// set the value of the current location in the array
*arrayItr = i;
// by incrementing the pointer, we move it to the next position in the array.
// This is easy for a contiguous memory container, since pointer arithmetic
// handles the traversal.
++arrayItr;
}
// Do not be messy, clean up after yourself
delete[] myArray;
这段代码对于数组来说运行速度很快,但我们如何遍历链表呢,因为内存不是连续的?考虑以下基本链表的实现
class IteratorCannotMoveToNext{}; // Error class
class MyIntLList
{
public:
// The Node class represents a single element in the linked list.
// The node has a next node and a previous node, so that the user
// may move from one position to the next, or step back a single
// position. Notice that the traversal of a linked list is O(N),
// as is searching, since the list is not ordered.
class Node
{
public:
Node():mNextNode(0),mPrevNode(0),mValue(0){}
Node *mNextNode;
Node *mPrevNode;
int mValue;
};
MyIntLList():mSize(0)
{}
~MyIntLList()
{
while(!Empty())
pop_front();
} // See expansion for further implementation;
int Size() const {return mSize;}
// Add this value to the end of the list
void push_back(int value)
{
Node *newNode = new Node;
newNode->mValue = value;
newNode->mPrevNode = mTail;
mTail->mNextNode = newNode;
mTail = newNode;
++mSize;
}
// Remove the value from the beginning of the list
void pop_front()
{
if(Empty())
return;
Node *tmpnode = mHead;
mHead = mHead->mNextNode;
delete tmpnode;
--mSize;
}
bool Empty()
{return mSize == 0;}
// This is where the iterator definition will go,
// but lets finish the definition of the list, first
private:
Node *mHead;
Node *mTail;
int mSize;
};
这个链表的内存不是连续的,因此不能使用指针运算。我们不想将链表的内部细节暴露给其他开发人员,迫使他们学习它们,并且会阻止我们改变它们。
这就是迭代器发挥作用的地方。公共接口使得学习容器的使用变得更容易,并将遍历逻辑隐藏起来,防止其他开发人员看到它。
让我们检查一下迭代器本身的代码。
/*
* The iterator class knows the internals of the linked list, so that it
* may move from one element to the next. In this implementation, I have
* chosen the classic traversal method of overloading the increment
* operators. More thorough implementations of a bi-directional linked
* list would include decrement operators so that the iterator may move
* in the opposite direction.
*/
class Iterator
{
public:
Iterator(Node *position):mCurrNode(position){}
// Prefix increment
const Iterator &operator++()
{
if(mCurrNode == 0 || mCurrNode->mNextNode == 0)
throw IteratorCannotMoveToNext();e
mCurrNode = mCurrNode->mNextNode;
return *this;
}
// Postfix increment
Iterator operator++(int)
{
Iterator tempItr = *this;
++(*this);
return tempItr;
}
// Dereferencing operator returns the current node, which should then
// be dereferenced for the int. TODO: Check syntax for overloading
// dereferencing operator
Node * operator*()
{return mCurrNode;}
// TODO: implement arrow operator and clean up example usage following
private:
Node *mCurrNode;
};
// The following two functions make it possible to create
// iterators for an instance of this class.
// First position for iterators should be the first element in the container.
Iterator Begin(){return Iterator(mHead);}
// Final position for iterators should be one past the last element in the container.
Iterator End(){return Iterator(0);}
有了这个实现,现在就可以在不知道容器大小或其数据组织方式的情况下,按顺序遍历每个元素,操作或仅仅访问数据。这通过 MyIntLList 类中的访问器 Begin() 和 End() 来完成。
// Create a list
MyIntLList myList;
// Add some items to the list
for(int i = 0; i < 10; ++i)
myList.push_back(i);
// Move through the list, adding 42 to each item.
for(MyIntLList::Iterator it = myList.Begin(); it != myList.End(); ++it)
(*it)->mValue += 42;
以下程序给出了带有通用模板的迭代器设计模式的实现
/************************************************************************/
/* Iterator.h */
/************************************************************************/
#ifndef MY_ITERATOR_HEADER
#define MY_ITERATOR_HEADER
#include <iterator>
#include <vector>
#include <set>
//////////////////////////////////////////////////////////////////////////
template<class T, class U>
class Iterator
{
public:
typedef typename std::vector<T>::iterator iter_type;
Iterator(U *pData):m_pData(pData){
m_it = m_pData->m_data.begin();
}
void first()
{
m_it = m_pData->m_data.begin();
}
void next()
{
m_it++;
}
bool isDone()
{
return (m_it == m_pData->m_data.end());
}
iter_type current()
{
return m_it;
}
private:
U *m_pData;
iter_type m_it;
};
template<class T, class U, class A>
class setIterator
{
public:
typedef typename std::set<T,U>::iterator iter_type;
setIterator(A *pData):m_pData(pData)
{
m_it = m_pData->m_data.begin();
}
void first()
{
m_it = m_pData->m_data.begin();
}
void next()
{
m_it++;
}
bool isDone()
{
return (m_it == m_pData->m_data.end());
}
iter_type current()
{
return m_it;
}
private:
A *m_pData;
iter_type m_it;
};
#endif
/************************************************************************/
/* Aggregate.h */
/************************************************************************/
#ifndef MY_DATACOLLECTION_HEADER
#define MY_DATACOLLECTION_HEADER
#include "Iterator.h"
template <class T>
class aggregate
{
friend class Iterator<T, aggregate>;
public:
void add(T a)
{
m_data.push_back(a);
}
Iterator<T, aggregate> *create_iterator()
{
return new Iterator<T, aggregate>(this);
}
private:
std::vector<T> m_data;
};
template <class T, class U>
class aggregateSet
{
friend class setIterator<T, U, aggregateSet>;
public:
void add(T a)
{
m_data.insert(a);
}
setIterator<T, U, aggregateSet> *create_iterator()
{
return new setIterator<T,U,aggregateSet>(this);
}
void Print()
{
copy(m_data.begin(), m_data.end(), std::ostream_iterator<T>(std::cout, "\n"));
}
private:
std::set<T,U> m_data;
};
#endif
/************************************************************************/
/* Iterator Test.cpp */
/************************************************************************/
#include <iostream>
#include <string>
#include "Aggregate.h"
using namespace std;
class Money
{
public:
Money(int a = 0): m_data(a) {}
void SetMoney(int a)
{
m_data = a;
}
int GetMoney()
{
return m_data;
}
private:
int m_data;
};
class Name
{
public:
Name(string name): m_name(name) {}
const string &GetName() const
{
return m_name;
}
friend ostream &operator<<(ostream& out, Name name)
{
out << name.GetName();
return out;
}
private:
string m_name;
};
struct NameLess
{
bool operator()(const Name &lhs, const Name &rhs) const
{
return (lhs.GetName() < rhs.GetName());
}
};
int main()
{
//sample 1
cout << "________________Iterator with int______________________________________" << endl;
aggregate<int> agg;
for (int i = 0; i < 10; i++)
agg.add(i);
Iterator< int,aggregate<int> > *it = agg.create_iterator();
for(it->first(); !it->isDone(); it->next())
cout << *it->current() << endl;
//sample 2
aggregate<Money> agg2;
Money a(100), b(1000), c(10000);
agg2.add(a);
agg2.add(b);
agg2.add(c);
cout << "________________Iterator with Class Money______________________________" << endl;
Iterator<Money, aggregate<Money> > *it2 = agg2.create_iterator();
for (it2->first(); !it2->isDone(); it2->next())
cout << it2->current()->GetMoney() << endl;
//sample 3
cout << "________________Set Iterator with Class Name______________________________" << endl;
aggregateSet<Name, NameLess> aset;
aset.add(Name("Qmt"));
aset.add(Name("Bmt"));
aset.add(Name("Cmt"));
aset.add(Name("Amt"));
setIterator<Name, NameLess, aggregateSet<Name, NameLess> > *it3 = aset.create_iterator();
for (it3->first(); !it3->isDone(); it3->next())
cout << (*it3->current()) << endl;
}
控制台输出
________________Iterator with int______________________________________ 0 1 2 3 4 5 6 7 8 9 ________________Iterator with Class Money______________________________ 100 1000 10000 ________________Set Iterator with Class Name___________________________ Amt Bmt Cmt Qmt
定义一个封装一组对象如何交互的对象。中介者模式通过阻止对象显式引用彼此来促进松散耦合,并且允许您独立地改变它们的交互。
#include <iostream>
#include <string>
#include <list>
class MediatorInterface;
class ColleagueInterface {
std::string name;
public:
ColleagueInterface (const std::string& newName) : name (newName) {}
std::string getName() const {return name;}
virtual void sendMessage (const MediatorInterface&, const std::string&) const = 0;
virtual void receiveMessage (const ColleagueInterface*, const std::string&) const = 0;
};
class Colleague : public ColleagueInterface {
public:
using ColleagueInterface::ColleagueInterface;
virtual void sendMessage (const MediatorInterface&, const std::string&) const override;
private:
virtual void receiveMessage (const ColleagueInterface*, const std::string&) const override;
};
class MediatorInterface {
private:
std::list<ColleagueInterface*> colleagueList;
public:
const std::list<ColleagueInterface*>& getColleagueList() const {return colleagueList;}
virtual void distributeMessage (const ColleagueInterface*, const std::string&) const = 0;
virtual void registerColleague (ColleagueInterface* colleague) {colleagueList.emplace_back (colleague);}
};
class Mediator : public MediatorInterface {
virtual void distributeMessage (const ColleagueInterface*, const std::string&) const override;
};
void Colleague::sendMessage (const MediatorInterface& mediator, const std::string& message) const {
mediator.distributeMessage (this, message);
}
void Colleague::receiveMessage (const ColleagueInterface* sender, const std::string& message) const {
std::cout << getName() << " received the message from " << sender->getName() << ": " << message << std::endl;
}
void Mediator::distributeMessage (const ColleagueInterface* sender, const std::string& message) const {
for (const ColleagueInterface* x : getColleagueList())
if (x != sender) // Do not send the message back to the sender
x->receiveMessage (sender, message);
}
int main() {
Colleague *bob = new Colleague ("Bob"), *sam = new Colleague ("Sam"), *frank = new Colleague ("Frank"), *tom = new Colleague ("Tom");
Colleague* staff[] = {bob, sam, frank, tom};
Mediator mediatorStaff, mediatorSamsBuddies;
for (Colleague* x : staff)
mediatorStaff.registerColleague(x);
bob->sendMessage (mediatorStaff, "I'm quitting this job!");
mediatorSamsBuddies.registerColleague (frank); mediatorSamsBuddies.registerColleague (tom); // Sam's buddies only
sam->sendMessage (mediatorSamsBuddies, "Hooray! He's gone! Let's go for a drink, guys!");
return 0;
}
备忘录模式在不违反封装的情况下捕获并外部化对象的内部状态,以便稍后可以将对象恢复到该状态。虽然四人帮使用朋友作为实现此模式的一种方式,但它不是最佳设计[需要引用]。它也可以使用PIMPL(指向实现的指针或不透明指针)来实现。最佳用例是编辑器中的“撤销-重做”。
发起者(要保存的对象)创建自身快照作为备忘录对象,并将该引用传递给管理者对象。管理者对象保留备忘录,直到发起者希望恢复备忘录对象中记录的先前状态。
有关此模式的传统示例,请参阅memoize。
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
const std::string NAME = "Object";
template <typename T>
std::string toString (const T& t) {
std::stringstream ss;
ss << t;
return ss.str();
}
class Memento;
class Object {
private:
int value;
std::string name;
double decimal; // and suppose there are loads of other data members
public:
Object (int newValue): value (newValue), name (NAME + toString (value)), decimal ((float)value / 100) {}
void doubleValue() {value = 2 * value; name = NAME + toString (value); decimal = (float)value / 100;}
void increaseByOne() {value++; name = NAME + toString (value); decimal = (float)value / 100;}
int getValue() const {return value;}
std::string getName() const {return name;}
double getDecimal() const {return decimal;}
Memento* createMemento() const;
void reinstateMemento (Memento* mem);
};
class Memento {
private:
Object object;
public:
Memento (const Object& obj): object (obj) {}
Object snapshot() const {return object;} // want a snapshot of Object itself because of its many data members
};
Memento* Object::createMemento() const {
return new Memento (*this);
}
void Object::reinstateMemento (Memento* mem) {
*this = mem->snapshot();
}
class Command {
private:
typedef void (Object::*Action)();
Object* receiver;
Action action;
static std::vector<Command*> commandList;
static std::vector<Memento*> mementoList;
static int numCommands;
static int maxCommands;
public:
Command (Object *newReceiver, Action newAction): receiver (newReceiver), action (newAction) {}
virtual void execute() {
if (mementoList.size() < numCommands + 1)
mementoList.resize (numCommands + 1);
mementoList[numCommands] = receiver->createMemento(); // saves the last value
if (commandList.size() < numCommands + 1)
commandList.resize (numCommands + 1);
commandList[numCommands] = this; // saves the last command
if (numCommands > maxCommands)
maxCommands = numCommands;
numCommands++;
(receiver->*action)();
}
static void undo() {
if (numCommands == 0)
{
std::cout << "There is nothing to undo at this point." << std::endl;
return;
}
commandList[numCommands - 1]->receiver->reinstateMemento (mementoList[numCommands - 1]);
numCommands--;
}
void static redo() {
if (numCommands > maxCommands)
{
std::cout << "There is nothing to redo at this point." << std::endl;
return ;
}
Command* commandRedo = commandList[numCommands];
(commandRedo->receiver->*(commandRedo->action))();
numCommands++;
}
};
std::vector<Command*> Command::commandList;
std::vector<Memento*> Command::mementoList;
int Command::numCommands = 0;
int Command::maxCommands = 0;
int main()
{
int i;
std::cout << "Please enter an integer: ";
std::cin >> i;
Object *object = new Object(i);
Command *commands[3];
commands[1] = new Command(object, &Object::doubleValue);
commands[2] = new Command(object, &Object::increaseByOne);
std::cout << "0.Exit, 1.Double, 2.Increase by one, 3.Undo, 4.Redo: ";
std::cin >> i;
while (i != 0)
{
if (i == 3)
Command::undo();
else if (i == 4)
Command::redo();
else if (i > 0 && i <= 2)
commands[i]->execute();
else
{
std::cout << "Enter a proper choice: ";
std::cin >> i;
continue;
}
std::cout << " " << object->getValue() << " " << object->getName() << " " << object->getDecimal() << std::endl;
std::cout << "0.Exit, 1.Double, 2.Increase by one, 3.Undo, 4.Redo: ";
std::cin >> i;
}
}
观察者模式定义了对象之间一对多的依赖关系,以便当一个对象改变状态时,所有依赖它的对象都会被自动通知并更新。
- 问题
- 在应用程序中的一个或多个地方,我们需要了解系统事件或应用程序状态变化。我们希望有一种标准的方式来订阅监听系统事件,以及一种标准的方式来通知相关方。通知应该在相关方订阅系统事件或应用程序状态变化后自动完成。还应该有一种取消订阅的方法。
- 力量
- 观察者和被观察者可能应该由对象表示。观察者对象将由被观察者对象通知。
- 解决方案
- 订阅后,监听对象将通过方法调用方式被通知。
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
// The Abstract Observer
class ObserverBoardInterface
{
public:
virtual void update(float a,float b,float c) = 0;
};
// Abstract Interface for Displays
class DisplayBoardInterface
{
public:
virtual void show() = 0;
};
// The Abstract Subject
class WeatherDataInterface
{
public:
virtual void registerOb(ObserverBoardInterface* ob) = 0;
virtual void removeOb(ObserverBoardInterface* ob) = 0;
virtual void notifyOb() = 0;
};
// The Concrete Subject
class ParaWeatherData: public WeatherDataInterface
{
public:
void SensorDataChange(float a,float b,float c)
{
m_humidity = a;
m_temperature = b;
m_pressure = c;
notifyOb();
}
void registerOb(ObserverBoardInterface* ob)
{
m_obs.push_back(ob);
}
void removeOb(ObserverBoardInterface* ob)
{
m_obs.remove(ob);
}
protected:
void notifyOb()
{
list<ObserverBoardInterface*>::iterator pos = m_obs.begin();
while (pos != m_obs.end())
{
((ObserverBoardInterface* )(*pos))->update(m_humidity,m_temperature,m_pressure);
(dynamic_cast<DisplayBoardInterface*>(*pos))->show();
++pos;
}
}
private:
float m_humidity;
float m_temperature;
float m_pressure;
list<ObserverBoardInterface* > m_obs;
};
// A Concrete Observer
class CurrentConditionBoard : public ObserverBoardInterface, public DisplayBoardInterface
{
public:
CurrentConditionBoard(ParaWeatherData& a):m_data(a)
{
m_data.registerOb(this);
}
void show()
{
cout<<"_____CurrentConditionBoard_____"<<endl;
cout<<"humidity: "<<m_h<<endl;
cout<<"temperature: "<<m_t<<endl;
cout<<"pressure: "<<m_p<<endl;
cout<<"_______________________________"<<endl;
}
void update(float h, float t, float p)
{
m_h = h;
m_t = t;
m_p = p;
}
private:
float m_h;
float m_t;
float m_p;
ParaWeatherData& m_data;
};
// A Concrete Observer
class StatisticBoard : public ObserverBoardInterface, public DisplayBoardInterface
{
public:
StatisticBoard(ParaWeatherData& a):m_maxt(-1000),m_mint(1000),m_avet(0),m_count(0),m_data(a)
{
m_data.registerOb(this);
}
void show()
{
cout<<"________StatisticBoard_________"<<endl;
cout<<"lowest temperature: "<<m_mint<<endl;
cout<<"highest temperature: "<<m_maxt<<endl;
cout<<"average temperature: "<<m_avet<<endl;
cout<<"_______________________________"<<endl;
}
void update(float h, float t, float p)
{
++m_count;
if (t>m_maxt)
{
m_maxt = t;
}
if (t<m_mint)
{
m_mint = t;
}
m_avet = (m_avet * (m_count-1) + t)/m_count;
}
private:
float m_maxt;
float m_mint;
float m_avet;
int m_count;
ParaWeatherData& m_data;
};
int main(int argc, char *argv[])
{
ParaWeatherData * wdata = new ParaWeatherData;
CurrentConditionBoard* currentB = new CurrentConditionBoard(*wdata);
StatisticBoard* statisticB = new StatisticBoard(*wdata);
wdata->SensorDataChange(10.2, 28.2, 1001);
wdata->SensorDataChange(12, 30.12, 1003);
wdata->SensorDataChange(10.2, 26, 806);
wdata->SensorDataChange(10.3, 35.9, 900);
wdata->removeOb(currentB);
wdata->SensorDataChange(100, 40, 1900);
delete statisticB;
delete currentB;
delete wdata;
return 0;
}
状态模式允许对象在内部状态改变时改变其行为。该对象将看起来像改变了其类。
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <memory>
enum Input {DUCK_DOWN, STAND_UP, JUMP, DIVE};
class Fighter;
class StandingState; class JumpingState; class DivingState;
class FighterState {
public:
static std::shared_ptr<StandingState> standing;
static std::shared_ptr<DivingState> diving;
virtual ~FighterState() = default;
virtual void handleInput (Fighter&, Input) = 0;
virtual void update (Fighter&) = 0;
};
class DuckingState : public FighterState {
private:
int chargingTime;
static const int FullRestTime = 5;
public:
DuckingState() : chargingTime(0) {}
virtual void handleInput (Fighter&, Input) override;
virtual void update (Fighter&) override;
};
class StandingState : public FighterState {
public:
virtual void handleInput (Fighter&, Input) override;
virtual void update (Fighter&) override;
};
class JumpingState : public FighterState {
private:
int jumpingHeight;
public:
JumpingState() {jumpingHeight = std::rand() % 5 + 1;}
virtual void handleInput (Fighter&, Input) override;
virtual void update (Fighter&) override;
};
class DivingState : public FighterState {
public:
virtual void handleInput (Fighter&, Input) override;
virtual void update (Fighter&) override;
};
std::shared_ptr<StandingState> FighterState::standing (new StandingState);
std::shared_ptr<DivingState> FighterState::diving (new DivingState);
class Fighter {
private:
std::string name;
std::shared_ptr<FighterState> state;
int fatigueLevel = std::rand() % 10;
public:
Fighter (const std::string& newName) : name (newName), state (FighterState::standing) {}
std::string getName() const {return name;}
int getFatigueLevel() const {return fatigueLevel;}
virtual void handleInput (Input input) {state->handleInput (*this, input);} // delegate input handling to 'state'.
void changeState (std::shared_ptr<FighterState> newState) {state = newState; updateWithNewState();}
void standsUp() {std::cout << getName() << " stands up." << std::endl;}
void ducksDown() {std::cout << getName() << " ducks down." << std::endl;}
void jumps() {std::cout << getName() << " jumps into the air." << std::endl;}
void dives() {std::cout << getName() << " makes a dive attack in the middle of the jump!" << std::endl;}
void feelsStrong() {std::cout << getName() << " feels strong!" << std::endl;}
void changeFatigueLevelBy (int change) {fatigueLevel += change; std::cout << "fatigueLevel = " << fatigueLevel << std::endl;}
private:
virtual void updateWithNewState() {state->update(*this);} // delegate updating to 'state'
};
void StandingState::handleInput (Fighter& fighter, Input input) {
switch (input) {
case STAND_UP: std::cout << fighter.getName() << " remains standing." << std::endl; return;
case DUCK_DOWN: fighter.changeState (std::shared_ptr<DuckingState> (new DuckingState)); return fighter.ducksDown();
case JUMP: fighter.jumps(); return fighter.changeState (std::shared_ptr<JumpingState> (new JumpingState));
default: std::cout << "One cannot do that while standing. " << fighter.getName() << " remains standing by default." << std::endl;
}
}
void StandingState::update (Fighter& fighter) {
if (fighter.getFatigueLevel() > 0)
fighter.changeFatigueLevelBy(-1);
}
void DuckingState::handleInput (Fighter& fighter, Input input) {
switch (input) {
case STAND_UP: fighter.changeState (FighterState::standing); return fighter.standsUp();
case DUCK_DOWN:
std::cout << fighter.getName() << " remains in ducking position, ";
if (chargingTime < FullRestTime) std::cout << "recovering in the meantime." << std::endl;
else std::cout << "fully recovered." << std::endl;
return update (fighter);
default:
std::cout << "One cannot do that while ducking. " << fighter.getName() << " remains in ducking position by default." << std::endl;
update (fighter);
}
}
void DuckingState::update (Fighter& fighter) {
chargingTime++;
std::cout << "Charging time = " << chargingTime << "." << std::endl;
if (fighter.getFatigueLevel() > 0)
fighter.changeFatigueLevelBy(-1);
if (chargingTime >= FullRestTime && fighter.getFatigueLevel() <= 3)
fighter.feelsStrong();
}
void JumpingState::handleInput (Fighter& fighter, Input input) {
switch (input) {
case DIVE: fighter.changeState (FighterState::diving); return fighter.dives();
default:
std::cout << "One cannot do that in the middle of a jump. " << fighter.getName() << " lands from his jump and is now standing again." << std::endl;
fighter.changeState (FighterState::standing);
}
}
void JumpingState::update (Fighter& fighter) {
std::cout << fighter.getName() << " has jumped " << jumpingHeight << " feet into the air." << std::endl;
if (jumpingHeight >= 3)
fighter.changeFatigueLevelBy(1);
}
void DivingState::handleInput (Fighter& fighter, Input) {
std::cout << "Regardless of what the user input is, " << fighter.getName() << " lands from his dive and is now standing again." << std::endl;
fighter.changeState (FighterState::standing);
}
void DivingState::update (Fighter& fighter) {
fighter.changeFatigueLevelBy(2);
}
int main() {
std::srand(std::time(nullptr));
Fighter rex ("Rex the Fighter"), borg ("Borg the Fighter");
std::cout << rex.getName() << " and " << borg.getName() << " are currently standing." << std::endl;
int choice;
auto chooseAction = [&choice](Fighter& fighter) {
std::cout << std::endl << DUCK_DOWN + 1 << ") Duck down " << STAND_UP + 1 << ") Stand up " << JUMP + 1
<< ") Jump " << DIVE + 1 << ") Dive in the middle of a jump" << std::endl;
std::cout << "Choice for " << fighter.getName() << "? ";
std::cin >> choice;
const Input input1 = static_cast<Input>(choice - 1);
fighter.handleInput (input1);
};
while (true) {
chooseAction (rex);
chooseAction (borg);
}
}
定义一系列算法,封装每个算法,并使它们可互换。策略模式允许算法独立于使用它的客户端而改变。
#include <iostream>
using namespace std;
class StrategyInterface
{
public:
virtual void execute() const = 0;
};
class ConcreteStrategyA: public StrategyInterface
{
public:
void execute() const override
{
cout << "Called ConcreteStrategyA execute method" << endl;
}
};
class ConcreteStrategyB: public StrategyInterface
{
public:
void execute() const override
{
cout << "Called ConcreteStrategyB execute method" << endl;
}
};
class ConcreteStrategyC: public StrategyInterface
{
public:
void execute() const override
{
cout << "Called ConcreteStrategyC execute method" << endl;
}
};
class Context
{
private:
StrategyInterface * strategy_;
public:
explicit Context(StrategyInterface *strategy):strategy_(strategy)
{
}
void set_strategy(StrategyInterface *strategy)
{
strategy_ = strategy;
}
void execute() const
{
strategy_->execute();
}
};
int main(int argc, char *argv[])
{
ConcreteStrategyA concreteStrategyA;
ConcreteStrategyB concreteStrategyB;
ConcreteStrategyC concreteStrategyC;
Context contextA(&concreteStrategyA);
Context contextB(&concreteStrategyB);
Context contextC(&concreteStrategyC);
contextA.execute(); // output: "Called ConcreteStrategyA execute method"
contextB.execute(); // output: "Called ConcreteStrategyB execute method"
contextC.execute(); // output: "Called ConcreteStrategyC execute method"
contextA.set_strategy(&concreteStrategyB);
contextA.execute(); // output: "Called ConcreteStrategyB execute method"
contextA.set_strategy(&concreteStrategyC);
contextA.execute(); // output: "Called ConcreteStrategyC execute method"
return 0;
}
通过在一个操作中定义算法的骨架,并将某些步骤推迟到子类中,模板方法模式允许子类重新定义该算法的某些步骤,而不会改变算法的结构。
#include <ctime>
#include <assert.h>
#include <iostream>
namespace wikibooks_design_patterns
{
/**
* An abstract class that is common to several games in
* which players play against the others, but only one is
* playing at a given time.
*/
class Game
{
public:
Game(): playersCount(0), movesCount(0), playerWon(-1)
{
srand( (unsigned)time( NULL));
}
/* A template method : */
void playOneGame(const int playersCount = 0)
{
if (playersCount)
{
this->playersCount = playersCount;
}
InitializeGame();
assert(this->playersCount);
int j = 0;
while (!endOfGame())
{
makePlay(j);
j = (j + 1) % this->playersCount;
if (!j)
{
++movesCount;
}
}
printWinner();
}
protected:
virtual void initializeGame() = 0;
virtual void makePlay(int player) = 0;
virtual bool endOfGame() = 0;
virtual void printWinner() = 0;
private:
void InitializeGame()
{
movesCount = 0;
playerWon = -1;
initializeGame();
}
protected:
int playersCount;
int movesCount;
int playerWon;
};
//Now we can extend this class in order
//to implement actual games:
class Monopoly: public Game {
/* Implementation of necessary concrete methods */
void initializeGame() {
// Initialize players
playersCount = rand() * 7 / RAND_MAX + 2;
// Initialize money
}
void makePlay(int player) {
// Process one turn of player
// Decide winner
if (movesCount < 20)
return;
const int chances = (movesCount > 199) ? 199 : movesCount;
const int random = MOVES_WIN_CORRECTION * rand() * 200 / RAND_MAX;
if (random < chances)
playerWon = player;
}
bool endOfGame() {
// Return true if game is over
// according to Monopoly rules
return (-1 != playerWon);
}
void printWinner() {
assert(playerWon >= 0);
assert(playerWon < playersCount);
// Display who won
std::cout<<"Monopoly, player "<<playerWon<<" won in "<<movesCount<<" moves."<<std::endl;
}
private:
enum
{
MOVES_WIN_CORRECTION = 20,
};
};
class Chess: public Game {
/* Implementation of necessary concrete methods */
void initializeGame() {
// Initialize players
playersCount = 2;
// Put the pieces on the board
}
void makePlay(int player) {
assert(player < playersCount);
// Process a turn for the player
// decide winner
if (movesCount < 2)
return;
const int chances = (movesCount > 99) ? 99 : movesCount;
const int random = MOVES_WIN_CORRECTION * rand() * 100 / RAND_MAX;
//std::cout<<random<<" : "<<chances<<std::endl;
if (random < chances)
playerWon = player;
}
bool endOfGame() {
// Return true if in Checkmate or
// Stalemate has been reached
return (-1 != playerWon);
}
void printWinner() {
assert(playerWon >= 0);
assert(playerWon < playersCount);
// Display the winning player
std::cout<<"Player "<<playerWon<<" won in "<<movesCount<<" moves."<<std::endl;
}
private:
enum
{
MOVES_WIN_CORRECTION = 7,
};
};
}
int main()
{
using namespace wikibooks_design_patterns;
Game* game = NULL;
Chess chess;
game = &chess;
for (unsigned i = 0; i < 100; ++i)
game->playOneGame();
Monopoly monopoly;
game = &monopoly;
for (unsigned i = 0; i < 100; ++i)
game->playOneGame();
return 0;
}
访问者模式将表示对对象结构的元素执行的操作,它允许您定义新的操作,而无需更改其操作元素的类。
#include <string>
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Wheel;
class Engine;
class Body;
class Car;
// interface to all car 'parts'
struct CarElementVisitor
{
virtual void visit(Wheel& wheel) const = 0;
virtual void visit(Engine& engine) const = 0;
virtual void visit(Body& body) const = 0;
virtual void visitCar(Car& car) const = 0;
};
// interface to one part
struct CarElement
{
virtual void accept(const CarElementVisitor& visitor) = 0;
};
// wheel element, there are four wheels with unique names
class Wheel : public CarElement
{
public:
explicit Wheel(const string& name) : name_(name){}
const string& getName() const
{
return name_;
}
void accept(const CarElementVisitor& visitor)
{
visitor.visit(*this);
}
private:
string name_;
};
class Engine : public CarElement
{
public:
void accept(const CarElementVisitor& visitor)
{
visitor.visit(*this);
}
};
class Body : public CarElement
{
public:
void accept(const CarElementVisitor& visitor)
{
visitor.visit(*this);
}
};
class Car
{
public:
vector<unique_ptr<CarElement>>& getElements()
{
return elements_;
}
Car() {
// assume that neither push_back nor Wheel(const string&) may throw
elements_.push_back( make_unique<Wheel>("front left") );
elements_.push_back( make_unique<Wheel>("front right") );
elements_.push_back( make_unique<Wheel>("back left") );
elements_.push_back( make_unique<Wheel>("back right") );
elements_.push_back( make_unique<Body>() );
elements_.push_back( make_unique<Engine>() );
}
private:
vector<unique_ptr<CarElement>> elements_;
};
// PrintVisitor and DoVisitor show by using a different implementation the Car class is unchanged even though the algorithm is different in PrintVisitor and DoVisitor.
class CarElementPrintVisitor : public CarElementVisitor
{
public:
void visit(Wheel& wheel) const
{
cout << "Visiting " << wheel.getName() << " wheel" << endl;
}
void visit(Engine& engine) const
{
cout << "Visiting engine" << endl;
}
void visit(Body& body) const
{
cout << "Visiting body" << endl;
}
void visitCar(Car& car) const
{
cout << endl << "Visiting car" << endl;
vector<unique_ptr<CarElement>>& elems = car.getElements();
for(auto &it : elems)
{
// this issues the callback i.e. to this from the element
it->accept(*this);
}
cout << "Visited car" << endl;
}
};
class CarElementDoVisitor : public CarElementVisitor
{
public:
// these are specific implementations added to the original object without modifying the original struct
void visit(Wheel& wheel) const
{
cout << "Kicking my " << wheel.getName() << " wheel" << endl;
}
void visit(Engine& engine) const
{
cout << "Starting my engine" << endl;
}
void visit(Body& body) const
{
cout << "Moving my body" << endl;
}
void visitCar(Car& car) const
{
cout << endl << "Starting my car" << endl;
vector<unique_ptr<CarElement>>& elems = car.getElements();
for(auto& it : elems)
{
it->accept(*this); // this issues the callback i.e. to this from the element
}
cout << "Stopped car" << endl;
}
};
int main()
{
Car car;
CarElementPrintVisitor printVisitor;
CarElementDoVisitor doVisitor;
printVisitor.visitCar(car);
doVisitor.visitCar(car);
return 0;
}
一种经常被需要维护相同数据多个视图的应用程序使用的模式。模型-视图-控制器模式直到最近[需要引用]一直是一种非常常见的模式,尤其是对于图形用户界面编程,它将代码分成 3 部分:模型、视图和控制器。
模型是实际的数据表示(例如,数组 vs 链表)或其他表示数据库的对象。视图是读取模型的接口或胖客户端 GUI。控制器提供更改或修改数据的接口,然后选择“最佳下一视图”(NBV)。
新手可能会认为这种“MVC”模型很浪费,主要是因为您在运行时使用了很多额外的对象,而看起来一个巨大的对象就足够了。但 MVC 模式成功的关键不在于编写代码,而在于维护它,并允许人们修改代码而无需改变其他太多东西。此外,请记住,不同的开发人员有不同的优势和劣势,因此围绕 MVC 进行团队构建更容易。想象一个视图团队负责制作优秀的视图,一个模型团队精通数据,一个控制器团队了解应用程序流程的全局情况,处理请求,与模型协作,并为该客户端选择最合适的下一视图。
例如:一个简单的中央数据库可以使用仅“模型”进行组织,例如,一个简单的数组。但是,在以后,使用链表可能更适用。所有数组访问都必须重新制作成各自的链表形式(例如,您将更改 myarray[5] 为 mylist.at(5) 或您使用的语言中任何等效的东西)。
好吧,如果我们遵循 MVC 模式,中央数据库将使用某种函数进行访问,例如 myarray.at(5)。如果我们将模型从数组更改为链表,我们只需要用模型更改视图,整个程序就会改变。保持接口不变,但更改其底层结构。这将允许我们比以前更自由、更快速地进行优化。
模型-视图-控制器模式的一个巨大优势显然是在实现不同的视图时能够重用应用程序的逻辑(在模型中实现)。一个很好的例子是在 Web 开发中,一个常见的任务是在现有软件中实现一个外部 API。如果 MVC 模式被干净地遵循,这只需要修改控制器,控制器可以根据用户代理请求的内容类型呈现不同类型的视图。