为了账号安全,请及时绑定邮箱和手机立即绑定
首页 手记 常用设计模式讲解(史上最简短)

常用设计模式讲解(史上最简短)

2022.11.12 16:37 41浏览

这篇文章会简单粗暴地讲解一下常用设计模式,算是一个汇总,不会作详细地讲解

系统设计从设计原则开始,在过程中会自然而然发现需要加入模式的地方。所有的原则都不是必须遵守的,要考虑全局进行舍取,常常要折中。

所有的设计模式都是一种思想,即使在某种场合下没有办法实现常规的写法,但是用到它们的思想就可以了。尽可能保持设计的简单,只有真正需要模式时才使用,复杂度一般与灵活性正相关。

使用设计模式的目的:在不改变设计好的运行框架的前提下,需增加功能是只需要额外添加一个类,然后就可以在系统运行时增加功能

适配器模式

能够被适配的目标类一定有着可重写的接口,能够被适配的目标函数一定是虚函数

代码

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

代码

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


点击查看更多内容
0人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
软件工程师
手记
粉丝
0
获赞与收藏
1

关注TA,一起探索更多经验知识

同主题相似文章浏览排行榜

风间影月说签约讲师

51篇手记,涉及Java、MySQL、Redis、Spring等方向

进入讨论

Tony Bai 说签约讲师

151篇手记,涉及Go、C、Java、Python等方向

进入讨论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消