这篇文章会简单粗暴地讲解一下常用设计模式,算是一个汇总,不会作详细地讲解
系统设计从设计原则开始,在过程中会自然而然发现需要加入模式的地方。所有的原则都不是必须遵守的,要考虑全局进行舍取,常常要折中。
所有的设计模式都是一种思想,即使在某种场合下没有办法实现常规的写法,但是用到它们的思想就可以了。尽可能保持设计的简单,只有真正需要模式时才使用,复杂度一般与灵活性正相关。
使用设计模式的目的:在不改变设计好的运行框架的前提下,需增加功能是只需要额外添加一个类,然后就可以在系统运行时增加功能
适配器模式
能够被适配的目标类一定有着可重写的接口,能够被适配的目标函数一定是虚函数
代码
class Adapter : public absClassA
{
absClassB* realInstance; //被适配
def func() override
{
//Todo:调用realInstance的一些方法
}
};
设计原则
尽量减少一个类中对于其它类的引用,以减少调用一个功能需要.出来的次数
装饰者模式
这个模式中使用继承是为了能够做到类型匹配,因为一个对象在被装饰之后还必须是原来的那个对象类型。不能因为装饰而改变自己的类型。一个化了妆的人还是一个人
能够被装饰的类一定有着可重写的接口,能够被装饰的函数一定是虚函数
代码
class Decorator : absClass
{
absClass* realInstance; //被装饰者
def func() override
{
realInstance->func();
//Todo:do more something
}
};
设计原则
对扩展开放,对修改关闭
代理模式
代理(proxy):代表特定实例
能够被代理的类一定有着可重写的接口,能够被代理化的函数一定是虚函数
创建代理通常会使用工厂方法
代码
class Proxy : absClass
{
absClass* realInstance;
void func() override
{
#if 0
//保护代理
//通过一些条件控制对realInstance的访问
if(满足条件)
realInstance->func();
#elif 0
//虚拟代理
if(realInstance != NULL)
realInstance->func();
else
//开启异步线程创建absClass实例
//或 执行默认操作
#else
//远程代理
realInstance->func(); //func通过网络和远程实例通信
#endif
}
};
对比
装饰者模式的目的是为对象加上行为,而代理模式的目的是控制对对象的访问
其他代理
- 远程代理:代表特定远程实例
- 防火墙代理:公司防火墙系统
- 智能引用代理:引用计数
- 缓存代理:Web服务器
- 同步代理:JavaSpace
- 复杂隐藏代理(外观理解):字典
- 写入时复制代理:Java5的CopyOnWriteArrayList
观察者模式
观察感兴趣的事物 或者 事情
代码
class Subject //可以被包含
{
absClass*[] observers; //核心数据结构
}
设计原则
对象之间保持松耦合
迭代器与组合模式
组合模式代码
class xxx:absClass
{
absClass*[] a;
}
迭代器模式代码
class Iterator
{
def hasNext() = 0;
def next() = 0;
}
组合迭代器模式代码
class xxx:Iterator
{
Iterator*[] it;
def hasNext() override {...}
def next() override {...}
}
设计原则
每个类保持单一的责任
策略模式
多用接口
代码
class xxx
{
absClassA* a;
absClassB* b;
absClassC* c;
...
}
设计原则
- 多用组合,少用继承
- 使用组合可以拥有多个实例
- 针对接口编程,不针对具体实现编程
- 将变和不变的内容分开
状态模式
基于有限状态机;允许对象在内部状态改变时改变它的行为,对象好像在运行过程中修改了它的类定义
- 让一个类具有状态机功能的方法:
- 让类中拥有一个State成员
- 较简单
- 让类中拥有一个StateMachine成员
- 较复杂
- state <--> stateMachine <--> object
- 让类中拥有一个State成员
代码
class State
{
def Enter() = 0;
def Exit() = 0;
//其它纯虚函数
def DoSomethingA() = 0;
...
}
class StateMachine
{
State* curState; //核心数据结构
def changeState(State* newState) //核心函数
{
curState->Exit();
curState = newState;
curState->Enter();
}
//其他纯虚函数的实现
def DoSomethingA()
{
curState->DoSomethingA();
}
...
}
对比
策略模式和状态模式本质上是相同的,只是状态模式在执行逻辑时,状态可以自动发生改变
适用条件
- 实体的行为基于一些内在状态
- 状态可以被严格的分割为相对较少的不相干项目
- 实体响应一系列输入或事件
- 状态的跳转呈现出复杂的图结构
应用场景
- 玩家输入
- 导航菜单界面
- 分析文字
- 网络协议
- 其他异步行为
并发状态机
降低了状态的数量
一个对象拥有两个状态机,分别负责m种A类状态、n种B类状态,实现A类状态和B类状态的组合
分层状态机
实现了代码重用
用继承来实现:子类不处理的输入交给来父类处理
用栈来实现:每一个元素都是它下一个元素的子状态,接收到输入时,从栈顶开始向下查找可以处理输入的那个状态来处理该输入
下推状态机
实现了状态回退功能
状态机使用一个栈来记录历史状态,栈顶代表当前状态
Meyers单例模式
C++11标准之后的最佳选择
全局唯一、有全局访问点,但延迟构建不是必须的
class Singleton
{
public:
static Singleton* getInstance()
{
static Singleton s;
return &s;
}
}
- 缺点
- 不可控制创建时间
- 不可控制析构顺序
- getInstance的开销可能很大
一般类的构造和析构函数可以不做任何事情,定义额外的startUp和shutDown函数
- 管理多个单例的启动和终止的方法
- 手动控制启动和终止
- 把各个单例类登记在一个列表中,逐一启动
- 建立各个单例类之间的依赖关系图(dependency graph),自动计算出最优启动顺序和终止顺序
命令模式
将发出请求的对象和执行请求的对象进行解耦
命令模式的一个强大功能就是撤销
class Command
{
void execute() = 0;
void undo() = 0;
}
class Remote
{
map<int, Command*> commands; //核心数据结构
statck<Command*> historyCommands;
}
应用场景
- 控制器按键映射
- 模拟闭包:让函数有变量
模板方法模式
代码
class Template
{
def mainProcess()
{
funcA();
funcB();
if(funcC())
funcD();
...
}
def funcA() = 0;
def funcB() = 0;
def funcC() = 0;
def funcD() = 0;
}
设计原则
低层组件不可以调用高层组件,但是高层组件可以调用低层组件
工厂模式
抽象类的具体类比较多、需要分类或创建过程比较复杂时,可以用工厂模式将创建它们的函数封装起来
简单工厂模式(Simple Factory)
class AbsProduct
{
}
class Product1 : AbsProduct
{
}
class Product2 : AbsProduct
{
}
AbsProduct* create(string name)
{
if(...)
return new Product1();
else if(...)
return new Product2();
...
}
工厂方法模式(Factory Method)
class AbsProduct
{
}
class Product1 : AbsProduct
{
}
class Product2 : AbsProduct
{
}
...
class AbsFactory
{
public:
virtual AbsProduct createProduct() = 0;
}
class Factory1 : AbsFactory
{
public:
override AbsProduct createProduct()
{
...
return new Product1();
}
}
class Factory2 : AbsFactory
{
public:
override AbsProduct createProduct()
{
...
return new Product2();
}
}
...
抽象工厂模式(Abstract Factory)
class AbsProductA
{
}
class ProductA1 : AbsProductA
{
}
class ProductA2 : AbsProductA
{
}
...
class AbsProductB
{
}
class ProductB1 : AbsProductB
{
}
class ProductB2 : AbsProductB
{
}
...
class AbsFactory
{
public:
virtual AbsProductA createProductA() = 0;
virtual AbsProductB createProductB() = 0;
}
class Factory1 : AbsFactory
{
public:
override AbsProductA createProductA()
{
...
return new ProductA1();
}
override AbsProductB createProductB()
{
...
return new ProductB1();
}
}
class Factory2 : AbsFactory
{
public:
override AbsProductA createProductA()
{
...
return new ProductA2();
}
override AbsProductB createProductB()
{
...
return new ProductB2();
}
}
...
设计原则
依赖倒置原则:要依赖抽象类,不要依赖具体类
MVC
复合模式:结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题
MVC的本质就是将数据、显示、逻辑分开,一切以实现这个目标为目的的设计都可以说是MVC,以下是其中一种设计:
- controller响应view的调用来控制view和model
- model利用观察者通知controller和view
- view利用策略模式接上controller
- view利用组合模式管理所有组件。由于现在的GUI系统都比较先进,所以一般不用再使用组合模式进行管理了
view和controller之间的关系可以是1对1、1对n、n对1
原文链接