五、行为型模式

用来对类或对象怎样交互和怎样分配职责进行描述

1.模板方法
2.命令模式
3.策略模式
4.观察者模式

1.模板方法

1
1.1模板方法模式中的角色和职责
模板方法类图
  • AbstractClass(抽象类):在抽象类中定义一系列基本操作(PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。
  • ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。
1.2模板方法模式的案例
案例类图
#include 
#include 
using namespace std;

//抽象的制作饮料的方法
class MakeDrink
{
public://模板中 1 3在各个子类中是相同的,没有必要写成纯虚函数
  //1.把水煮开
  virtual void boil(){
    cout<<"把水煮开"<isAdd = isAdd
  }
  //2.冲某物
  virtual void brew(){
     cout<<"冲泡咖啡豆"<isAdd = isAdd;
   }
  //2.冲某物
  virtual void brew(){
     cout<<"冲泡茶叶"<make();

 cout<<"--------------------------------"<make();
 return 0;
}
1.3模板方法的优缺点

优点:
(1) 在父类中形式化地定义一个算法,而由它的子类来实现细节的处理,在子类实现详细的处理算法时并不会改变算法中步骤的执行次序。
(2) 模板方法模式是一种代码复用技术,它在类库设计中尤为重要,它提取了类库中的公共行为,将公共行为放在父类中,而通过其子类来实现不同的行为,它鼓励我们恰当使用继承来实现代码复用。
(3) 可实现一种反向控制结构,通过子类覆盖父类的钩子方法来决定某一特定步骤是否需要执行。
(4) 在模板方法模式中可以通过子类来覆盖父类的基本方法,不同的子类可以提供基本方法的不同实现,更换和增加新的子类很方便,符合单一职责原则和开闭原则。

缺点:
需要为每一个基本方法的不同实现提供一个子类,如果父类中可变的基本方法太多,将会导致类的个数增加,系统更加庞大,设计也更加抽象。

1.4适用场景

(1)具有统一的操作步骤或操作过程;
(2)具有不同的操作细节;
(3)存在多个具有同样操作步骤的应用场景,但某些具体的操作细节却各不相同;

在抽象类中统一操作步骤,并规定好接口;让子类实现接口。这样可以把各个具体的子类和操作步骤解耦合。

2.命令模式

将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。

命令模式可以将请求发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。

1.png
//系统的一个核心类模块
class Doctor
{
public:
  //治疗眼睛的方法
  void treatEyes(){
    cout<<"医生治疗了眼睛"<treatEyes();
  d->treatNose();
  return 0;
}

每个人都来直接找医生,医生的耦合度很高,现在提供病单的方法!

2.png
#include 
#include 
using namespace std;

//系统的一个核心类模块
class Doctor
{
public:
  //治疗眼睛的方法
  void treatEyes(){
    cout<<"医生治疗了眼睛"<d = doctor;
   }
   void treat(){
     this->d->treatEyes();
   }

   ~CommandEyes(){
     if(this->d != NULL){
       delete this->d;
       this->d = NULL;
     }
   }
private:
   Doctor *d;
};

//治疗鼻子的病单
class CommandNose
{
public:
   CommandNose(Doctor *doctor){
     this->d = doctor;
   }
   void treat(){
     this->d->treatNose();
   }

   ~CommandNose(){
     if(this->d != NULL){
       delete this->d;
       this->d = NULL;
     }
   }
private:
   Doctor *d;
};



//病人
int main(void)
{
  //病单通过填写病单 来找医生 
  Doctor *doctor = new Doctor;
  CommandEyes *cmdEyes = new CommandEyes(doctor);
  cmdEyes->treat();

  CommandNose *cmdNose = new CommandNose(doctor);
  cmdNose->treat();
 
 return 0;
}

但是 这样填写的病单类型未免太多 应该要抽出抽象类(护士)

3.png
#include 
#include 
using namespace std;

//系统的一个核心类模块
class Doctor
{
public:
  //治疗眼睛的方法
  void treatEyes(){
    cout<<"医生治疗了眼睛"<doctor = doctor;
  }

  virtual void treat() = 0;

  ~Command(){
    if(this->doctor != NULL){
      delete doctor;
      doctor = NULL;
    }
  }
protected:
  Doctor *doctor;
};

//治疗眼睛的病单
class CommandEyes:public Command
{
public:
   CommandEyes(Doctor *doctor):Command(doctor){}

   //病单执行治疗的时候,实际上让医生治疗
   virtual void treat(){
     this->doctor->treatEyes();
   }
};

//治疗鼻子的病单
class CommandNose:public Command
{
public:
   CommandNose(Doctor *doctor):Command(doctor){}

   //病单执行治疗的时候,实际上让医生治疗
   virtual void treat(){
     this->doctor->treatNose();
   }
};

//护士
class Nurse
{
public:
   Nurse(Command *cmd){
     this->cmd = cmd;
   }

   //下发病单方法
   void notify(){
     this->cmd->treat();
   }

   ~Nurse(){
     if(this->cmd != NULL){
       delete this->cmd;
     }
   }

private:
   Command *cmd;
}

//护士长
class NurseBoss
{
public:
   NurseBoss(){
      m_list.clear();
   }

   //给护士长添加病单的方法
   void setCmd(Command *cmd){
     this->m_list.push_back(cmd);
   }

   //下发手里所有病单的指令
   void notify(){
     for(list::iterator it = m_list.begin();it != m_list.end();it++){
       (*it)->treat();
     }
   }

   ~NurseBoss(){
      m_list.clear();
   }
private:
   list m_list;
}


//病人
int main(void)
{
 //如果我一天要看10个病,这创建的对象未免太多!需要护士登场,作统一调整了
 #if 0
 Command *cmd1 = new CommandEyes(new Doctor);
 cmd1->treat();

 Command *cmd2 = new CommandNose(new Doctor);
 cmd2->treat();
 #endif 

 #if 0   //即使有了护士,也和上面差不多,这时候,我们需要护士长!
 Nurse *nurse = new Nurse(new CommandEyes(new Doctor));
 nurse->notify();

 Nurse *nurse2 = new Nurse(new CommandNorse(new Doctor));
 nurse2->notify();
 #endif

 NurseBoss *woman = new NurseBoss;

 Command *cmd1 = new CommandEyes(new Doctor);
 Command *cmd2 = new CommandNose(new Doctor);

 //将所有指令都给护士长
 woman->setCmd(cmd1);
 woman->setCmd(cmd2);

 woman->notify();

 return 0;
}
案例类图
2.1命令模式中的角色和职责
命令模式类图

Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作。
ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作(Action)。
Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。
Receiver(接收者):接收者执行与请求相关的操作,它具体实现对请求的业务处理。

2.2命令模式的优缺点

优点:
(1) 降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,相同的请求者可以对应不同的接收者,同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。
(2) 新的命令可以很容易地加入到系统中。由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码,甚至客户类代码,满足“开闭原则”的要求。
(3)可以比较容易地设计一个命令队列或宏命令(组合命令)

缺点:
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用操作都需要设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。

2.3使用场景

(1) 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者也无须关心何时被调用。
(2) 系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,换言之,最初的请求发出者可能已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请求接收者,而无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。
(3) 系统需要将一组操作组合在一起形成宏命令。

2.5案例

联想路边撸串烧烤场景, 有烤羊肉,烧鸡翅命令,有烤串师傅,和服务员MM。根据命令模式,设计烤串场景。

#include 

using namespace std;

//命令执行者
class Cooker
{
public:
   //烤串
  void makeChuaner(){
    cout<<"烤串师傅进行了烤串"<cooker = cooker;
   }

   //菜单让最终的执行者干活的方法
   virtual void execute() = 0;

   ~Command(){
     if(this->cooker != NULL){
       delete this->cooker;
       this->cooker = NULL;
     }
   }
protected:
   Cooker *cooker;
};

//烤串的菜单
class CommandChuaner:public Command
{
public:
   CommandChuaner(Cooker *cooker):Command(cooker){}
   virtual void execute(){
      //命令 最终让执行者干的工作
     this->cooker->makeChuaner();
   }
};

//烤鸡翅的菜单
class CommandChicken:public Command
{
public:
   CommandChicken(Cooker *cooker):Command(cooker){}
   virtual void execute(){
     this->cooker->makeChicken();
   }
};

//管理所有命令的一个模块
//服务员MM
class Waitress
{
public:
  //给服务员添加菜单的方法
  void setCmd(Command *cmd){
     this->m_list.push_back(cmd);
  }

  //让服务员MM下单
  void notify(){
    for(list::iterator it = m_list.begin();it != m_list.end();it++){
      (*it)->execute();//在此发生了多态
    }
  }
private:
   list m_list;
}

int main(void)
{
 Waitress *mm = new Waitress;

 Command *chuanger = new CommandChuaner(new Cooker);
 Command *chicken = new CommandChicken(new Cooker);

 //把订单丢给服务员
 mm->setCmd(chuanger);
 mm->setCmd(chicken);

 //让服务员下单
 mm->notify();
 return 0;
}

命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。

3.策略模式

3.1策略模式中的角色和职责
策略模式类图

Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。
Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。
ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。

3.2策略模式案例
案例类图
#include 

using namespace std;

//抽象的侧罗(抽象的武器)
class AbstractStrategy
{
public:
  //纯虚函数,使用具体武器的策略
  virtual void useWeapon() = 0;
};

class KnifeStartegy:public AbstractStrategy
{
public:
   virtual void useWeapon(){
     cout<<"使用匕首,进行近战攻击"<strategy = strategy;
  }

  //攻击方法
  void fight(){
    cout<<"英雄开始战斗了"<strategy->useWeapon();
  }
private:
   //拥有一个使用攻击策略的抽象成员
  AbstractStrategy *strategy;
};


int main(void)
{
 AbstractStrategy *knife = new KnifeStartegy;
 AbstractStrategy *ak47 = new AkStrategy;

 Hero *hero = new Hero;

 cout<<"远程兵来了,更换远程攻击"<setStrategy(ak47);
 hero->fight();

 cout<<"近战兵了,更换近战的攻击"<setStrategy(knife);
 hero->fight();
 return 0;
}

案例2

#include 
#define _CRT_SECURE_NO_WARNINGS
using namespace std;


//销售策略
class AbstractStrategy
{
public:
  //商品具体的销售策略计算方式
  virtual double getPrice(double price) = 0;
};

//策略A 商品打8折
class StrategyA:public AbstractStrategy{
public:
   virtual double getPrice(double price){
     return price * 0.8;
   }
};

//策略B 商品减100
class StrategyB:public AbstractStrategy{
public:
   virtual double getPrice(double price){
     if(price >= 200)price = price - 100;
     return price;
   }
  
};

//商品
class Item
{
public:
   Item(string name,double price){
     this->name = name;
     this->price = price;
   }

   //提供一个可以更换策略的方法
  void setStrategy(AbstractStrategy *strategy)
  {
    this->strategy = strategy;
  }

   //最终获得商品的价格的方法
   double SellPrice(){
     return this->strategy->getPrice(this->price);
   }
private:
   string name;
   double price;//商品的价格
   //销售的策略
   AbstractStrategy *strategy;
}

int main(void)
{
    Item it("Nike鞋",200);
  AbstractStrategy *sA = new StrategyA;
  AbstractStrategy *sB = new StrategyB;

  cout<<"上午 商场执行 销售策略A 全程八折"<
3.3策略模式的优缺点

优点:

  • 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  • 使用策略模式可以避免多重条件选择语句。多重条件选择语句不易维护,它把采取哪一种算法或行为的逻辑与算法或行为本身的实现逻辑混合在一起,将它们全部硬编码(Hard Coding)在一个庞大的多重条件选择语句中,比直接继承环境类的办法还要原始和落后。
  • 策略模式提供了一种算法的复用机制。由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。

缺点:

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
  • 策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。
3.4适用场景

准备一组算法,并将每一个算法封装起来,使得它们可以互换。

4.观察者模式

观察者模式是用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。

4.1观察者模式中的角色和职责
观察者模式类图

Subject(被观察者或目标,抽象主题):被观察的对象。当需要被观察的状态发生变化时,需要通知队列中所有观察者对象。Subject需要维持(添加,删除,通知)一个观察者对象的队列列表。
ConcreteSubject(具体被观察者或目标,具体主题):被观察者的具体实现。包含一些基本的属性状态及其他操作。
Observer(观察者):接口或抽象类。当Subject的状态发生变化时Observer对象将通过一个callback函数得到通知。
ConcreteObserver(具体观察者):观察者的具体实现。得到通知后将完成一些具体的业务逻辑处理。

4.2观察者模式案例
案例类图
#include 
#include 
#define _CRT_SECURE_NO_WARNINGS
using namespace std;

//观察者 (监听者)
class Listener
{
public:
  //老师来了 我该怎么办
  virtual void onTeacherComming() = 0;

  //学生干坏事的方法
  virtual void doBadThings() = 0;
};

//抽象的 被观察者(通知者) 
class Notifier
{
public:
  //添加观察者的方法
  virtual void addListener(Listener *listener) = 0;
  //删除观察者的方法
  virtual void delListener(Listener *listener) = 0;
  //通知所有观察者的方法
  virtual void notify() = 0;
};

//具体的观察者
class Student:public Listener
{
public:
   Student(string name,string badthing){
     this->name = name;
     this->badthing = badthing;
   }
   //老师来了学生怎么办
   virtual void onTeacherComming(){
     cout<<"学生"<l_list.push_back(listener);
  }
  //删除观察者的方法
  virtual void delListener(Listener *listener){
     this->l_list.remove(listener);
  }
  //通知所有观察者的方法
  //班长使眼神的方法
  virtual void notify(){
     //广播信息,让每一个学生都执行各自重写的onTeacherComming方法
      for(list::iterator it = l_list.begin();it != l_list.end();it++){
        (*it)->onTeacherComming();
      }
  } 
private:
   list l_list;//班长手上所有学生列表(观察者)
};

int main(void)
{
  Listener *s1 = new Student("张三","抄作业");
  Listener *s2 = new Student("李四","打lol");
  Listener *s3 = new Student("王五","看李四打lol");

  Notifier *monitor = new Monitor;

  //将所有的学生列表告诉通知者,好让通知者进行通知
  monitor->addListener(s1);
  monitor->addListener(s2);
  monitor->addListener(s3);

  cout<<"教室一片和谐,老师没有来"<doBadThings();
  s2->doBadThings();
  s3->doBadThings();

  cout<<"班长突然发现老师来了,给学生们使了一个眼神"<notify();

  return 0;
}
4.3观察者模式的优缺点

优点:
(1) 观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色。
(2) 观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
(3) 观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多系统设计的难度。
(4) 观察者模式满足“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。

缺点:
(1) 如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间。
(2) 如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
(3) 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

4.4适用场景
  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道具体有多少对象将发生改变,也不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
4.5案例2

江湖中有多个帮派,还有一名无人不知,无事不晓的百晓生。当江湖中发生武林打斗事件,百晓生作为天生的大嘴巴会广播武林消息,每个帮派的门第对于事件的处理方式均不同,同帮派被欺负,要报仇,同帮派欺负别人,叫好。

用观察者模式模拟场景。

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include 
using namespace std;

class Notifier;
//抽象的观察者
class Listener
{
public:
    virtual void OnFriendsBeFight(Listener *one,Listener *another) = 0;
    virtual void fight(Listener *another,Notifier *notifier) = 0;
    virtual string getName() = 0;
    virtual string getParty() = 0;
};

//抽象的通知者
class Notifier
{
public:
    //增加观察者
    virtual void addListener(Listener *listener) = 0;
    //删除观察者
    virtual void delListener(Listener *listener) = 0;
    //群发通知
    virtual void notify(Listener *one, Listener *another) = 0;
};


class Hero :public Listener
{
public:
    Hero(string name, string party) {
        this->name = name;
        this->party = party;
    }

    virtual string getName() {
        return this->name;
    }

    virtual string getParty() {
        return this->party;
    }

    //one 是打人的,another是被打的
    virtual void OnFriendsBeFight(Listener *one,Listener *another) {
        //是自己,什么都不做
        if ((one->getName() == this->name) || (another->getName() == this->name))
        {
        //  cout << "和自己有关,什么都不做" << endl;
        }
        else if ((one->getParty() == this->party)) {//不是自己,是自己帮派打人,拍手叫好
            cout << this->name << "[" << this->getParty() << "]拍手叫好" << endl;
        }
        else {//不是自己,是自己帮派被打,帮忙
            cout << this->name << "[" << this->getParty() << "]去班马" << endl;
        }


    };
    virtual void fight(Listener *another,Notifier *notifier) {
        cout << this->name << "[" << this->getParty() << "]" << "把" << another->getName() << "[" << this->getParty() << "]给揍了" << endl;
        //通知百晓生
        //TO DO....
        notifier->notify(this, another);
    }
private:
    string name;
    string party;
};

class BaiXiao :public Notifier
{
public:
    //增加观察者
    virtual void addListener(Listener *listener) {
        this->l_list.push_back(listener);
    }
    //删除观察者
    virtual void delListener(Listener *listener) {
        this->l_list.remove(listener);
    }
    //群发通知
    virtual void notify(Listener *one, Listener *another) {
        list::iterator it = l_list.begin();
        for (; it != l_list.end(); it++)
        {
            (*it)->OnFriendsBeFight(one,another);
        }
    }
private:
    list l_list;
};

int main(void)
{
    Listener *hong7 = new Hero("洪七公", "丐帮");
    Listener *huangrong = new Hero("黄蓉", "丐帮");

    Listener *wuyazi = new Hero("无崖子", "逍遥派");
    Listener *tonglao = new Hero("天山童姥", "逍遥派");

    //创建一个百晓生
    Notifier *baixiao = new BaiXiao;

    //百晓生收集全部的武林人士名单
    baixiao->addListener(hong7);
    baixiao->addListener(huangrong);
    baixiao->addListener(wuyazi);
    baixiao->addListener(tonglao);

    //以上初始化完毕
    hong7->fight(wuyazi,baixiao);

    cout << "---------------" << endl;

    tonglao->fight(hong7,baixiao);

    system("pause");
    return 0;
}

你可能感兴趣的:(五、行为型模式)