Skip to content

常用设计模式知识整理,Head First设计模式(Java)的C++整理实现版,C++新经典设计模式整理----AstroWYH

Notifications You must be signed in to change notification settings

AstroWYH/Design-Patterns

Repository files navigation

Design-Patterns

常用设计模式知识整理,Head First设计模式(Java)的C++整理实现版,C++新经典设计模式整理----AstroWYH

设计模式的关键点和核心在于以下几个方面:

  1. 抽象:设计模式强调抽象概念的使用,而不是具体实现。它们提供了一种通用的解决方案,可以适用于不同的应用场景。
  2. 分离关注点:设计模式可以将不同的关注点分离开来,从而使代码更加模块化和易于维护。例如,MVC模式将应用程序分为模型、视图和控制器,分别处理数据、用户界面和应用逻辑。
  3. 封装变化:设计模式可以封装变化,从而使代码更加灵活和可扩展。例如,策略模式可以将不同的算法封装为策略对象,并让客户端代码根据需要选择不同的策略。
  4. 重用代码:设计模式提供了可重用的解决方案,避免在开发过程中重复编写代码,提高代码的可维护性和可重用性。

00-HeadFirst/Duck

  • 在Duck基类里,如果加上Fly行为,则会出现问题,因为不是所有的鸭子都会飞,比如如果RedHeadDuck不会飞的话,则其也具有了Fly行为,这是不对的。而Swim行为则没问题,因为所有的鸭子都会游泳。而Quack和Fly类似。
  • 基于上述考虑,如果直接将Fly行为从Duck类中抽出,将其作为接口(interface,C++中用只含纯虚函数的类表示),每个Duck的子类实现(implement)该接口,则代码的复用性很差。比如一共有50种不同鸭子的子类,其中20种子类里写了某种Fly,比如FlyWithWings,另外30种子类里写了另一种Fly,比如FlyNoWay,则相当于重复的代码写了20次+30次。
  • 基于上述考虑,将Fly这种行为再进行抽象一层。如FlyBehavior作为接口,提供Fly函数,而FlyWithWings作为一个类实现该接口,其内实现Fly;同理,FlyNoWay作为另一个类实现Fly。并将FlyBehavior接口的引用(C++中用unique_ptr即可)放入Duck父类,如此一来,每个Duck子类只需要在构造时,对从Duck父类继承到的FlyBehavior,进行赋值即可(多态的体现,父类指针/引用指向子类对象)。这样,20次+30次的重复代码就没有了,代码复用性提高。
  • 系统设计时,总会有不变+会变的两部分,需将不变+会变拆分。
  • 针对接口编程,而不是针对实现编程。

01-模板方法模式

  • 设计模式的作用就是在变化和稳定中间寻找隔离点,去分离稳定和变化,从而管理变化
  • 但如果整个设计中导出都是变化或者到处都稳定,那么自然也就不需要使用任何设计模式了。
  • 模板方法模式的关键:父类Fitghter的Burn()中有几个非常固定成员函数调用:EffectEnemy()、EffectSelf()、Show()。这是非常稳定的结构(也是模板方法的由来),前两个的具体实现交给子类,最后一个则是父类自己实现。

02-简单工厂模式

  • 开闭原则:对扩展开放,对修改关闭。
  • 实现了将创建怪物的代码,和怪物类对象要实现的业务逻辑相隔离,实现了封装变化
  • 避免将依赖遍布整个代码,提高了代码的可维护性和可扩展性,避免一修改代码就要修改一大片的困境。
  • 希望能够创建一个对象,但创建过程比较复杂,希望对外隐藏这些细节
  • 一个怪物工厂,生产3种怪物。如果增加怪物,则需要修改CreateMonster(),违反开闭原则

03-工厂方法模式

  • 工厂方法模式是使用频率最高的工厂模式,又叫工厂模式多态工厂模式
  • 有些人将简单工厂模式看做工厂方法模式的特例;抽象工厂模式见外层md,这里不详细再写。
  • 3个怪物工厂,分别生产3种怪物。当有新的怪物诞生时,需要新增对应的工厂类,满足开闭原则。
  • 除怪物类外,工厂类也有父类和多个子类(和怪物类一一对应)。
  • 抽象工厂模式的核心:由于工厂方法模式每增加一种怪物子类,就要增加一种工厂子类,导致工厂子类数量太多。因此,如果一个工厂子类能够生产多种怪物(这多种怪物具有相似规则),那么就能有效减少工厂子类的数量,这就是抽象工厂模式的核心思想!
  • 比如一个城镇工厂子类可生产:城镇亡灵怪、城镇元素怪、城镇机械怪。

04-建造者模式

  • 建造者模式通常用来创建一个比较复杂的对象,改对象的创建一般分一定的步骤顺序执行。
  • 比如本例中,创建一个怪物,需要组装躯干、头部、四肢。这部分代码是稳定的,不发生变化。
  • 将LoadTrunkModel()、LoadHeadModel()、LoadLimbsModel()这些创建载入相关成员函数成为构建过程的相关函数,考虑到Monster类中要实现的其他功能可能还较多,因此将这部分成员函数提取(分离)到单独一个类中(MonsterBuilder类,并根据Monster子类各自创建相应子类),不但可以减少Monster类代码量,还增加构建过程的独立性。日后游戏中任何躯干、头部、四肢的组装,都可单独通过该类。
  • MonsterDirector是指挥者类,其内部封装MonsterBuilder的指针,其Conduct()中依次调用builder的LoadTrunkModel()、LoadHeadModel()、LoadLimbsModel()的组装流程,扮演指挥者的作用。

05-适配器模式

  • LogToFile是代码中原先直接记录log到文件的类。
  • LogToDB是后来随着工程的扩张,代码越来越多,于是将log从文件记录到DB中。
  • 然而有一次,DB损坏了,必须要使用原来的LogToFile,但此时很多db->xxx()这样的代码如果全部修改,则代价太大
  • 因此引入LogToAdapter类作为适配层,将其作为LogToDB的子类,在其内封装LogToDB的指针,在其子类各函数中实现LogToFile的实际操作。
  • 可以看到,经过适配,极大地减少了代码的改动

06-观察者模式

  • 关键是map<int, list<shared_ptr>> family_list_,把隶属于某个家族id的所有玩家收集到一个列表中,把所有家族的玩家形成一个map。当某个玩家发出消息后,则只需要遍历该玩家所在家族的list,即只向该家族list发送消息。
  • “观察者模式”也叫“发布-订阅(Publish-Subscribe)模式”。“观察者(Fighter子类)”提供NotifyWords,并将其自己加入到“通知器(Notify子类)”的一个对象列表中(family_list_),当通知器意识到有事件发生时,遍历对象列表找到每个观察者,并**调用观察者提供的NotifyWords(发布)**来达到通知观察者的目的,观察者可在NotifyWords中实现收到通知后想做的事情。
  • 观察者模式特点:1)观察者和通知器是松耦合,改变一方只要接口不变,不影响另一方。通知器不需要了解具体的观察者类。2)通知器向观察者列表中的所有观察者发送通知,而非观察者不断向通知器查询状态,简化了一对多系统的设计难度。3)可增加新的观察者和通知器,满足开闭原则
  • 举例:游戏中,家族成员运送的镖车收到敌人攻击,本家族其他成员会收到通知,单击通知中的“前往”按钮即可出现到战场救援。

07-单例模式

About

常用设计模式知识整理,Head First设计模式(Java)的C++整理实现版,C++新经典设计模式整理----AstroWYH

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published