1 设计模式的分类
设计模式总的来说分为三大类:
- 创建型模式;
- 结构型模式;
- 行为型模式。
2 设计模式遵循的六大原则
2.1 开闭原则(Open Close Principle)
开闭原则意味着开放扩展,关闭修改。意味着当需求变化时只扩展模块的功能,不具体的修改模块的源代码,实现一种热拔插的效果。这样做的好处是易于项目的扩展升级和维护。开闭原则的实现方式是对系统进行抽象,形成一个抽象层,当遇到新需求时可以从底层导出相应的实现。
2.2 里氏代换原则(Liskov Substitution Principle)
里氏替换原则,OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。“抽象”是语言提供的功能。“多态”由继承语义实现。 里氏替换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
2.3 依赖倒转原则(Dependence Inversion Principle)
高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
2.4 接口隔离原则(Interface Segregation Principle)
客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。基本意思是降低接口之间的耦合度,为系统的维护和升级带来便利。
2.5 迪米特法则(最少知道原则)(Demeter Principle)
又叫作最少知道原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。英文简写为: LoD。即一个软件实体应当尽可能少的与其他实体发生相互作用。每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
2.6 合成复用原则(Composite Reuse Principle)
尽量使用合成/聚合的方式,而不是使用继承。
3 具体的设计模式
3.1 创建型模式
创建型模式是处理对象创建的设计模式,试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。
创建型模式由两个主导思想构成。一是将系统使用的具体类封装起来,二是隐藏这些具体类的实例创建和结合的方式。
创建型模式又分为对象创建型模式和类创建型模式。对象创建型模式处理对象的创建,类创建型模式处理类的创建。详细地说,对象创建型模式把对象创建的一部分推迟到另一个对象中,而类创建型模式将它对象的创建推迟到子类中。
3.1.1 工厂模式
3.1.1.1 工厂方法
实现:
定义一个接口用于创建对象,但是让子类决定初始化哪个类。工厂方法把一个类的初始化下放到子类。
创建一个对象常常需要复杂的过程,所以不适合包含在一个复合对象中。创建对象可能会导致大量的重复代码,可能会需要复合对象访问不到的信息,也可能提供不了足够级别的抽象,还可能并不是复合对象概念的一部分。工厂方法模式通过定义一个单独的创建对象的方法来解决这些问题。由子类实现这个方法来创建具体类型的对象。
class button
{
};
class win_button : public button
{
};
class mac_button : public button
{
};
class factory
{
public:
virtual button *create_button() = 0;
};
class win_factory : public factory
{
public:
button *create_button()
{
return new win_button();
}
};
class mac_factory : public factory
{
public:
virtual button *create_button()
{
return new mac_button();
}
};
适用性:
工厂方法,适用于面向接口编程(programming to interface)与实现依赖反转原则。 下列情况可以考虑使用工厂方法模式:
- 创建对象需要大量重复的代码。可以把这些代码写在工厂基类中;
- 创建对象需要访问某些信息,而这些信息不应该包含在复合类中;
- 创建对象的生命周期必须集中管理,以保证在整个程序中具有一致的行为。 对象创建时会有很多参数来决定如何创建出这个对象;
- 创建对象可能是一个pool里的,不是每次都凭空创建一个新的。而pool的大小等参数可以用另外的逻辑去控制。比如连接池对象,线程池对;
- 业务对象的代码作者希望隐藏对象的真实类型,而构造函数一定要真实的类名才能;
- 简化一些常规的创建过程。根据配置去创建一个对象也很复杂;但可能95%的情况只创建某个特定类型的对象。这时可以弄个函数直接省略那些配置过程。如Java的线程池的相关创建api(;Executors.newFixedThreadPool等)
- 创建一个对象有复杂的依赖关系,比如Foo对象的创建依赖A,A又依赖B,B又依赖C……。于是创建过程是一组对象的的创建和注入;
- 知道怎么创建一个对象,但是无法把控创建的时机。需要把“如何创建”的代码塞给“负责决定什么时候创建”的代码。后者在适当的时机,回调执行创建对象的函数。在支持用函数作为一等公民传;的语言,比如js,go等,直接用创建函数就行了。对于java需要搞个XXXXFactory的类去传。
- 构造函数里不要抛出异常。
局限性:
- 重构已经存在的类会破坏客户端代码;
- 工厂方法所实例化的类具有私有的构造方法,所以这些类就不能扩展了;
- 如果确实扩展了工厂方法所实例化的类(例如将构造方法设为保护的,虽然有风险但也是可行的),子类必须具有所有工厂方法的一套实现。
通过修改底层编程语言,使工厂方法称为第一类的类成员。
3.1.1.2 简单工厂
普通的工厂方法模式通常伴随着对象的具体类型与工厂具体类型的一一对应,客户端代码根据需要选择合适的具体类型工厂使用。然而,这种选择可能包含复杂的逻辑。这时,可以创建一个单一的工厂类,用以包含这种选择逻辑,根据参数的不同选择实现不同的具体对象。这个工厂类不需要由每个具体产品实现一个自己的具体的工厂类,所以可以将工厂方法设置为静态方法。 而且,工厂方法封装了对象的创建过程。
class button
{
};
class win_button : public button
{
};
class mac_button : public button
{
};
class button_factory
{
public:
button * create_button(int type)
{
switch (type)
{
case WIN_BUTTON:
return new win_button();
break;
case MAC_BUTTON:
return new mac_button();
default:
break;
}
}
};
3.1.1.3 抽象工厂
实现:
抽象工厂模式(英语:Abstract factory pattern)为一个产品族提供了统一的创建接口。当需要这个产品族的某一系列的时候,可以从抽象工厂中选出相应的系列创建一个具体的工厂类。
“工厂”是创建产品(对象)的地方,其目的是将产品的创建与产品的使用分离。抽象工厂模式的目的,是将若干抽象产品的接口与不同主题产品的具体实现分离开。这样就能在增加新的具体工厂的时候,不用修改引用抽象工厂的客户端代码。
抽象工厂:抽象工厂模式的实质是“提供接口,创建一系列相关或独立的对象,而不指定这些对象的具体类。
class button{};
class win_button : public button{};
class mac_button : public button{};
class label{};
class win_label : public label{};
class mac_label : public label{};
class factory
{
public:
virtual button* create_button() = 0;
virtual label* create_label() = 0;
};
class mac_factory : public factory
{
public:
button * create_button(){
return new mac_button();
}
label *create_label(){
return new mac_label();
}
};
class win_factory : public factory
{
public:
button * create_button(){
return new win_button();
}
label *create_label(){
return new win_label();
}
};
适用性:
- 一个系统要独立于它的产品的创建、组合和表示时;
- 一个系统要由多个产品系列中的一个来配置时;
- 需要强调一系列相关的产品对象的设计以便进行联合使用时;
- 提供一个产品类库,而只想显示它们的接口而不是实现时。
局限性:
- 在产品族中扩展新的产品是很困难的,它需要修改抽象工厂的接口。
3.1.2 生成器模式
实现:
生成器模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用性:
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时;
- 当构造过程必须允许被构造的对象有不同的表示时。
//建造汽车
class engine
{
string type;
public:
engine(string &type) { this->type = type; }
};
class body
{
string type;
public:
body(string &type) { this->type = type; }
};
class tire
{
string type;
public:
tire(string &type) { this->type = type; }
};
class vecile
{
engine *_engine;
body *_body;
tire *_tire;
public:
void set_engine(engine *e)
{
_engine = e;
}
void set_body(body *b)
{
_body = b;
}
void set_tire(tire *t)
{
_tire = t;
}
};
class vecile_builder
{
protected:
vecile *_vecile;
public:
vecile *get_vecile() { return _vecile; }
void create_another() { _vecile = new vecile; }
virtual void build_engine() = 0;
virtual void build_body() = 0;
virtual void build_tire() = 0;
};
class car_builder : public vecile_builder
{
public:
void build_engine()
{
_vecile->set_engine("HanMa_I");
}
void build_body()
{
_vecile->set_body("Steel_I");
}
void build_tire()
{
_vecile->set_tire("Tire_I");
}
};
class truck_builder : public vecile_builder
{
public:
void build_engine()
{
_vecile->set_engine("HanMa_III");
}
void build_body()
{
_vecile->set_body("Steel_III");
}
void build_tire()
{
_vecile->set_tire("Tire_III");
}
};
class VechileFactory
{
vecile_builder *_builder;
public:
void set_builder(vecile_builder *builder)
{
_builder = builder;
}
vecile* create_product()
{
if(_builder == nullptr)
return nullptr;
_builder->create_another();
_builder->build_engine();
_builder->build_body();
_builder->build_tire();
return _builder->get_vecile();
}
};
抽象工厂模式与生成器相似,因为它也可以创建复杂对象。主要的区别是生成器模式着重于一步步构造一个复杂对象。而抽象工厂模式着重于多个系列的产品对象(简单的或是复杂的)。生成器在最后的一步返回产品,而对于抽象工厂来说,产品是立即返回的。
3.1.3 惰性初始化模式
实现:
惰性初始化模式:推迟对象的创建、数据的计算等需要耗费较多资源的操作,只有在第一次访问的时候才执行。
以设计模式的观点来说,惰性初始通常会和工厂方法模式合作,这结合了三种构想:
- 使用一个工厂去得到一个类别的实例(工厂方法模式);
- 将实例存在一个集合中,所以下次要求一个实例却有相同参数时,可以得到同一个实例(可和单例模式来做比较);
- 在第一次时,使用惰性初始来实例化物件(惰性初始模式)。
class fruit
{
static map<string, fruit*> _table;
public:
fruit* get_fruit(const string &type)
{
fruit *f = _table[type];
if(!f)
{
f = new fruit;
_table[type] = f;
}
return f;
};
3.1.4 对象池模式
实现:
对象池模式:通过回收利用对象避免获取和释放资源所需的昂贵成本。
若初始化、实例化的代价高,且有需求需要经常实例化,但每次实例化的数量较少的情况下,使用对象池可以获得显著的效能提升。从池子中取得对象的时间是可预测的,但新建一个实例所需的时间是不确定。
常见的比如内存池,线程池等等。
3.1.5 原型模式
实现:
原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
class cookie
{
string _type;
public:
cookie(const string &t)
{
_type = t;
}
cookie(const cookie &c)
{
_type = c._type;
}
cookie* clone(const cookie *c)
{
return new cookie(*c);
}
};
class oven
{
cookie *_cookie;
public:
void set_cookie(cookie *c)
{
_cookie = c;
}
cookie* bake_cookie()
{
if(_cookie == nullptr)
return nullptr;
return _cookie->clone();
}
};
1.2 单例模式
实现:
单例模式,也叫单子模式,是一种常用的软件设计模式,属于创建型模式的一种。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
实现单例模式的时候需要考虑多线程安全问题。
饿汉模式:
class singleton
{
private:
singleton();
singleton(const singleton &s);
singleton& operator=(const singleton &s);
static singleton* _instance;
public:
static singleton* instance(){
if(instance == nullptr)
_instance = new singleton();
return _instance;
}
};
singleton* singleton::_instance = nullptr;
懒汉模式:
class singleton
{
private:
singleton();
singleton(const singleton &s);
singleton& operator=(const singleton &s);
static singleton* _instance;
public:
static singleton* instance(){
return _instance;
}
};
singleton* singleton::_instance = new singleton();
使用锁保证线程安全问题。
class singleton
{
private:
singleton();
singleton(const singleton &s);
singleton &operator=(const singleton &s);
static singleton *_instance;
static std::mutex lock;
public:
static singleton *instance()
{
if (nullptr == _instance)
{
if (lock.try_lock())
{
lock.lock();
if (nullptr == _instance)
{
_instance = new singleton();
}
lock.unlock();
}
}
return _instance;
}
};
3.1.6 多例模式
多例模式:确保一个类只有命名的实例,并提供对这些实例的全局访问。
class mutition
{
static map<string, mutition *> _table;
private:
mutition();
mutition(const mutition &);
public:
static mutition *get_instance(const string &key)
{
mutition *p = _table[key];
if (!p)
{
_table[key] = new mutition();
}
return _table[key];
}
};
3.2 结构型模式
借由一以贯之的方式来了解元件间的关系,以简化设计。
3.2.1 适配器模式
适配器模式 将某个类的接口转换成客户端期望的另一个接口表示。适配器模式可以消除由于接口不匹配所造成的类兼容性问题。
适配器模式是当客户的接口系统无法提供客户需要的接口时,可使用适配器模式将一个接口转换为另一个客户希望的接口以达到兼容性工作的目的。
对象适配器通过组合实现,类适配器通过继承实现。
下面为对象适配器:
//现在有一个双头插头,和一个三头插头,希望将双头插入到三头上,中间添加一个适配器即可
class double_plug
{
public:
virtual void plug_left() = 0;
virtual void plug_right() = 0;
};
class triple_plug
{
public:
virtual void plug_0() = 0;
virtual void plug_1() = 0;
virtual void plug_2() = 0;
};
class gongniu_double_plug : public double_plug
{
public:
void plug_left() {}
void plug_right() {}
};
class gongniu_triple_plug : public triple_plug
{
public:
void plug_0() {}
void plug_1() {}
void plug_2() {}
};
class plug_adapter : public double_plug
{
triple_plug *_plug;
public:
void set_plug(triple_plug *p)
{
_plug = p;
}
void interface_convert()
{
//TODO:做一些转换代码
}
void plug_left()
{
this->interface_convert();
_plug->plug_0();
_plug->plug_1();
}
void plug_right()
{
this->interface_convert();
_plug->plug_0();
_plug->plug_2();
}
};
下面为类适配器:
class plug_adapter : public double_plug, public triple_plug
{
public:
void interface_convert()
{
//TODO:做一些转换代码
}
void plug_0(){}
void plug_1(){}
void plug_2(){}
void plug_left()
{
this->interface_convert();
plug_0();
plug_1();
}
void plug_right()
{
this->interface_convert();
plug_0();
plug_2();
}
};
3.2.2 桥接模式
桥接模式将一个抽象与实现解耦,以便两者可以独立的变化。
不是让抽象基类与具体类分离,而是现实系统可能有多角度分类,每一种分类都有可能变化,那么把这种多角度分离出来让它们独立变化,减少它们之间的耦合性,即如果继承不能实现“开放-封闭原则”的话,就应该考虑用桥接模式。
class application
{
public:
virtual void runapplication() = 0; //implement
};
class linuxapp : public application
{
public:
virtual void runapplication()
{
std::cout << "run apache server on linux";
}
};
class windowsapp : public application
{
public:
virtual void runapplication()
{
std::cout << "run apache server on windwos";
}
};
class operatesystem
{
protected:
application *_app;
public:
void setapplication(application *app)
{
_app = app;
}
virtual void runapplication()
{
if (_app)
{
_app->runapplication();
}
}
};
class windows : public operatesystem
{
//todo:other logic code
};
class linux : public operatesystem
{
//todo:other logic code
};
3.2.3 组合模式
把多个对象组成树状结构来表示局部与整体,这样用户可以一样的对待单个对象和对象的组合。类似于特殊的树结构。
class node
{
protected:
std::string _name;
public:
node(std::string tmp) : _name(tmp) {}
virtual void addNode(node *) = 0;
virtual void show(int depth = 0) = 0;
};
class leaf : public node
{
public:
leaf(std::string tmp) : node(tmp) {}
virtual void addNode(node *tmp)
{
std::cout << "叶子无法添加组件\n";
}
virtual void show(int depth = 0)
{
std::string tmp = "";
for (int i = 0; i < depth; i++)
{
tmp += "-";
}
tmp += _name;
std::cout << tmp.c_str();
}
};
class root : public node
{
private:
std::vector<node *> _root;
public:
root(std::string tmp) : node(tmp) {}
void addNode(node *tmp)
{
_root.push_back(tmp);
}
void show(int depth = 0)
{
std::string tmp = "";
for (int i = 0; i < depth; i++)
{
tmp += "-";
}
tmp += _name;
std::cout << tmp.c_str();
std::vector<node *>::iterator it = _root.begin();
for (; it != _root.end(); it++)
{
(*it)->show(depth += 2);
}
}
};
3.2.4 装饰器模式
装饰器模式向某个对象动态地添加更多的功能。修饰模式是除类继承外另一种扩展功能的方法。
装饰器模式动态地给一个对象添加一些额外的职责(不重要的功能,只是偶然一次要执行),就增加功能来说,装饰模式比生成子类更为灵活。建造过程不稳定,按正确的顺序串联起来进行控制。在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。处理那些可以撤消的职责。当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
class mechine
{
private:
std::string _name;
public:
mechine(std::string tmp) : _name(tmp) {}
mechine() {}
virtual void show() = 0;
};
class aircondition : public mechine
{
public:
aircondition(std::string name) : mechine(name) {}
aircondition() {}
virtual void show()
{
std::cout << "机器的名称是" << _name.c_str();
}
};
class television : public mechine
{
public:
television(std::string name) : mechine(name) {}
television() {}
virtual void show()
{
std::cout << "机器的名称是" << _name.c_str();
}
};
class decorator : public mechine
{
protected:
mechine *_person; //要进行装饰的类
public:
decorate(mechine *tmp)
{
_person = tmp;
}
virtual void show()
{
_person->show();
}
};
class airdecorator : public decorator
{
public:
airdecorator(mechine *tmp) : decorator(tmp) {}
virtual void show()
{
this->externalfunction();
decorator::show();
}
void externalfunction() //额外功能
{
//todo:
}
};
class teledecorator : public decorator
{
public:
teledecorator(mechine *tmp) : decorator(tmp) {}
virtual void show()
{
this->externalfunction();
decorator::show();
}
void externalfunction() //额外功能
{
//todo:
}
};
3.2.5 外观模式
外观模式为子系统中的一组接口提供一个一致的界面, 外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
外观模式应该是用的很多的一种模式,特别是当一个系统很复杂时,系统提供给客户的是一个简单的对外接口,而把里面复杂的结构都封装了起来。客户只需使用这些简单接口就能使用这个系统,而不需要关注内部复杂的结构。比如编译器的编译过程由不同的模块完成,如预编译,编译,链接,可以将这几个步骤封装为一个接口为用户所使用。
class percompiler
{
public:
void percompile()
{
std::cout << "预编译" << std::endl;
}
};
class compiler
{
public:
void compile()
{
std::cout << "编译" << std::endl;
}
};
class link
{
public:
void link()
{
std::cout << "链接" << std::endl;
}
};
class comile
{
public:
void run()
{
percompiler per;
compiler com;
link link;
per.percompile();
com.compile();
link.link();
}
};
3.2.6 享元模式
享元模式通过共享以便有效的支持大量小颗粒对象。
如果一个应用程序使用了大量的对象,而这些对象造成了很大的存储开销就应该考虑使用。还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用较少的共享对象取代多组对象,此时可以考虑使用享元。
class web
{
protected:
std::string _name; //inner data
public:
web(std::string tmp) : _name(tmp) {}
std::string getString()
{
return _name;
}
virtual void use() = 0;
};
class share_web : public web
{
public:
share_web(std::string tmp) : web(tmp)
{
}
virtual void use()
{
std::cout << "共享网站\n";
}
};
class unshare_web : public web
{
public:
unshare_web(std::string tmp) : web(tmp)
{
}
virtual void use()
{
std::cout << "不共享网站\n";
}
};
//网站工厂
class web_fatory
{
private:
std::vector<web *> _web;
public:
web_fatory()
{
}
web *getweb(std::string key)
{
auto it = _web.begin();
for (; it != _web.end(); it++)
{
if ((*it)->getString() == key)
{
return *it;
}
}
_web.push_back(new share_web(key));
}
~web_fatory()
{
auto it = _web.begin();
for (; it != _web.end(); it++)
{
delete *it;
}
}
};
3.2.7 代理模式
代理模式为其他对象提供一个代理以控制对这个对象的访问。
代理模式一般用途如下:
- 远程代理(Remote Proxy):可以隐藏一个对象在不同地址空间的事实
- 虚拟代理(Virtual Proxy):通过代理来存放需要很长时间实例化的对象,利用惰性初始化实现
- 安全代理(保护代理ProtectionProxy):用来控制真是对象的访问权限
- 智能引用(Smart Reference):当调用真是对象时,代理处理另外一件事
class node
{
protected:
std::string _name;
public:
node(std::string name) : _name(name) {}
virtual void display() {}
};
class sprite : public node
{
public:
sprite(std::string name) : node(name) {}
virtual void display()
{
std::cout << "sprite display";
}
};
class displayproxy : public node
{
private:
sprite *_sprite;
public:
displayproxy(std::string name) : node(name), _sprite(nullptr) {}
virtual void display()
{
if (_sprite == nullptr)
{
_sprite = new sprite(node::_name);
}
_sprite->display();
}
};
3.3 行为型模式
3.3.1 策略模式
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。
class algorithm
{
public:
virtual void run() = 0;
};
class lru_algorithm : public algorithm
{
public:
virtual void run()
{
std::cout << "run lru algorithm" << std::endl;
}
};
class fifo_algorithm : public algorithm
{
public:
virtual void run()
{
std::cout << "run fifo algorithm" << std::endl;
}
};
class operate_system
{
algorithm* _algorithm;
public:
void set_algorithm(algorithm *al)
{
_algorithm = al;
}
void run_algorithm()
{
_algorithm->run();
}
};
3.3.2 观察者模式
观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。
使用情况:
- 当抽象个体有两个互相依赖的层面时。封装这些层面在单独的对象内将可允许程序员单独地去变更与重复使用这些对象,而不会产生两者之间交互的问题;
- 当其中一个对象的变更会影响其他对象,却又不知道多少对象必须被同时变更时;
- 当对象应该有能力通知其他对象,又不应该知道其他对象的实做细节时。
class ostream_observer
{
public:
virtual void update(void *data) = 0;
};
class ostream_displayer
{
public:
virtual void display() = 0;
};
class out_info
{
public:
virtual void register_observer(ostream_observer *ob) = 0;
virtual void remove_observer(ostream_observer *ob) = 0;
virtual void notify() = 0;
};
class debug_info : public out_info
{
set<ostream_observer *> obs;
void *data;
public:
void data_change(void *data)
{
this->data = data;
this->notify();
}
void register_observer(ostream_observer *ob)
{
obs.insert(ob);
}
void remove_observer(ostream_observer *ob)
{
obs.erase(ob);
}
protected:
void notify()
{
for (set<ostream_observer *>::iterator it = obs.begin(); it != obs.end(); it++)
{
(*it)->update(data);
}
}
};
class debug_console : public ostream_observer, public ostream_displayer
{
void *data;
out_info *_info;
public:
debug_console(out_info *info)
{
_info = info;
_info->register_observer(this);
}
void update(void *data)
{
this->data = data;
this->display();
}
void display()
{
cout << string((char *)data) << endl;
}
};
3.3.3 模板方法
函数的执行统一由父类管理,而接口的具体实现由子类完成。
//不同操作系统的中断处理
class operatesystem
{
protected:
virtual void triggerinterrupt() = 0; //触发中断
virtual void savecontext() = 0; //保存现场
virtual void interruptsoulation() = 0; //中断处理
virtual void returncontext() = 0; //返回现场
public:
virtual void handleinterrupt()
{
this->triggerinterrupt();
this->savecontext();
this->interruptsoulation();
this->returncontext();
}
};
class windows : public operatesystem
{
private:
virtual void triggerinterrupt() //触发中断
{
std::cout << "windows trriger\n";
}
virtual void savecontext() //保存现场
{
std::cout << "windows save context\n";
}
virtual void interruptsoulation() //中断处理
{
std::cout << "windows interrupt soulation\n";
}
virtual void returncontext() //返回现场
{
std::cout << "windows return context\n";
}
};
class linux : public operatesystem
{
private:
virtual void triggerinterrupt() //触发中断
{
std::cout << "linux trriger\n";
}
virtual void savecontext() //保存现场
{
std::cout << "linux save context\n";
}
virtual void interruptsoulation() //中断处理
{
std::cout << "linux interrupt soulation\n";
}
virtual void returncontext() //返回现场
{
std::cout << "linux return context\n";
}
};
3.3.4 命令模式
命令模式是将操作的对象和操作的命令分离,可以将命令装载入队列,执行与否可由操作者决定,而且并不影响其他类的新类,可用于撤销操作,可以将命令模式假想为顾客点餐的过程,客户端是客户,命令接受者是服务员,命令可以是不同的菜。客户可以选择点那份菜,(请求命令)服务员可以选择通知命令的与否命令由命令类派生可以列入队列等待执行,可以想象成要求一个厨师做不同的菜肴,厨师一个时刻只能制作一种,而执行的命令队列如同厨师的制作list,执行完全由厨师掌控,而list的制作由服务员从客户需求处得到。
class cook
{
public:
void fish()
{
std::cout << "水煮鱼\n";
}
void tofu()
{
std::cout << "千叶豆腐\n";
}
void beaf()
{
std::cout << "牛肉\n";
}
};
class command
{
protected:
cook *cooker;
public:
//可以修改厨师参数
command(cook *tmp)
{
cooker = tmp;
}
virtual void operate() = 0;
};
class fish : public command
{
public:
fish(cook *tmp) : command(tmp) {}
virtual void operate()
{
cooker->fish();
}
};
class beaf : public command
{
public:
beaf(cook *tmp) : command(tmp) {}
virtual void operate()
{
cooker->beaf();
}
};
class tofu : public command
{
public:
tofu(cook *tmp) : command(tmp) {}
virtual void operate()
{
cooker->tofu();
}
};
class waiter
{
protected:
std::vector<command *> _list;
public:
//点菜
void order(command *tmp)
{
_list.push_back(tmp);
std::cout << "点菜成功\n";
}
void run()
{
std::vector<command *>::iterator it = _list.begin();
while (it != _list.end())
{
(*it)->operate();
it++;
}
}
};
3.3.5 备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样就可以将以后的对象状态恢复到先前保存的状态。适用于功能比较复杂的,但需要记录或维护属性历史的类;或者需要保存的属性只是众多属性中的一小部分时Originator可以根据保存的Memo还原到前一状态。
//备忘录类
class memono
{
public:
std::string _state;
memono(std::string tmp)
{
_state = tmp;
}
};
class person
{
public:
std::string _state;
memono *create_memono()
{
return new memono(_state);
}
void setmemono(memono *tmp)
{
_state = tmp->_state;
}
void show()
{
std::cout << "状态:" << _state.c_str() << std::endl;
}
};
class manager
{
public:
memono *_memono;
};
3.3.6 状态模式
状态模式:允许一个对象在其内部状态改变时改变它的行为。
对象看起来似乎修改了它的类。它有两种使用情况:
- 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为;
- 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。可以想象一个人一天中有不同的规划,早上,中午,下午。这个人所做的事情可以根据时间点的变化而变化。
class work;
class state
{
public:
virtual void dosomething(work *) = 0;
};
class work
{
private:
state *_state;
public:
double hour;
public:
work()
{
_state = null;
}
void set_state(state *tmp)
{
if (_state)
{
delete _state;
_state = null;
}
_state = tmp;
this->run();
}
void run()
{
_state->dosomething(this);
}
};
class night : public state
{
public:
virtual void dosomething(work *w)
{
std::cout << "睡觉!\n";
}
};
class morning : public state
{
public:
virtual void dosomething(work *w)
{
std::cout << "起床!\n";
}
};
class noon : public state
{
public:
virtual void dosomething(work *w)
{
std::cout << "上班!\n";
}
};
3.3.7 访问者模式
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
class male;
class female;
//行为
class action
{
public:
virtual void get_male_cl(male *tmp) = 0;
virtual void get_female_cl(female *tmp) = 0;
};
//success
class success : public action
{
public:
virtual void get_male_cl(male *tmp)
{
std::cout << "man success\n";
}
virtual void get_female_cl(female *tmp)
{
std::cout << "female success\n";
}
};
//fail
class fail : public action
{
public:
virtual void get_male_cl(male *tmp)
{
std::cout << "man fail\n";
}
virtual void get_female_cl(female *tmp)
{
std::cout << "female fail\n";
}
};
class person
{
public:
virtual void accept(action *tmp) = 0;
};
class male : public person
{
public:
virtual void accept(action *tmp)
{
tmp->get_male_cl(this);
}
};
class female : public person
{
public:
virtual void accept(action *tmp)
{
tmp->get_female_cl(this);
}
};
class stuct
{
private:
std::vector<person *> _person;
public:
void add(person *tmp)
{
_person.push_back(tmp);
}
void show(action *tmp)
{
std::vector<person *>::iterator it = _person.begin();
for (; it != _person.end(); it++)
{
(*it)->accept(tmp);
}
}
};
3.3.8 中介者模式
中介者模式用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显示的相互引用,从而降低耦合;而且可以独立地改变它们之间的交互。可以把中介类想像成一个中介公司。
class person;
//中介类
class mediator
{
public:
virtual void send(std::string msg, person *tmp) = 0;
};
//操作对象
class person
{
protected:
mediator *_mediator;
public:
person(mediator *tmp) : _mediator(tmp) {}
};
class renter : public person
{
public:
renter(mediator *tmp) : person(tmp) {}
void send(std::string msg)
{
_mediator->send(msg, this);
}
void notify(std::string msg)
{
std::cout << "租客获得了消息:" << msg.c_str() << std::endl;
}
};
class houseprodiver : public person
{
public:
houseprodiver(mediator *tmp) : person(tmp) {}
void send(std::string msg)
{
_mediator->send(msg, this);
}
void notify(std::string msg)
{
std::cout << "房东获得了消息:" << msg.c_str() << std::endl;
}
};
class med : public mediator
{
public:
renter *_renter;
houseprodiver *_houseprodiver;
public:
void setrenter(renter *renter)
{
_renter = renter;
}
void sethouseprodiver(houseprodiver *prodiver)
{
_houseprodiver = prodiver;
}
void send(std::string msg, person *tmp)
{
if (tmp == _renter)
{
_renter->notify(msg);
}
else
{
_houseprodiver->notify(msg);
}
}
};
3.3.9 解释器模式
当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:该文法简单对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。
class context
{
public:
std::string _txt_rst;
std::string _txt_snd;
};
class interpreter
{
public:
virtual void run(context *) = 0;
};
class senior_inter : public interpreter
{
public:
virtual void run(context *tmp)
{
std::cout << tmp->_txt_rst.c_str() << "高级解释器\n";
}
};
class inferior_inter : public interpreter
{
public:
virtual void run(context *tmp)
{
std::cout << tmp->_txt_rst.c_str() << "次级解释器\n";
}
};
3.3.10 责任链模式
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。例如公司里的事物处理可以层层递交,直到可以被解决为止(链表),事务不断向上递交直至完全执行为止。
//抽象管理者
class manager
{
protected:
manager *_manager;
string _name;
public:
manager(manager *manager, string name) : _manager(manager), _name(name) {}
virtual void dealwithrequest(string name, int num) {}
};
//经理
class commonmanager : public manager
{
public:
commonmanager(manager *manager, string name) : manager(manager, name) {}
void dealwithrequest(string name, int num)
{
if (num < 500) //经理职权之内
{
cout << "经理" << _name << "批准" << name << "加薪" << num << "元" << endl
<< endl;
}
else
{
cout << "经理" << _name << "无法处理,交由总监处理" << endl;
_manager->dealwithrequest(name, num);
}
}
};
//总监
class majordomo : public manager
{
public:
majordomo(manager *manager, string name) : manager(manager, name) {}
void dealwithrequest(string name, int num)
{
if (num < 1000) //总监职权之内
{
cout << "总监" << _name << "批准" << name << "加薪" << num << "元" << endl
<< endl;
}
else
{
cout << "总监" << _name << "无法处理,交由总经理处理" << endl;
_manager->dealwithrequest(name, num);
}
}
};
//总经理
class generalmanager : public manager
{
public:
generalmanager(manager *manager, string name) : manager(manager, name) {}
void dealwithrequest(string name, int num) //总经理可以处理所有请求
{
cout << "总经理" << _name << "批准" << name << "加薪" << num << "元" << endl
<< endl;
}
};
3.3.11 迭代器模式
当用户所希望的接口系统无法提供时可以用适配器模式将接口转换为另一个接口例如当一类不支持迭代器时,利用类嵌套为目标类添加相应的支持。具体实现参照STL库中的迭代器。