大话设计模式读书笔记——策略模式


1. 策略模式

1.1 概念

策略模式(Strategy)定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户。

1.2 需求

现在需要实现一个商场收银软件,拥有打折和返利功能,并且营业员根据客户所购买商品的单价和数量,在客户端进行收费。

1.3 简单工厂模式实现

1.3.1 收费抽象类

public abstract class CashSuper{
	//收取费用的抽象方法,参数为单价和数量
	public abstract double acceptCash(double price,int num);
}

1.3.2 正常收费

public class CashNormal extends CashSuper {
	//原价返回
	public double acceptCash(double price,int num){
		return price*num;
	}
}

1.3.3 打折收费

public class CashRebate extends CashSuper {
	private double moneyRebate = 1d;
	//初始化时必须输入折扣率,八折就输入0.8
	public CashRebate(double moneyRebate){
		this.moneyRebate = moneyRebate;
	}

	//计算收费时需要在原价的基础上乘以折扣率
	public double acceptCash(double price,int num){
		return price*num*this.moneyRebate;
	}
}

1.3.4 返利

public class CashReturn extends CashSuper(){
	private double moneyCondition = 0d;//返利条件
	private double moneyReturn = 0d;//返利值

	//返利收费。初始化时需要输入返利条件和返利值
	public CashReturn(double moneyCondition,double moneyReturn){
		this.moneyCondition = moneyCondition;
		this.moneyReturn = moneyReturn
	}

	//计算收费时,当达到返利条件,就原价减去返利值
	public double acceptCash(double price,int num){
		double result = price * num;
		if(moneyCondition>0 && result >= moneyCondition){
			result = result - Math.floor(result / moneyCondition) * moneyReturn;
			return result;
		}
	}
}

1.3.5 收费工厂

//收费工厂
public class CashFactory {
	public static CashSuper createCashAccpet(int cashType){
		CashSuper cs = null;
		switch(cashType){
			case 1:
				cs = new CashNormal();	//正常收费
				break;
			case 2:
				cs = new CashRebate(0.8d);	//打八折
				break;
			case 3:
				cs = new CashRebate(0.7d);	//打七折
				break;
			case 4:
				cs = new CashReturn(300d,100d);	//满300返100
				break;
		}
		return cs;
	}
}

1.3.6 客户端程序主要部分

//简单工厂模式根据discount的数字选择合适的收费类生成实例
CashSuper csuper = CashFactory.createCashAccept(discount);
//通过多态,可以根据不同收费策略计算得到收费的结果
totalPrices = csuper.acceptCash(price,num);
total = total + totalPrices;

1.3.7 UML类图

在这里插入图片描述

1.3.8 弊端

简单工厂模式虽然也能解决这个问题,但这个模式只是解决对象的创建问题,而且由于工厂本身包括所有的收费方式,商场是可能经常性地更改打折额度和返利额度,每次维护或扩展收费方式都要改动这个工厂,以致代码需重新编译部署,这真的是很糟糕的处理方式,所以用它不是最好的办法。面对算法的时常变动,应该有更好的办法。因此下面介绍另外一种设计模式——策略模式

1.4 策略模式案例

商场收银时如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法对象,这没有错,但算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,这就是变化点,而封装变化点是我们面向对象的一种很重要的思维方式。接下来介绍策略模式的结构图和基本代码。

1.4.1 UML类图

在这里插入图片描述

1.4.2 基本代码

1.4.2.1 抽象算法类
//抽象算法类
abstract class Strategy{
	//算法方法
	public abstract void algorithmInterface();
}
1.4.2.2 具体算法类

具体算法类,封装了具体的算法或行为,继承于Strategy

//具体算法类A
class ConcreteStrategyA extends Strategy{
	//算法A实现方法
	public void algorithmInterface(){
		System.out.println("算法A实现");
	}
}
//具体算法类B
class ConcreteStrategyB extends Strategy{
	//算法B实现方法
	public void algorithmInterface(){
		System.out.println("算法B实现");
	}
}
//具体算法类C
class ConcreteStrategyB extends Strategy{
	//算法C实现方法
	public void algorithmInterface(){
		System.out.println("算法C实现");
	}
}
1.4.2.3 上下文Context类

Context类,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用。

class Context{
	Strategy strategy;
	//初始化时,传入具体的策略对象
	public Context(Strategy strategy){
		this.strategy = strategy;
	}
	//上下文接口
	public void contextInterface(){
		//根据具体的策略对象,调用其算法的方法
		strategy.algorithmInterface();
	}
}
1.4.2.4 客户端代码
	Context context;

	//由于实例化不同策略,所以最终调用gcontext.contextInterface时,
	//所获得的结果不同
	context = new Context(new ConcreteStrategyA());
	context.contextInterface();
	
	context = new Context(new ConcreteStrategyB());
	context.contextInterface();

	context = new Context(new ConcreteStrategyC());
	context.contextInterface();

1.5 策略模式实现

根据策略模式的思想,CashSuper类为抽象策略,而正常收费CashNormal、打折收费CashRebate和返利收费CashReturn就是三个具体策略,也就是策略模式中说的具体算法。

1.5.1 CashContext类

public class CashContext{
	private CashSuper cs;

	//通过构造方法,传入具体的收费策略
	public CashContext(CashSuper csuper){
		this.cs = csuper;
	}

	public double getResult(double price,int num){
		//根据收费策略的不同,获得计算结果
		return this.cs.acceptCash(price,num);
	}
}

1.5.2 客户端主要代码

	CashContext cc = null;

	//根据用户输入,将对应的策略对象作为参数传入CashContext对象中
	switch(discount){
		case 1:
			cc = new CashContext(new CashNormal());
			break;
		case 2:
			cc = new CashContext(new CashRebate(0.8d));
			break;
		case 3:
			cc = new CashContext(new CashRebate(0.7d));
			break;
		case 4:
			cc = new CashContext(new CashReturn(300d,100d));
			break;
	}
	//通过Context的getResult方法的调用可以获得收取费用的结果
	//让具体算法和客户进行了隔离
	totalPrices = cc.getResult(price,num);
	total = total + totalPrices;

1.5.3 弊端

通过上述代码,这样又回到了原来的样子:在客户端判断用哪一个方法。因此,我们需要将判断的过程从客户端程序转移走,这时候就需要将策略模式的Context简单工厂相结合

1.6 策略和简单工厂结合

1.6.1 改造后的CashContext

public class CashContext{
	private CashSuper cs;
	
	//通过构造方法,传入具体的收费策略
	public CashContext(int cashType){
		switch(cashType){
		case 1:
			this.cs = new CashNormal();
			break;
		case 2:
			this.cs = new CashRebate(0.8d);
			break;
		case 3:
			this.cs = new CashRebate(0.7d);
			break;
		case 4:
			this.cs = new CashReturn(300d,100d);
			break;
		}
	}
	
	public double getResult(double price,int num){
		//根据收费策略的不同,获取计算结果
		return this.cs.accpetCash(price,num)
	}
}

1.6.2 客户端代码

	//根据用户输入,将对应的策略对象作为参数传入CashContext对象中
	CashContext cc = new CashContext(discount);

	//通过Context的getResult方法的调用,获取收取费用的结果,从而让具体算法和客户进行隔离
	totalPrices = cc.getResult(price,num);
	total = total + totalPrices;

1.6.3 对比

	//简单工厂模式的用法
	CashSuper csuper = CashFactory.createCashAccept(discount);
	totalPrices = csuper.acceptCash(price,num)

	//策略模式与简单工厂结合的用法
	CashContext cc new CashContext(discount);
	totalPrices = cc.getResult(price,number)

简单工厂模式我需要让客户端认识两个类,CashSuperCashFactory,而策略模式与简单工厂结合的用法,客户端就只需要认识一个类CashContext就可以了,耦合更加降低。
在这里插入图片描述
客户端中实例化的是CashContext的对象,调用的是CashContext的方法GetResult,这使得具体的收费算法彻底地与客户端分离。连算法的父类CashSuper都不让客户端认识了,从而降低耦合度。

2. 总结

策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
那么它的优点如下:

  1. 策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能
  2. 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试
  3. 当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句,因此,策略模式封装了变化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

头盔程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值