简单理解常用设计模式(二)策略模式

写完简单工厂模式后,这里说说策略模式,厉害的观众已经发现了,简单工厂在有些场景非常不合适,例如市场营销的场景,商场打八折,打五折,满五百减一百,满两百减五十,不打折等。市场经常会变动这些策略,如果是简单工厂就需要每次都改动工厂类,用来添加新的策略,然后添加具体的实现类。这样每次变动都要修改项目,二次开发工作量很大,非常的不理想。

所以仔细思考。

每加一个策略,都加一个类,这样做好吗,面向对象不是类越多越好,类的划分是为了封装,但分装的基础是抽象,相同属性和功能的对象的抽象集合才是类。

这样思考发现,每个策略都是可以归类的,比如折扣类,满减类,原价类。这些类可以继承自一个促销类(父类),然后给调用者提供一个中间类,用来获得具体的策略实例,并且提供返回计算结果的方法(此方法使用策略实例执行对应的计算)。

这样调用者只需要new中间类,提供策略(促销)方式,对应的数字参数即可,通过中间类获取结果。

这样修改策略时只需要传入不同参数即可,不再需要修改代码。(添加不同策略类型的当然要修改代码,但这种变动很少,第一次开发基本就能确定有多少策略类型)

原书UML如下:


原书案例如下:



看到这里,问题来了,假如笔者是二次开发的程序员,看到封装的方法只是返回中间类对象,并且传参内容还是不好记忆的对象,传参的逻辑也要自己编写,会开喷的。

所以这里笔者参考原书又结合了简单工厂模式,将逻辑放到工厂类(结合中间类),传参改为便于记忆的数字(当然也可以是便于理解的单词)

调用类如下:

package com.gcc.strategyModel;

public class TestCash {
	
	/**
	 * CashContext是策略模式和抽象工厂的组合
	 * CashContext的辅助方法完成向上转型后对象的方法调用,900为原价
	 */
	public static void main(String[] args) throws Exception {
		
		CashContext cash1 = new CashContext(1);
		double result1 = cash1.getResult(900);
		System.out.println("原价为:"+result1);
		
		CashContext cash2 = new CashContext(2);
		double result2 = cash2.getResult(900);
		System.out.println("折后为:"+result2);
		
		CashContext cash3 = new CashContext(3);
		double result3 = cash3.getResult(900);
		System.out.println("满减后为:"+result3);
		
	}
	
}

工厂和策略结合的类如下:

package com.gcc.strategyModel;

import java.util.Properties;

/**
 *工厂模式和策略模式化结合 
 *
 */
public class CashContext {

	private CashSuper cs=null;

	public CashContext() {
		super();
	}

	/**
	 * 抽象工厂
	 * @param condition
	 * @throws ClassNotFoundException 
	 */
	public CashContext(int condition) throws Exception {
		//规定字典1为正常,2为折扣,3为返利,通过配置文件获得数据
		Properties prop = new Properties();
		prop.load(CashContext.class.getResourceAsStream("cash.properties"));
		double conditionNum = Double.parseDouble(prop.getProperty("conditionNum"));
		double rebateNum = Double.parseDouble(prop.getProperty("rebateNum"));
		double discountNum = Double.parseDouble(prop.getProperty("discountNum"));

		switch (condition) {
		case 1:
			cs= new CashNormal();
			break;
		case 2:
			cs=(CashSuper) CashDiscount.class//另一种反射的写法
				.getConstructor(double.class)
				.newInstance(discountNum);
			break;
		case 3:
			cs=(CashSuper) Class
				.forName(prop.getProperty("rebate"))
				.getConstructor(double.class,double.class)
				.newInstance(conditionNum,rebateNum);
			break;
		default:
			break;
		}
	}
	
	public double getResult(double money){
		return cs.acceptCash(money);
	}
	
}

这里笔者有其它的想法,因为编写代码时突然想到java反射,配合配置文件能进一步优化代码,这里举例了两种反射方式,第一种是通过类名.class然后获得构造方法再执行实例化,第二种是通过Class.forName(),参数为需要实例化的类的全路径,然后获得构造方法再执行实例化。

笔者支持前者,原因是:这样做不仅更加简单,而且更安全,它会在编译期间得到检查(后者不会)。由于它取消了对方法调用的需要,所以执行的效率也会更高。

至于参数数据,这里通过读取配置文件的方式来获得,方便修改。

另外值得注意的是这里的获取结果方法,灵活运用了java的多态,使得一个方法有多种实现方式。

配置文件cash.properties如下:

normal=com.gcc.strategyModel.CashNormal
conditionNum=500
rebateNum=100
discountNum=0.9
rebate=com.gcc.strategyModel.CashRebate
discount=com.gcc.strategyModel.CashDiscount

促销(策略)父类以及子类如下:

package com.gcc.strategyModel;

public interface CashSuper {
	
	/**
	 * 实际消费现金接口方法
	 * @param money
	 * @return
	 */
	public double acceptCash(double money);
	
}
package com.gcc.strategyModel;

public class CashDiscount implements CashSuper {

	private double discount;//折扣
	
	
	public CashDiscount() {
		super();
	}

	public CashDiscount(double discount){
		this.discount=discount;
	}
	
	@Override
	public double acceptCash(double money) {
		return money*(this.discount);
	}

}
package com.gcc.strategyModel;

public class CashNormal implements CashSuper {

	@Override
	public double acceptCash(double money) {
		return money;
	}

	
}
package com.gcc.strategyModel;

public class CashRebate implements CashSuper {

	private double rebate;//返利
	private double condition;//返利条件
	
	public CashRebate() {
		super();
	}

	public CashRebate(double condition,double rebate){
		this.rebate=rebate;
		this.condition=condition;
	}

	/**
	 * 例如消费800大于返利条件500,则返利100,返回实际消费额为800-100=700
	 */
	@Override
	public double acceptCash(double money) {
		if(money>=this.condition){
			money=money-rebate;
		}
		return money;
	}

	
}

到此完毕

no,no!

可能有人会提出,为什么中间类不用之前的简单工厂的方式,即如下方式:

package com.gcc.strategyModel;

import java.util.Properties;

/**
 *工厂模式和策略模式化结合 
 *
 */
public class CashContextTest {

	/**
	 * 工厂
	 * @param condition
	 * @throws ClassNotFoundException 
	 */
	public static CashSuper createCash(int condition) throws Exception {
		CashSuper cs=null;
		//规定字典1为正常,2为折扣,3为返利,通过配置文件获得数据
		Properties prop = new Properties();
		prop.load(CashContextTest.class.getResourceAsStream("cash.properties"));
		double conditionNum = Double.parseDouble(prop.getProperty("conditionNum"));
		double rebateNum = Double.parseDouble(prop.getProperty("rebateNum"));
		double discountNum = Double.parseDouble(prop.getProperty("discountNum"));

		switch (condition) {
		case 1:
			cs= new CashNormal();
			break;
		case 2:
			cs=(CashSuper) CashDiscount.class//另一种反射的写法
				.getConstructor(double.class)
				.newInstance(discountNum);
			break;
		case 3:
			cs=(CashSuper) Class
				.forName(prop.getProperty("rebate"))
				.getConstructor(double.class,double.class)
				.newInstance(conditionNum,rebateNum);
			break;
		default:
			break;
		}
		return cs;
	}
	
}

调用如下:

		CashSuper cash4 = CashContextTest.createCash(2);
		double result4=cash4.acceptCash(900);
		System.out.println("折后为:"+result4)

这样做在笔者看来是更加便于理解,但实际上这在使用时不知不觉耦合了底层逻辑代码。

调用者需要多知道底层接口CashSuper,不符合尽量解耦的原则。

策略模式的另外巧妙的一点就在于在中间类Context里添加了可以重用的方法如getResult(double money),进一步完成了解耦。

完毕,感谢你的观看





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值