设计模式——策略模式

简要来说,策略模式用于算法的自由切换和拓展,定义了一系列的算法并将它们封装起来,相互之间可以替换,而使用算法功能的客户端不会受到影响。

  • 为什么要运用这个模式:

我们在完成一项任务或者是达成一个目标,可以有很多种方式,这些方式我们称之为策略,根据需要我们选择不同的策略来完成这个事件。在软件开发过程中,我们实现某一个功能有很多种途径,对此可以使用策略模式来使得系统可以灵活的选择各项途径(策略),也可以方便地增加途径。

  • 策略模式定义:

定义一系列算法,将每一个算法封装起来,并让它们可以相互转换。策略模式让算法独立于使用它的客户而变化,也称政策模式。策略模式是一种对象行为型模式。

  • 模式结构:


Context(环境类):使用算法的角色,在实现某个方法时可以采用多种策略。在环境类中维护一个对抽象策略类的引用实例,用于定义所采用的策略。

Strategy(抽象策略类):为所支持的算法声明了抽象方法,是所有策略类的父类,可以是抽象类或者接口。环境类使用在其中声明的方法调用在具体策略类中实现的算法。

ConcreteStrategy(具体策略类):实现了再抽象策略类中定义的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务的功能。

总的概括来说,就是定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法,为了保证这些策略的一致性,使用一个抽象的策略类(或者是接口)来做算法的定义,而这些具体的每一种算法则对应一个具体的策略类。定义一个环境类在解决问题时采用多种策略,在其中维护一个对抽象策略类的引用实例。


下面是我写的一个关于商场促销收银的一个例子:

商场中促销商品,暂时有三种计费的方法:正常价格出售、满300减100、打八折;之后可能会有其他的促销方案或者删除某种促销方案,要求制作一个计算价格的客户端,同时也要方便今后的促销算法维护。

这是具体的工程结构,为了方便理解把包名与对应的模式角色名称相对应

Strategy(抽象策略类):抽象类包含一个抽象方法,用来定义收银计费的算法

package com.strategy;

public abstract class CashSuper {

	public abstract double acceptCash(double money);
	
}

ConcreteStrategy(具体策略类):三种促销计费方法的具体实现,继承抽象策略类

package com.concreteStrategy;

import com.strategy.CashSuper;

public class CashNormal extends CashSuper {

	@Override
	public double acceptCash(double money) {
		// TODO Auto-generated method stub
		return money;
	}

}
package com.concreteStrategy;

import com.strategy.CashSuper;

public class CashRebate extends CashSuper {

	private double moneyRebate = 1d;
	
	public CashRebate(String moneyRebate) {
		// TODO Auto-generated constructor stub
		this.moneyRebate = Double.parseDouble(moneyRebate);
	}
	
	@Override
	public double acceptCash(double money) {
		// TODO Auto-generated method stub
		return money*moneyRebate;
	}

}
package com.concreteStrategy;

import com.strategy.CashSuper;

public class CashReturn extends CashSuper {

	private double moneyCondition = 0.0d;
	private double moneyReturn = 0.0d;
	
	public CashReturn(String moneyCondition, String moneyReturn) {
		// TODO Auto-generated constructor stub
		this.moneyCondition = Double.parseDouble(moneyCondition);
		this.moneyReturn = Double.parseDouble(moneyReturn);
	}
	
	@Override
	public double acceptCash(double money) {
		// TODO Auto-generated method stub
		double result = money;
		if (money >= moneyCondition) {
			result = money-Math.floor(money/moneyCondition)*moneyReturn;
		}
		return result;
	}

}

Context(环境类):通过构造方法,传入具体的收费策略,根据收费策略的不同,获得计算结果

package com.context;

import com.strategy.CashSuper;

public class Context {
	
	private CashSuper cashSuper;
	
	public Context(CashSuper cs) {
		// TODO Auto-generated constructor stub
		this.cashSuper = cs;
	}
	
	public double getResult(double money) {
		return cashSuper.acceptCash(money);
	}

}

Client(客户端):

package com.client;

import java.util.Scanner;

import com.concreteStrategy.CashNormal;
import com.concreteStrategy.CashRebate;
import com.concreteStrategy.CashReturn;
import com.context.Context;

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Context context = null;
		
		System.out.println("请输入折扣类型:");
		@SuppressWarnings("resource")
		Scanner scanner1 = new Scanner(System.in);
		String select = scanner1.nextLine();
		
		System.out.println("请输入商品总价:");
		@SuppressWarnings("resource")
		Scanner scanner2 = new Scanner(System.in);
		double total = scanner2.nextDouble();
		
		switch (select) {
		case "正常收费":
			context = new Context(new CashNormal());
			break;
		case "满300减100":
			context = new Context(new CashReturn("300", "100"));
			break;
		case "打八折":
			context = new Context(new CashRebate("0.8"));
			break;
		default:
			break;
		}
		
		System.out.println("最终价格:"+context.getResult(total));
		
	}

}
  • 策略模式的优点:

对“开闭原则”完美支持,在不修改原有系统的基础上可以更换算法或者增加新的算法,能很好的管理算法族,提高了代码的复用性,是一种替换继承,避免多重条件转移语句的实现方式。

  • 策略模式的缺点:

客户端必须知道所有的策略类,并理解其区别,同时在一定程度上增加了系统中类的个数,可能会存在很多个策略类。

  • 适用情况:

一个系统里面有很多类,它们之间的区别仅在于它们的行为,使用策略模式可以动态地让一个对象在许多行为中选择一种行为;一个系统需要动态地在几种算法中选择一种;避免使用难以维护的多重条件选择语句;希望在具体策略类中封装算法和相关的数据结构。

  • 策略模式结合简单工厂模式:

在基本的策略模式中,选择所用具体实现类的职责由客户端承担,并转给策略模式的Context对象,这本身并没有解除客户端需要选择判断的压力,对此可以结合简单工厂模式,选择具体实现的职责由Context类承担,这就减轻了客户端的职责。

更改Context.java和Client.java如下

ContextWithFactory.java

package com.context;

import com.concreteStrategy.CashNormal;
import com.concreteStrategy.CashRebate;
import com.concreteStrategy.CashReturn;
import com.strategy.CashSuper;

public class ContextWithFactory {
	
	CashSuper cashSuper = null;
	
	public ContextWithFactory(String type) {
		// TODO Auto-generated constructor stub
		switch (type) {
		case "正常收费":
			cashSuper = new CashNormal();
			break;
		case "满300减100":
			cashSuper = new CashReturn("300", "100");
			break;
		case "打八折":
			cashSuper = new CashRebate("0.8");
			break;
		default:
			break;
		}
	}
	
	public double getResult(double money) {
		return cashSuper.acceptCash(money);
	}

}

ClientByFactory.java

package com.client;

import java.util.Scanner;

import com.context.ContextWithFactory;;

public class ClientByFactory {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		ContextWithFactory context = null;
		
		System.out.println("请输入折扣类型:");
		@SuppressWarnings("resource")
		Scanner scanner1 = new Scanner(System.in);
		String select = scanner1.nextLine();
		
		System.out.println("请输入商品总价:");
		@SuppressWarnings("resource")
		Scanner scanner2 = new Scanner(System.in);
		double total = scanner2.nextDouble();
		
		context = new ContextWithFactory(select);
		
		System.out.println("最终价格:"+context.getResult(total));
		
	}

}
  • 策略模式解析

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

策略模式中的Strategy类层次为Context定义了一系列可重用的算法或行为。继承有助于析取出这些算法中的公共功能。

策略模式简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值