设计模式:策略模式

1. 什么是策略模式?

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。该模式使得算法可独立于使用它的场景而变化。

策略模式的本质:分离算法,选择实现

策略模式的功能:把具体的算法实现从具体的业务处理中独立出来,把它们实现成为单独的算法类,从而形成一系列算法,并让这些算法可以相互替换。

策略模式的重心不是实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。

唯一性:在运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地切换在不同的策略实现中切换,但是同时只能使用一个。

策略算法是相同行为的不同实现。

比如:不同的人去买票,学生是半价票,军人是免费票,成年人是全价票,那么现在是同一件行为,不同的身份的人去完成则就会有不同的实现。

class People
{
public:
	virtual void BuyTicket() = 0;
};

class Student : public People
{
public:
	virtual void BuyTicket()
	{
		cout << "学生票半价" << endl;
	}
};

class Solider : public People
{
public:
	virtual void BuyTicket()
	{
		cout << "军人票免费" << endl;
	}
};

class Ordinary : public People
{
public:
	virtual void BuyTicket()
	{
		cout << "普通人全票" << endl;
	}
};

int main()
{
	People* s = new Student();

	s->BuyTicket();

	delete s;
	return 0;
}

上述情况是客户端创建了具体的策略对象;

调用顺序:
(1)客户端来选择并创建具体的策略对象;
(2)客户端创建上下文;
(3)客户端就可以调用上下文的方法来执行功能,在调用的时候,客户端需要传入相应的参数;
(4)接收到客户端的请求,调用相应的策略方法。

在这里插入图片描述

2. 谁来选择具体的策略算法呢?

客户端可以选择,如果客户端不选择,则由上下文来选择具体的策略算法。

一般在比较复杂多变的情况下,传递上下文作为参数给策略对象:

比如在支付工资的适合,有许多的支付方式:人民币现金、美元支付、银行卡转账汇款、甚至还有工资转股权等;

这样的情况下,随着不同的工资支付方式的改变,那么就需要更好更方便的切换支付方式。此时就可以选择策略模式,但是工资支付时,不同的支付方式需要的参数个数是不相同的,不可能每增加一个支付方式,就修改全部的代码,那么就相当复杂了。

一个解决方案就是:将上下文作参数传递给策略对象

2.1 上下文做参数传递给策略对象

class PayMentContext;
class PayStrategy;

class PayStrategy
{
public:
	virtual void pay(PayMentContext* ctx) = 0;
};

class PayMentContext
{
public:
	PayMentContext(string name, double money, PayStrategy* strategy)
		:_username(name)
		, _money(money)
		, _strategy(strategy)
	{}

	virtual void paynow()
	{
		_strategy->pay(this);
	}

	double GetMoney()
	{
		return _money;
	}

	string GetName()
	{
		return _username;
	}

private:
	string _username;
	double _money;
	PayStrategy* _strategy;
};


class PayMentContextCard : public PayMentContext
{
public:
	PayMentContextCard(string name, double money, string count, PayStrategy* strategy)
		:PayMentContext(name, money, strategy)
		,_count(count)
	{}
	string GetConut()
	{
		return _count;
	}
private:
	string _count;//账户
};

class PayDollarStrategy : public PayStrategy
{
public:
	virtual void pay(PayMentContext* ctx)
	{
		cout << "现在给:" << ctx->GetName() << "美元支付" << ctx->GetMoney() << "元" << endl;
	}
};

class PayCardStrategy : public PayStrategy
{
public:
	virtual void pay(PayMentContext* ctx)
	{
		PayMentContextCard* tmp = dynamic_cast<PayMentContextCard*>(ctx);
		cout << "现在给:" << ctx->GetName() << "的" << tmp->GetConut() << "的账号支付了" << ctx->GetMoney() << "元" << endl;
	}
};

int main()
{
	PayStrategy* s = new PayDollarStrategy();
	PayMentContext* s1 = new PayMentContext("小王", 8000, s);
	s1->paynow();

	PayStrategy* s2 = new PayCardStrategy();
	PayMentContext* s3 = new PayMentContextCard("小刘",8000,"0671256", s2);
	s3->paynow();


	delete s;
	delete s1;
	delete s2;
	delete s3;
	return 0;
}

容错恢复机制:程序运行的时候,正常情况下应该按照某种方式来执行,如果发生了错误的话,系统不会因此崩溃,也不是不能再继续往下运行,而是有容忍出错的能力,不但能容忍程序运行出现错误,还提供出现错误后的备用方案,也就是恢复机制,来代替正常执行的功能,使程序继续往下执行。

这里主要要判断错误,捕获异常,让代码在出现异常的情况下,不退出,选择以另一方式来执行。等到要来处理错误的时候,再将以另一方式执行的结果放入到原本的代码的正常下的逻辑中。

比如打开数据库失败了,但是不想让程序退出,则让代码通过策略模式,选择打开本地的文件,让数据写入到本地,最后再提交到数据库中。

3. 策略模式的优缺点:

优点:

  1. 定义一系列算法:这些算法之间可以相互替换,所以会为这个算法 提供一个公共的接口,以约束一系列算法要实现的功能;
  2. 避免多重条件语句:这一系列算法是平等的,写在一起就可以通过if-else结构来组织,使用策略模式就可以避免多重条件语句;
  3. 更好的扩展性:在策略模式中扩展新的策略实现非常容易,只要增加新的策略实现类,然后在使用策略的地方选择使用这个新策略实现就可以了;

缺点:

  1. 客户必须了解每种策略的不同:让客户端来选择具体使用哪一个策略,这就需要客户了解所有的策略,还要了解各种策略的功能和不同,这样就暴露了策略的具体实现;
  2. 增加了对象数目:将每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很多;
  3. 只适合扁平的算法结构:策略模式的算法都是平等的,可以相互替换,在每一个运行时刻,只能有一个算法被使用,这就限制了算法使用的层级,使用的适合不能嵌套使用。

4. 何时选用策略模式?

  1. 出现许多相关的类,仅仅只是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个来配置一个类的方法,实现算法的动态切换;
  2. 出现同一个算法,有很多不同实现的情况下,可以使用策略模式来把这些“不同的实现”实现成为一个算法类层次;
  3. 需要封装算法中,有与算法相关数据的情况下,可以使用策略模式来避免暴露这些跟算法相关的数据结构;
  4. 出现一个抽象了很多行为的类,并且是通过多个if-else来选择这些行为的情况下,可以使用策略模式来代替这些条件语句。
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值