被说了很多遍的设计模式---策略模式

[把你的理性思维慢慢变成条件反射]

本文,我们讲介绍策略模式,文章主题结构与上位一致。惯例,先来看看我们示例工程的环境:

操作系统:win7 x64

其他软件:eclipse mars,jdk7

-------------------------------------------------------------------------------------------------------------------------------------

经典问题:

销售结算系统中的费用结算,在实际情况下,经常存在对于不同类型,级别的客户,或不同的商品,采用不同的折扣计价策略。

思路分析:

要点一:客户,存在不同类型。

要点二:商品,存在不同类型的折扣策略。

综合以上内容,系统中可能出现不同类型的调用方或者不同客户的参数。同时,运算方法存在多分支。

示例工程:


错误写法(1):

创建CalculatorDebate.java,具体内容如下:

package com.csdn.ingo.gof_strategy.base;

import java.util.Scanner;


public class CalculatorDebate {

	public enum Debate {
		// 通过括号赋值,而且必须带有一个参构造器和一个属性跟方法,否则编译出错
		// 赋值必须都赋值或都不赋值,不能一部分赋值一部分不赋值;如果不赋值则不能写构造器,赋值编译也出错
		_9("0.9"), _8("0.8"), _7("0.7"), _6("0.6"), _5("0.5");

		private final String value;

		// 构造器默认也只能是private, 从而保证构造函数只能在内部使用
		Debate(String value) {
			this.value = value;
		}

		public double getValue() {
			return Double.parseDouble(value);
		}
	}

	public static void main(String[] args) {
		double total = 0d;
		Scanner scan = new Scanner(System.in);
		
		while (true) {
			System.out.println("please enter number:");
			int numA = scan.nextInt();
			System.out.println("please enter prize:");
			int numB = scan.nextInt();
			System.out.println("please enter debate:");
			for(Debate debate : Debate.values()){
	            System.out.print(debate+",");
	        }
			String d = scan.next();
			Debate debate = Debate.valueOf("_"+d);
			total = (total + numA * numB);
			System.out.println("当前总价:" + total);
			System.out.println("折扣:" + debate.getValue());
			System.out.println("折后:" + total*debate.getValue());
		}
	}
}

错误原因:

虽然代码功能是正确的,并且能够满足不同的折扣策略。但是,请注意,这里的计算方法严格限制了只能结算一次,只能采用固定折扣计算方法,即9折,8折等类似的计算方法。而对于满减,根据客户级别的结算策略完全无法使用。并且,违反了我们第一篇文章介绍的“开闭原则”等诸多原则。故,这是一种极其不好的解决方案。

错误写法(2):


创建CachFactory.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.one;

public class CachFactory {
	public static CashSuper createCashFactory(String type){
		CashSuper cs = null;
		switch(type){
		case "1":
			cs = new CashNormal();
			break;
		case "mj":
			cs = new CashReturn("300", "100");
			break;
		case "2":
			cs = new CashRebate("0.8");
			break;
		}
		return cs;
	}
}
创建CashNormal.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.one;

public class CashNormal extends CashSuper{
	@Override
	public double acceptCash(double money) {
		return money;
	}

}
创建CashRebate.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.one;

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

}
创建CashReturn.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.one;

public class CashReturn extends CashSuper{
	private double moneyCondition = 0d;
	private double moneyReturn =0d;
	public CashReturn(String moneyCondition,String moneyReturn){
		this.moneyCondition = Double.parseDouble(moneyCondition);
		this.moneyReturn = Double.parseDouble(moneyReturn);
	}
	@Override
	public double acceptCash(double money) {
		double result = money;
		if(money>=moneyCondition){
			result = money-Math.floor(money/moneyCondition)*moneyReturn;
		}
		return result;
	}

}
创建CashSuper.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.one;

public abstract class CashSuper {
	public abstract double acceptCash(double money);
}
创建Window.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.one;

import java.util.Scanner;

public class Window {
	public static void main(String[] args) {
		double total = 0d;
		String isEnd = "y";
		Scanner scan = new Scanner(System.in);
		while(isEnd.equals("y")){
			System.out.println("please enter number:");
			int numA = scan.nextInt();
			System.out.println("please enter prize:");
			int numB = scan.nextInt();
			total = (total+numA*numB);
			System.out.println("当前总价:"+total);
			System.out.println("继续添加(y/n):");
			isEnd = scan.next();
		}
		CashSuper csuper = CachFactory.createCashFactory("2");
		total= csuper.acceptCash(total);
		System.out.println("折扣:"+"2");
		System.out.println("折后总价:"+total);
	}
}

错误原因:

version_one的代码,虽然在功能上实现了无限制的商品计价,并且在代码设计上讲不通的策略进行隔离,满足了“单一职责原则”,“开闭原则”等。并且使用前文的简单工厂模式加以实现。但是,我们需要注意谨慎简单工厂模式的使用。因为,该模式只是负责创建了不同类型的对象而已。细心的读者应该记得,我们前文介绍过简单工厂模式的限制条件:通过静态方法创建实例对象。换句话说,对于经常改变营销策略的销售系统,每次都需要重新开发,编译软件的代价是巨大的。所以,该方案的错误原因是不符合实际需求。

错误写法(2):


创建CashContext.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.three;


public class CashContext {
	private CashSuperStrategy cashSuperStrategy;

	public CashContext(CashSuperStrategy cashSuperStrategy) {
		this.cashSuperStrategy = cashSuperStrategy;
	}

	public double getResult(double money) {
		return cashSuperStrategy.acceptCash(money);
	}
}
创建CashNormal.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.three;

public class CashNormal extends CashSuperStrategy{
	@Override
	public double acceptCash(double money) {
		return money;
	}

}
创建CashRebate.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.three;

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

}
创建CashReturn.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.three;

public class CashReturn extends CashSuperStrategy{
	private double moneyCondition = 0d;
	private double moneyReturn =0d;
	public CashReturn(String moneyCondition,String moneyReturn){
		this.moneyCondition = Double.parseDouble(moneyCondition);
		this.moneyReturn = Double.parseDouble(moneyReturn);
	}
	@Override
	public double acceptCash(double money) {
		double result = money;
		if(money>=moneyCondition){
			result = money-Math.floor(money/moneyCondition)*moneyReturn;
		}
		return result;
	}

}

创建CashSuperStrategy.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.three;

public abstract class CashSuperStrategy {
	public abstract double acceptCash(double money);
}
创建Window.java文件,具体内容如下:
package com.csdn.ingo.gof_strategy.three;

import java.util.Scanner;

public class WIndow {
	public static void main(String[] args) {
		double total = 0d;
		String isEnd = "y";
		Scanner scan = new Scanner(System.in);
		while(isEnd.equals("y")){
			System.out.println("please enter number:");
			int numA = scan.nextInt();
			System.out.println("please enter prize:");
			int numB = scan.nextInt();
			total = (total+numA*numB);
			System.out.println("当前总价:"+total);
			System.out.println("继续添加(y/n):");
			isEnd = scan.next();
		}
		CashContext cs = null;
		String type = "fl";
		switch(type){
		case "1":
			cs = new CashContext(new CashNormal());
			break;
		case "fl":
			cs = new CashContext(new CashReturn("300", "100"));
			break;
		case "2":
			cs = new CashContext(new CashRebate("0.8"));
			break;
		}
		total= cs.getResult(total);
		System.out.println("折扣:"+"1");
		System.out.println("折后总价:"+total);
	}
}

错误原因:

version_three的已经实现了我们将要介绍策略模式的主体设计。但是,在Window.java文件中,出现了一个违反“单一职责原则”的写法,及策略的判断留给了客户端。在实际开发中,客户端是丝毫不需要关注这部分操作的,于是,我们需要将这部分内容转移到其他地方。

推荐写法:


修改CashContext.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.four;

import com.csdn.ingo.gof_strategy.three.CashNormal;
import com.csdn.ingo.gof_strategy.three.CashRebate;
import com.csdn.ingo.gof_strategy.three.CashReturn;
import com.csdn.ingo.gof_strategy.three.CashSuperStrategy;


public class CashContext {
	private CashSuperStrategy cashSuperStrategy;

	public CashContext(String type) {
		switch(type){
		case "1":
			cashSuperStrategy = new CashNormal();
			break;
		case "fl":
			cashSuperStrategy = new CashReturn("300", "100");
			break;
		case "2":
			cashSuperStrategy = new CashRebate("0.8");
			break;
		}
	}

	public double getResult(double money) {
		return cashSuperStrategy.acceptCash(money);
	}
}
修改Window.java文件,具体内容如下:

package com.csdn.ingo.gof_strategy.four;

import java.util.Scanner;

public class Window {
	public static void main(String[] args) {
		double total = 0d;
		String isEnd = "y";
		Scanner scan = new Scanner(System.in);
		while(isEnd.equals("y")){
			System.out.println("please enter number:");
			int numA = scan.nextInt();
			System.out.println("please enter prize:");
			int numB = scan.nextInt();
			total = (total+numA*numB);
			System.out.println("当前总价:"+total);
			System.out.println("继续添加(y/n):");
			isEnd = scan.next();
		}
		CashContext cs = new CashContext("2");
		total= cs.getResult(total);
		System.out.println("折后总价:"+total);
	}
}

推荐原因:

  • 符合“单一职责原则”,“开闭原则”等
  • 客户端,每个计算单元均是独立的,达到易扩展,易维护的目的。
  • 使用方调用不同策略的方式完全相同,减少了调用方与提供方之间的耦合度。并且提高了算法的重用次数。
  • 提供了单元测试能力,因为,每一个策略都有独立的实现过程。
  • Context类将条件语句封装为类的行为,将各种类型规则封装为统一的外在表现。有效减少了其他组件的功能及负担。特别的:对于条件语句的更高级的实现方式将在后文介绍(配置文件中设置具体子类名,反射机制),这里我们先将其独立为类行为,方便后续操作。敬请期待!


模式总结:

UML结构图:


概念总结:

策略模式:该模式定义了一系列算法的集合,分别封装为独立的单元,使得他们之间可以相互替换,并且使得算法的变化,不会影响到使用该算法的客户。

组成部分:Context,抽象父类,具体子类,三部分组成。

用法总结:

根据结构图,创建3个基本组成部分。客户端只需要传入算法必须的参数,Context负责决定具体策略,每个具体的实现类均独立。特别注意:客户端的代码中不要加入条件判断等多余代码,防止后期维护时产生不可预知的错误。另外,作为模式的讲解,我们暂时不介绍如何通过配置文件的方式实现动态策略调整,更多内容请各位看官自行学习。

反思:

应用场景:

  1. 系统中存在需要根据某些条件动态的选择一个算法实现,而这些算法具有某个相同的共性。换句话说,能够抽象出一个父类。
  2. 系统中某个对象的行为不确定,无法满足“单一职责原则”时。
  3. 希望对客户端屏蔽算法的实现细节,以提高安全性。

优点:

  1. 完全符合“开闭原则”。能够实现在不修改原有系统的同时,灵活的增加新的算法及实现类。
  2. 将众多的具体实现抽象为一个父类,一定程度上的减少了重复的公共代码。
  3. Context类将策略的选择功能完全的独立出来,同事避免了多重选择语句,方便的实现了后续的调整。
  4. 极大的简化了客户端的代码量,方便了客户端的使用。

缺点:

  1. 当具体的策略类非常多时,客户端虽然不需要显示的判断,但仍需要非常清楚不同策略之间的区别。
  2. 造成系统内过多的策略算法文件。
  3. 无法组合策略。客户端只能选择调用某一个具体的策略执行。

特别注意:

上文中,我们给出的示例代码是通过多重选择的形式来执行具体的实现类。在实际使用时,客户端也可以直接通过指明实现类的方式来调用具体的实现类(里氏代换原则)。

-------------------------------------------------------------------------------------------------------------------------------------

至此,被说了很多遍的设计模式---策略模式 结束


参考资料:

图书:《大话设计模式》

其他博文:http://blog.csdn.NET/lovelion/article/details/7563445

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值