设计模式—行为模式深度分析与实现【c++与golang】【万字总结】

文章目录

  • 前言
  • 一、行为模式
  • 二、观察者模式
    • 场景
    • 原理
    • c++实现
    • go实现
  • 三、责任链模式
    • 场景
    • 原理
    • c++实现
    • go实现
  • 四、中介模式
    • 场景
    • 原理
    • c++实现
    • go实现
  • 五、状态模式
    • 场景
    • 原理
    • c++实现
    • go实现
  • 六、策略模式
    • 场景
    • 原理
    • c++实现
    • go实现
  • 总结


前言

设计模式系列文章之行为模式汇总,并且分析他们的原理以及场景。并且分别用c++与go语言进行举例实现。

一、行为模式

行为型模式关注的是系统中对象之间的相互交互,解决系统在运行时对象之间的相互通信和协作,进一步明确对象的职责。
下面分别介绍观察者模式,责任链模式,中介模式,状态模式以及策略模式。

二、观察者模式

场景

消息推送系统:当有新消息到来时,所有订阅该消息的用户都会收到通知。
股票行情系统:当股票价格发生变化时,所有订阅该股票的用户都会收到通知。
GUI编程:当用户界面中的某个控件发生变化时,所有依赖该控件的控件都会自动更新。

当一个对象状态的改变需要改变其他对象, 或实际对象是事先未知的或动态变化的时, 可使用观察者模式。
当你使用图形用户界面类时通常会遇到一个问题。 比如, 你创建了自定义按钮类并允许客户端在按钮中注入自定义代码, 这样当用户按下按钮时就会触发这些代码。

观察者模式允许任何实现了订阅者接口的对象订阅发布者对象的事件通知。 你可在按钮中添加订阅机制, 允许客户端通过自定义订阅类注入自定义代码。
当应用中的一些对象必须观察其他对象时, 可使用该模式。 但仅能在有限时间内或特定情况下使用。

原理

**定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。**它还有两个别名,依赖(Dependents),发布-订阅(Publish-Subsrcibe)。可以举个博客订阅的例子,当博主发表新文章的时候,即博主状态发生了改变,那些订阅的读者就会收到通知,然后进行相应的动作,比如去看文章,或者收藏起来。博主与读者之间存在种一对多的依赖关
系。
可以看到博客类中有一个观察者链表(即订阅者),当博客的状态发生变化时,通过Notify成员函数通知所有的观察者,告诉他们博客的状态更新了。而观察者通过Update成员函数获取博客的状态信息
设计模式—行为模式深度分析与实现【c++与golang】【万字总结】_第1张图片

c++实现

首先是观察者的基类 Observer,它定义了一个虚函数 Update。
接下来是博客的基类 Blog,它定义了添加、移除和通知观察者的方法,并且包含观察者链表和一个状态属性。
然后是具体的博客类 BlogCSDN,继承自 Blog,它实现了设置状态和获取状态的方法。
接着是具体的观察者类 ObserverBlog,继承自 Observer,它包含观察者名称和观察的博客对象,并实现了更新方法,在更新时输出观察者名称和博客状态。
使用具体步骤:
观察者通过调用主题的 Attach 方法注册到主题中。
主题维护一个观察者链表,并在状态改变时通过调用观察者的 Update 方法来通知观察者。
观察者接收到通知后,可以通过调用主题的方法来获取状态信息,并执行相应的操作。

//观察者
class Observer  
{
public:
	Observer() {}
	virtual ~Observer() {}
	virtual void Update() {} 
};
//博客
class Blog  
{
public:
	Blog() {}
	virtual ~Blog() {}
	void Attach(Observer *observer) { m_observers.push_back(observer); }	 //添加观察者
	void Remove(Observer *observer) { m_observers.remove(observer); }        //移除观察者
	void Notify() //通知观察者
	{
		list<Observer*>::iterator iter = m_observers.begin();
		for(; iter != m_observers.end(); iter++)
			(*iter)->Update();
	}
	virtual void SetStatus(string s) { m_status = s; } //设置状态
	virtual string GetStatus() { return m_status; }    //获得状态
private:
	list<Observer* > m_observers; //观察者链表
protected:
	string m_status; //状态
};

//具体博客类
class BlogCSDN : public Blog
{
private:
	string m_name; //博主名称
public:
	BlogCSDN(string name): m_name(name) {}
	~BlogCSDN() {}
	void SetStatus(string s) { m_status = "CSDN通知 : " + m_name + s; } //具体设置状态信息
	string GetStatus() { return m_status; }
};
//具体观察者
class ObserverBlog : public Observer   
{
private:
	string m_name;  //观察者名称
	Blog *m_blog;   //观察的博客,当然以链表形式更好,就可以观察多个博客
public: 
	ObserverBlog(string name,Blog *blog): m_name(name), m_blog(blog) {}
	~ObserverBlog() {}
	void Update()  //获得更新状态
	{ 
		string status = m_blog->GetStatus();
		cout<<m_name<<"-------"<<status<<endl;
	}
};
//测试案例
int main()
{
	Blog *blog = new BlogCSDN("1985");
	Observer *observer1 = new ObserverBlog("ad", blog);
	blog->Attach(observer1);
	blog->SetStatus("发表——观察者模式");
	blog->Notify();
	delete blog; delete observer1;
	return 0;
}

go实现

在 Go 语言中,我们使用接口来实现多态性。首先定义了 Observer 接口,它包含了 Update() 方法。

然后定义了 Subject 接口,它包含了添加观察者、移除观察者、通知观察者等方法,以及获取和设置状态的方法。

接着是 BlogCSDN 结构体,它实现了 Subject 接口,用于具体的博客主题。这里使用了 Go 语言中的双向链表来存储观察者。

package main

import (
	"container/list"
	"fmt"
)

// 观察者接口
type Observer interface {
	Update()
}

// 主题接口
type Subject interface {
	Attach(observer Observer)
	Remove(observer Observer)
	Notify()
	SetStatus(status string)
	GetStatus() string
}

// 具体主题类
type BlogCSDN struct {
	name   string
	status string
	observers *list.List
}

func NewBlogCSDN(name string) *BlogCSDN {
	return &BlogCSDN{
		name:      name,
		observers: list.New(),
	}
}

func (b *BlogCSDN) Attach(observer Observer) {
	b.observers.PushBack(observer)
}

func (b *BlogCSDN) Remove(observer Observer) {
	for item := b.observers.Front(); item != nil; item = item.Next() {
		if item.Value.(Observer) == observer {
			b.observers.Remove(item)
			break
		}
	}
}

func (b *BlogCSDN) Notify() {
	for item := b.observers.Front(); item != nil; item = item.Next() {
		item.Value.(Observer).Update()
	}
}

func (b *BlogCSDN) SetStatus(status string) {
	b.status = "CSDN通知: " + b.name + status
}

func (b *BlogCSDN) GetStatus() string {
	return b.status
}

// 具体观察者类
type ObserverBlog struct {
	name string
	blog Subject
}

func NewObserverBlog(name string, blog Subject) *ObserverBlog {
	return &ObserverBlog{
		name: name,
		blog: blog,
	}
}

func (o *ObserverBlog) Update() {
	status := o.blog.GetStatus()
	fmt.Printf("%s-------%s\n", o.name, status)
}

func main() {
	blog := NewBlogCSDN("wuzhekai1985")
	observer1 := NewObserverBlog("tutupig", blog)
	blog.Attach(observer1)

	blog.SetStatus("发表设计模式C++实现(15)——观察者模式")
	blog.Notify()
}

三、责任链模式

场景

当程序需要使用不同方式处理不同种类请求, 而且请求类型和顺序预先未知时, 可以使用责任链模式。
该模式能将多个处理者连接成一条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进行处理。 这样所有处理者都有机会来处理请求。

当必须按顺序执行多个处理者时, 可以使用该模式。
无论你以何种顺序将处理者连接成一条链, 所有请求都会严格按照顺序通过链上的处理者。

如果所需处理者及其顺序必须在运行时进行改变, 可以使用责任链模式。
如果在处理者类中有对引用成员变量的设定方法, 你将能动态地插入和移除处理者, 或者改变其顺序

原理

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。其思想很简单,考虑员工要求加薪。公司的管理者一共有三级,总经理、总监、经理,如果一个员工要求加薪,应该向主管的经理申请,如果加薪的数量在经理的职权内,那么经理可以直接批准,否则将申请上交给总监。总监的处理方式也一样,总经理可以处理所有请求。这就是典型的职责链模式,请求的处理形成了一条链,直到有一个对象处理请求。给出这个例子的UML图。
设计模式—行为模式深度分析与实现【c++与golang】【万字总结】_第2张图片

c++实现

首先,我们定义了一个抽象基类Manager(管理者),包含了一个指向下一个处理者的指针m_manager和一个管理者的名称m_name。同时,还有一个纯虚函数DealWithRequest(string name, int num)用于处理请求。

接下来,我们定义了三个具体的管理者类:CommonManager(经理)、Majordomo(总监)和GeneralManager(总经理)。这三个具体类都继承自基类Manager。在每个具体类的构造函数中,除了传入管理者的名称和指向下一个处理者的指针外,还调用了基类的构造函数。

在每个具体类中,我们重写了基类的DealWithRequest函数。在CommonManager中,经理可以处理金额小于500的请求,否则将请求交给总监处理;在Majordomo中,总监可以处理金额小于1000的请求,否则将请求交给总经理处理;而在GeneralManager中,总经理可以处理所有请求。

//抽象管理者
class Manager
{
protected:
	Manager *m_manager;
	string m_name;
public:
	Manager(Manager *manager, string name):m_manager(manager), m_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<<"经理"<<m_name<<"批准"<<name<<"加薪"<<num<<"元"<<endl<<endl;
		}
		else
		{
			cout<<"经理"<<m_name<<"无法处理,交由总监处理"<<endl;
			m_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<<"总监"<<m_name<<"批准"<<name<<"加薪"<<num<<"元"<<endl<<endl;
		}
		else
		{
			cout<<"总监"<<m_name<<"无法处理,交由总经理处理"<<endl;
			m_manager->DealWithRequest(name, num);
		}
	}
};
//总经理
class GeneralManager: public Manager
{
public:
	GeneralManager(Manager *manager, string name):Manager(manager,name) {}
	void DealWithRequest(string name, int num)  //总经理可以处理所有请求
	{
		cout<<"总经理"<<m_name<<"批准"<<name<<"加薪"<<num<<"元"<<endl<<endl;
	}
};
//测试案例
int main()
{	
	Manager *general = new GeneralManager(NULL, "A"); //设置上级,总经理没有上级
	Manager *majordomo = new Majordomo(general, "B"); //设置上级
	Manager *common = new CommonManager(majordomo, "C"); //设置上级
	common->DealWithRequest("D",300);   //员工D要求加薪
	common->DealWithRequest("E", 600);
	common->DealWithRequest("F", 1000);
	delete common; delete majordomo; delete general;
	return 0;
}

go实现

在这个Go语言版本中,我使用了接口和结构体来实现抽象管理者和具体管理者的定义。每个具体的管理者结构体都实现了Manager接口中的DealWithRequest方法,用于处理请求。在每个具体的管理者中,如果该管理者没有权限处理该请求,就将请求交给上一个级别的管理者处理。

package main

import "fmt"

// 抽象管理者接口
type Manager interface {
	DealWithRequest(name string, num int)
}

// 经理
type CommonManager struct {
	manager Manager
	name    string
}

func NewCommonManager(manager Manager, name string) CommonManager {
	return CommonManager{
		manager: manager,
		name:    name,
	}
}

func (c CommonManager) DealWithRequest(name string, num int) {
	if num < 500 {
		fmt.Printf("经理%s批准%s加薪%d元\n\n", c.name, name, num)
	} else {
		fmt.Printf("经理%s无法处理,交由总监处理\n", c.name)
		c.manager.DealWithRequest(name, num)
	}
}

// 总监
type Majordomo struct {
	manager Manager
	name    string
}

func NewMajordomo(manager Manager, name string) Majordomo {
	return Majordomo{
		manager: manager,
		name:    name,
	}
}

func (m Majordomo) DealWithRequest(name string, num int) {
	if num < 1000 {
		fmt.Printf("总监%s批准%s加薪%d元\n\n", m.name, name, num)
	} else {
		fmt.Printf("总监%s无法处理,交由总经理处理\n", m.name)
		m.manager.DealWithRequest(name, num)
	}
}

// 总经理
type GeneralManager struct {
	manager Manager
	name    string
}

func NewGeneralManager(manager Manager, name string) GeneralManager {
	return GeneralManager{
		manager: manager,
		name:    name,
	}
}

func (g GeneralManager) DealWithRequest(name string, num int) {
	fmt.Printf("总经理%s批准%s加薪%d元\n\n", g.name, name, num)
}

func main() {
	generalManager := NewGeneralManager(nil, "王总")          // 创建总经理对象,没有上级
	majordomo := NewMajordomo(generalManager, "张总监")     // 创建总监对象,并设置总经理为上级
	commonManager := NewCommonManager(majordomo, "李经理") // 创建经理对象,并设置总监为上级

	commonManager.DealWithRequest("小明", 300)  // 经理处理
	commonManager.DealWithRequest("小李", 800)  // 总监处理
	commonManager.DealWithRequest("小王", 1200) // 总经理处理
}

四、中介模式

场景

当一些对象和其他对象紧密耦合以致难以对其进行修改时, 可使用中介者模式。
该模式让你将对象间的所有关系抽取成为一个单独的类, 以使对于特定组件的修改工作独立于其他组件。

当组件因过于依赖其他组件而无法在不同应用中复用时, 可使用中介者模式。
应用中介者模式后, 每个组件不再知晓其他组件的情况。 尽管这些组件无法直接交流, 但它们仍可通过中介者对象进行间接交流。 如果你希望在不同应用中复用一个组件, 则需要为其提供一个新的中介者类。

如果为了能在不同情景下复用一些基本行为, 导致你需要被迫创建大量组件子类时, 可使用中介者模式。
由于所有组件间关系都被包含在中介者中, 因此你无需修改组件就能方便地新建中介者类以定义新的组件合作方式。

原理

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。中介者模式的例子很多,大到联合国安理会,小到房屋中介,都扮演了中间者的角色,协调各方利益。
本文就以租房为例子,如果没有房屋中介,那么房客要自己找房东,而房东也要自己找房客,非常不方便。有了房屋中介机构就方便了,房东可以把要出租的房屋信息放到中介机构,而房客可以去中介机构咨询。设计模式—行为模式深度分析与实现【c++与golang】【万字总结】_第3张图片

c++实现

首先,代码中定义了一个抽象基类Person,其中包含一个指向中介者对象的指针m_mediator,以及设置中介者SetMediator、发送信息SendMessage和接收信息GetMessage的虚函数。

接下来,Renter和Landlord类分别表示租房者和房东。它们继承自Person类,并实现了基类中的虚函数。SetMediator用于设置中介者对象,SendMessage用于向中介者发送信息,GetMessage用于从中介者获取信息。当租房者发送信息时,中介者将信息转发给房东;当房东发送信息时,中介者将信息转发给租房者。这样,租房者和房东可以通过中介者进行通信。

最后,HouseMediator类表示房屋中介,它继承自抽象中介者类Mediator。该类包含两个私有成员变量m_A和m_B,分别指向租房者和房东。通过SetA和SetB方法设置其中一方的对象。Send方法用于接收发送方的信息,并根据发送方是租房者还是房东,将信息转发给另一方。具体来说,当租房者发送信息时,中介者将信息转发给房东;当房东发送信息时,中介者将信息转发给租房者。

class Mediator;
//抽象人
class Person
{
protected:
	Mediator *m_mediator; //中介
public:
	virtual void SetMediator(Mediator *mediator){} //设置中介
	virtual void SendMessage(string message) {}    //向中介发送信息
	virtual void GetMessage(string message) {}     //从中介获取信息
};
//抽象中介机构
class Mediator
{
public:
	virtual void Send(string message, Person *person) {}
	virtual void SetA(Person *A) {}  //设置其中一方
	virtual void SetB(Person *B) {}
};
//租房者
class Renter: public Person
{
public:
	void SetMediator(Mediator *mediator) { m_mediator = mediator; }
	void SendMessage(string message) { m_mediator->Send(message, this); }
	void GetMessage(string message) { cout<<"租房者收到信息"<<message; }
};
//房东
class Landlord: public Person
{
public:
	void SetMediator(Mediator *mediator) { m_mediator = mediator; }
	void SendMessage(string message) { m_mediator->Send(message, this); }
	void GetMessage(string message) { cout<<"房东收到信息:"<<message; }
};
//房屋中介
class HouseMediator : public Mediator
{
private:
	Person *m_A; //租房者
	Person *m_B; //房东
public:
	HouseMediator(): m_A(0), m_B(0) {}
	void SetA(Person *A) { m_A = A; }
	void SetB(Person *B) { m_B = B; }
	void Send(string message, Person *person) 
	{
		if(person == m_A) //租房者给房东发信息
			m_B->GetMessage(message); //房东收到信息
		else
			m_A->GetMessage(message);
	}
};
//测试案例
int main()
{	
	Mediator *mediator = new HouseMediator();
	Person *person1 = new Renter();    //租房者
	Person *person2 = new Landlord();  //房东
	mediator->SetA(person1);
	mediator->SetB(person2);
	person1->SetMediator(mediator);
	person2->SetMediator(mediator);
	person1->SendMessage("我想在南京路附近租套房子,价格800元一个月\n");
	person2->SendMessage("出租房子:南京路100号,70平米,1000元一个月\n");
	delete person1; delete person2; delete mediator;
	return 0;
}

go实现

与之前的C++代码类似,我们定义了抽象人接口Person和房屋中介接口Mediator。然后,定义了租房者结构体Renter和房东结构体Landlord,它们分别实现了抽象人接口。最后,实现了房屋中介结构体HouseMediator,它实现了房屋中介接口。

在测试案例中,我们创建了房屋中介对象mediator、租房者对象person1和房东对象person2。然后,通过调用房屋中介对象的SetASetB方法,将租房者和房东对象设置给房屋中介。接着,通过调用租房者和房东对象的SetMediator方法,将房屋中介对象设置给它们。

最后,我们调用租房者和房东对象的SendMessage方法发送消息。租房者发送的消息是“我想在南京路附近租套房子,价格800元一个月”,而房东发送的消息是“出租房子:南京路100号,70平米,1000元一个月”。在每个对象的GetMessage方法中,我们简单地打印出收到的消息。



```go
// 抽象人接口
type Person interface {
	SetMediator(mediator Mediator)
	SendMessage(message string)
	GetMessage(message string)
}

// 房屋中介接口
type Mediator interface {
	Send(message string, person Person)
	SetA(person Person)
	SetB(person Person)
}

// 租房者结构体
type Renter struct {
	mediator Mediator
}

func (r *Renter) SetMediator(mediator Mediator) {
	r.mediator = mediator
}

func (r *Renter) SendMessage(message string) {
	r.mediator.Send(message, r)
}

func (r *Renter) GetMessage(message string) {
	fmt.Println("租房者收到信息:", message)
}

// 房东结构体
type Landlord struct {
	mediator Mediator
}

func (l *Landlord) SetMediator(mediator Mediator) {
	l.mediator = mediator
}

func (l *Landlord) SendMessage(message string) {
	l.mediator.Send(message, l)
}

func (l *Landlord) GetMessage(message string) {
	fmt.Println("房东收到信息:", message)
}

// 房屋中介结构体
type HouseMediator struct {
	A Person
	B Person
}

func (h *HouseMediator) SetA(person Person) {
	h.A = person
}

func (h *HouseMediator) SetB(person Person) {
	h.B = person
}

func (h *HouseMediator) Send(message string, person Person) {
	if person == h.A {
		h.B.GetMessage(message)
	} else {
		h.A.GetMessage(message)
	}
}

// 测试案例
func main() {
	mediator := &HouseMediator{}
	person1 := &Renter{}   // 租房者
	person2 := &Landlord{} // 房东
	mediator.SetA(person1)
	mediator.SetB(person2)
	person1.SetMediator(mediator)
	person2.SetMediator(mediator)
	person1.SendMessage("我想在南京路附近租套房子,价格800元一个月\n")
	person2.SendMessage("出租房子:南京路100号,70平米,1000元一个月\n")
}

五、状态模式

场景

如果对象需要根据自身当前状态进行不同行为, 同时状态的数量非常多且与状态相关的代码会频繁变更的话, 可使用状态模式。
模式建议你将所有特定于状态的代码抽取到一组独立的类中。 这样一来, 你可以在独立于其他状态的情况下添加新状态或修改已有状态, 从而减少维护成本。

如果某个类需要根据成员变量的当前值改变自身行为, 从而需要使用大量的条件语句时, 可使用该模式。
状态模式会将这些条件语句的分支抽取到相应状态类的方法中。 同时, 你还可以清除主要类中与特定状态相关的临时成员变量和帮手方法代码。

当相似状态和基于条件的状态机转换中存在许多重复代码时, 可使用状态模式。
状态模式让你能够生成状态类层次结构, 通过将公用代码抽取到抽象基类中来减少重复。

原理

状态模式:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。它有两种使用情况:(1)一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。(2)一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。本文的例子为第一种情况,以战争为例,假设一场战争需经历四个阶段:前期、中期、后期、结束。当战争处于不同的阶段,战争的行为是不一样的,也就说战争的行为取决于所处的阶段,而且随着时间的推进是动态变化的。下面给出相应的UML图。
设计模式—行为模式深度分析与实现【c++与golang】【万字总结】_第4张图片

c++实现

在该模式中,有一个上下文类(Context Class),即War类,它持有一个状态类(State Class)的引用,根据具体情况调用该状态类的方法。同时,状态类也持有上下文类的引用,以便在状态转移时修改上下文类的状态。

在该模式中,War类是上下文类,State类是状态类,ProphaseState、MetaphaseState、AnaphaseState是具体的状态子类。

在War类中,m_state表示当前状态,m_days表示战争持续时间。SetState()方法用于设置当前状态,GetState()方法用于获取当前状态并调用相应的方法,GetDays()和SetDays()方法用于获取和设置战争持续时间。

在State类中,Prophase()、Metaphase()、Anaphase()、End()方法分别表示战争的前期、中期、后期和结束阶段的行为。CurrentState()方法用于调用相应的行为方法。

在具体的状态子类中,重写了父类的行为方法,实现了具体的行为逻辑。在ProphaseState中,Prophase()方法表示战争的前期行为,若战争持续时间小于10天,则输出“战争初期,双方你来我往,互相试探对方”,否则将状态设置为MetaphaseState,并调用GetState()方法。MetaphaseState和AnaphaseState的实现类似。

在EndState中,End()方法表示战争结束的行为,输出“战争结束”。

在主函数中,创建一个War对象,初始状态为ProphaseState。通过循环不断改变战争持续时间,并调用GetState()方法获取当前状态并输出相应信息。最后删除War对象。

class War;
class State 
{
public:
	virtual void Prophase() {}
	virtual void Metaphase() {}
	virtual void Anaphase() {}
	virtual void End() {}
	virtual void CurrentState(War *war) {}
};
//战争
class War
{
private:
	State *m_state;  //目前状态
	int m_days;      //战争持续时间
public:
	War(State *state): m_state(state), m_days(0) {}
	~War() { delete m_state; }
	int GetDays() { return m_days; }
	void SetDays(int days) { m_days = days; }
	void SetState(State *state) { delete m_state; m_state = state; }
	void GetState() { m_state->CurrentState(this); }
};

//战争结束
class EndState: public State
{
public:
	void End(War *war) //结束阶段的具体行为
	{
		cout<<"战争结束"<<endl;
	}
	void CurrentState(War *war) { End(war); }
};
//后期
class AnaphaseState: public State
{
public:
	void Anaphase(War *war) //后期的具体行为
	{
		if(war->GetDays() < 30)
			cout<<"第"<<war->GetDays()<<"天:战争后期,双方拼死一搏"<<endl;
		else
		{
			war->SetState(new EndState());
			war->GetState();
		}
	}
	void CurrentState(War *war) { Anaphase(war); }
};
//中期
class MetaphaseState: public State
{
public:
	void Metaphase(War *war) //中期的具体行为
	{
		if(war->GetDays() < 20)
			cout<<"第"<<war->GetDays()<<"天:战争中期,进入相持阶段,双发各有损耗"<<endl;
		else
		{
			war->SetState(new AnaphaseState());
			war->GetState();
		}
	}
	void CurrentState(War *war) { Metaphase(war); }
};
//前期
class ProphaseState: public State
{
public:
	void Prophase(War *war)  //前期的具体行为
	{
		if(war->GetDays() < 10)
			cout<<"第"<<war->GetDays()<<"天:战争初期,双方你来我往,互相试探对方"<<endl;
		else
		{
			war->SetState(new MetaphaseState());
			war->GetState();
		}
	}
	void CurrentState(War *war) { Prophase(war); }
};

//测试案例
int main()
{
	War *war = new War(new ProphaseState());
	for(int i = 1; i < 40;i += 5)
	{
		war->SetDays(i);
		war->GetState();
	}
	delete war;
	return 0;
}

go实现

在Go语言中,我们使用接口来定义状态类和上下文类的方法,实现了与C++中类似的状态模式。在该示例代码中,我们定义了State接口,War结构体表示上下文类,ProphaseState、MetaphaseState、AnaphaseState和EndState分别表示具体的状态类。

在War结构体中,days表示战争持续时间,state表示当前的状态。GetState()方法用于获取当前状态,并调用CurrentState()方法,SetDays()方法用于设置战争持续时间。

在State接口中,定义了Prophase()、Metaphase()、Anaphase()、End()和CurrentState()方法,并在具体的状态类中实现了这些方法。

**package main

import "fmt"

// State interface
type State interface {
	Prophase(war *War)
	Metaphase(war *War)
	Anaphase(war *War)
	End(war *War)
	CurrentState(war *War)
}

// War context
type War struct {
	state State    // current state
	days  int      // duration of the war
}

// ProphaseState represents the state of the war in the prophase stage
type ProphaseState struct{}

func (p *ProphaseState) Prophase(war *War) {
	if war.days < 10 {
		fmt.Printf("Day %d: War in prophase stage, both sides are testing each other.\n", war.days)
	} else {
		war.state = &MetaphaseState{}
		war.state.CurrentState(war)
	}
}

func (p *ProphaseState) Metaphase(war *War) {}

func (p *ProphaseState) Anaphase(war *War) {}

func (p *ProphaseState) End(war *War) {}

func (p *ProphaseState) CurrentState(war *War) {
	war.state = p
	war.state.Prophase(war)
}

// MetaphaseState represents the state of the war in the metaphase stage
type MetaphaseState struct{}

func (m *MetaphaseState) Prophase(war *War) {}

func (m *MetaphaseState) Metaphase(war *War) {
	if war.days < 20 {
		fmt.Printf("Day %d: War in metaphase stage, both sides are engaged in a stalemate.\n", war.days)
	} else {
		war.state = &AnaphaseState{}
		war.state.CurrentState(war)
	}
}

func (m *MetaphaseState) Anaphase(war *War) {}

func (m *MetaphaseState) End(war *War) {}

func (m *MetaphaseState) CurrentState(war *War) {
	war.state = m
	war.state.Metaphase(war)
}

// AnaphaseState represents the state of the war in the anaphase stage
type AnaphaseState struct{}

func (a *AnaphaseState) Prophase(war *War) {}

func (a *AnaphaseState) Metaphase(war *War) {}

func (a *AnaphaseState) Anaphase(war *War) {
	if war.days < 30 {
		fmt.Printf("Day %d: War in anaphase stage, both sides are fighting to the death.\n", war.days)
	} else {
		war.state = &EndState{}
		war.state.CurrentState(war)
	}
}

func (a *AnaphaseState) End(war *War) {}

func (a *AnaphaseState) CurrentState(war *War) {
	war.state = a
	war.state.Anaphase(war)
}

// EndState represents the state of the war in the end stage
type EndState struct{}

func (e *EndState) Prophase(war *War) {}

func (e *EndState) Metaphase(war *War) {}

func (e *EndState) Anaphase(war *War) {}

func (e *EndState) End(war *War) {
	fmt.Println("War has ended.")
}

func (e *EndState) CurrentState(war *War) {
	war.state = e
	war.state.End(war)
}

// GetState gets the current state of the war
func (w *War) GetState() {
	w.state.CurrentState(w)
}

// SetDays sets the duration of the war
func (w *War) SetDays(days int) {
	w.days = days
}

// NewWar creates a new War object with the initial state set to prophase
func NewWar() *War {
	return &War{
		state: &ProphaseState{},
	}
}

func main() {
	war := NewWar()
	for i := 1; i < 40; i += 5 {
		war.SetDays(i)
		war.GetState()
	}
}**

六、策略模式

场景

当你想使用对象中各种不同的算法变体, 并希望能在运行时切换算法时, 可使用策略模式。
策略模式让你能够将对象关联至可以不同方式执行特定子任务的不同子对象, 从而以间接方式在运行时更改对象行为。

当你有许多仅在执行某些行为时略有不同的相似类时, 可使用策略模式。
策略模式让你能将不同行为抽取到一个独立类层次结构中, 并将原始类组合成同一个, 从而减少重复代码。

如果算法在上下文的逻辑中不是特别重要, 使用该模式能将类的业务逻辑与其算法实现细节隔离开来。
策略模式让你能将各种算法的代码、 内部数据和依赖关系与其他代码隔离开来。 不同客户端可通过一个简单接口执行算法, 并能在运行时进行切换。

当类中使用了复杂条件运算符以在同一算法的不同变体中切换时, 可使用该模式。
策略模式将所有继承自同样接口的算法抽取到独立类中, 因此不再需要条件语句。 原始对象并不实现所有算法的变体, 而是将执行工作委派给其中的一个独立算法对象。

原理

策略模式是指定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。也就是说这些算法所完成的功能一样,对外的接口一样,只是各自实现上存在差异。用策略模式来封装算法,效果比较好。下面以高速缓存(Cache)的替换算法为例,实现策略模式。
ReplaceAlgorithm是一个抽象类,定义了算法的接口,有三个类继承自这个抽象类,也就是具体的算法实现。Cache类中需要使用替换算法,因此维护了一个 ReplaceAlgorithm的对象。这个UML图的结构就是策略模式的典型结构。下面根据UML图,给出相应的实现。
设计模式—行为模式深度分析与实现【c++与golang】【万字总结】_第5张图片

c++实现

定义了一个抽象接口ReplaceAlgorithm,其中包含一个纯虚函数Replace()。

实现了三个具体的替换算法类:LRU_ReplaceAlgorithm、FIFO_ReplaceAlgorithm和Random_ReplaceAlgorithm,它们分别继承自ReplaceAlgorithm,并实现了Replace()方法。

在Cache类中,定义了一个私有成员变量m_ra,该变量是一个指向ReplaceAlgorithm的指针。

Cache类的构造函数根据传入的标签ra来选择具体的替换算法对象,并将其赋值给m_ra成员变量。如果标签是LRU,就创建一个LRU_ReplaceAlgorithm对象;如果是FIFO,就创建一个FIFO_ReplaceAlgorithm对象;如果是RANDOM,就创建一个Random_ReplaceAlgorithm对象。

Cache类的析构函数释放了m_ra指针所指向的内存。

Cache类还提供了一个Replace()方法,该方法调用了m_ra指针所指向的替换算法对象的Replace()方法。

在主函数main()中,创建了一个Cache对象cache,并传入标签LRU。然后调用了cache.Replace()方法。

//抽象接口
class ReplaceAlgorithm
{
public:
	virtual void Replace() = 0;
};
//三种具体的替换算法
class LRU_ReplaceAlgorithm : public ReplaceAlgorithm
{
public:
	void Replace() { cout<<"Least Recently Used replace algorithm"<<endl; }
};
 
class FIFO_ReplaceAlgorithm : public ReplaceAlgorithm
{
public:
	void Replace() { cout<<"First in First out replace algorithm"<<endl; }
};
class Random_ReplaceAlgorithm: public ReplaceAlgorithm
{
public:
	void Replace() { cout<<"Random replace algorithm"<<endl; }
};
//Cache需要用到替换算法
enum RA {LRU, FIFO, RANDOM}; //标签
class Cache
{
private:
	ReplaceAlgorithm *m_ra;
public:
	Cache(enum RA ra) 
	{ 
		if(ra == LRU)
			m_ra = new LRU_ReplaceAlgorithm();
		else if(ra == FIFO)
			m_ra = new FIFO_ReplaceAlgorithm();
		else if(ra == RANDOM)
			m_ra = new Random_ReplaceAlgorithm();
		else 
			m_ra = NULL;
	}
	~Cache() { delete m_ra; }
	void Replace() { m_ra->Replace(); }
};
int main()
{
	Cache cache(LRU); //指定标签即可
	cache.Replace();
	return 0;
}


go实现

在这个示例中,我们使用了Go语言的面向对象特性来实现抽象接口和具体的替换算法。ReplaceAlgorithm接口定义了Replace()方法,而LRU_ReplaceAlgorithm、FIFO_ReplaceAlgorithm和Random_ReplaceAlgorithm结构体分别实现了该接口的方法。

Cache结构体包含了一个ReplaceAlgorithm接口类型的字段ra。NewCache函数根据传入的替换算法类型字符串来创建对应的具体替换算法对象,并将其赋值给ra字段。Replace()方法调用了ra.Replace()来执行具体的替换算法。

在main()函数中,我们创建了一个Cache对象cache,并传入标签"LRU"来选择LRU替换算法。然后调用了cache.Replace()来执行替换算法,输出"Least Recently Used replace algorithm"。

package main

import "fmt"

// 抽象接口
type ReplaceAlgorithm interface {
	Replace()
}

// 三种具体的替换算法
type LRU_ReplaceAlgorithm struct{}

func (lru *LRU_ReplaceAlgorithm) Replace() {
	fmt.Println("Least Recently Used replace algorithm")
}

type FIFO_ReplaceAlgorithm struct{}

func (fifo *FIFO_ReplaceAlgorithm) Replace() {
	fmt.Println("First in First out replace algorithm")
}

type Random_ReplaceAlgorithm struct{}

func (random *Random_ReplaceAlgorithm) Replace() {
	fmt.Println("Random replace algorithm")
}

// Cache需要用到替换算法
type Cache struct {
	ra ReplaceAlgorithm
}

func NewCache(raType string) *Cache {
	var ra ReplaceAlgorithm
	switch raType {
	case "LRU":
		ra = &LRU_ReplaceAlgorithm{}
	case "FIFO":
		ra = &FIFO_ReplaceAlgorithm{}
	case "RANDOM":
		ra = &Random_ReplaceAlgorithm{}
	default:
		ra = nil
	}

	return &Cache{ra: ra}
}

func (cache *Cache) Replace() {
	cache.ra.Replace()
}

func main() {
	cache := NewCache("LRU")
	cache.Replace()
}

总结

责任链模式、 命令模式、 中介者模式和观察者模式用于处理请求发送者和接收者之间的不同连接方式:

1、责任链按照顺序将请求动态传递给一系列的潜在接收者, 直至其中一名接收者对请求进行处理。
2、命令在发送者和请求者之间建立单向连接。
3、中介者清除了发送者和请求者之间的直接连接, 强制它们通过一个中介对象进行间接沟通。
4、观察者允许接收者动态地订阅或取消接收请求。

桥接模式、 状态模式和策略模式 (在某种程度上包括适配器模式) 模式的接口非常相似。 实际上, 它们都基于组合模式——即将工作委派给其他对象, 不过也各自解决了不同的问题。 模式并不只是以特定方式组织代码的配方, 你还可以使用它们来和其他开发者讨论模式所解决的问题。

状态可被视为策略的扩展。 两者都基于组合机制: 它们都通过将部分工作委派给 “帮手” 对象来改变其在不同情景下的行为。 策略使得这些对象相互之间完全独立, 它们不知道其他对象的存在。 但状态模式没有限制具体状态之间的依赖, 且允许它们自行改变在不同情景下的状态。

你可能感兴趣的:(设计模式,c++,golang)