前言
代码例子是来大话设计模式,本文主要是根据个人的理解,对书中的内容做学习笔记。如果个人理解的有问题,请各位大佬指正🙏。
基础遗忘了可以复习一下:
 面向对象Java基础
 简单了解UML类图
1、业务背景
商场收银软件
2、UML与代码
2.1 简单工厂实现
先复习,并看下怎么使用简单工厂实现
 
// 收费抽象类
public abstract class CashSuper {
	// 收取费用的抽象方法,参数为单价和数量
	public abstract double acceptCash(double price, int num);
}
// 正常收费
public class CashNormal extends CashSuper {
	// 原价返回
	public double acceptCash(double price, int num){
		return price * num;
	}
}
// 打折收费
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;
	}
}
// 返利收费
public class CashReturn extends CashSuper {
	private double moneyCondition = 0d; // 返利条件
	private double moneyReturn = 0d; // 返利值
	// 初始化时必须输入返利条件和返利值
	// 如:“满300返100”,就是moneyCondition=300,moneyReturn=100
	public CashReturn(double moneyRebate){
		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;
	}
}
扩展:
Math.ceil()的理解 为向上取整(比其本身大的数取值) 取整的类型为double类型的整数 其小数部分变为0。
Math.floor()的理解 为向下取整(比其本身小的数取值) 取整的类型也是为double类型的整数其小数部分同样变为0.
// 收费工厂(收费对象生成工厂)
public class CashFactory {
	
	public static CashSuper createCashAccept(int cashType){
		CashSuper cs = null;
		switch (cashType) {
			case 1:
				oper = new CashNormal();
				break;
			case 2:
				oper = new CashRebate(0.8d);
				break;
			case 3:
				oper = new CashRebate(0.7d);
				break;
			case 4:
				oper = new CashReturn(300d,100d);
				break;
		}
		return cs;
	}
}
客户端程序主要部分:
	double price = 0d; 		// 商品单价
	int num = 0; 			// 商品购买数量
	double totalPrice = 0d; // 当前商品合计费用
	double total = 0d; 		// 总计所有商品费用
	
	...
	
	// 简单工厂模式根据discount的数字选择合适的收费类 生成实例
	CashSuper csuper = CashFactory.createCashAccept(discount);
	// 通过多态,可以根据不同的收费策略计算的到收费结果
	totalPrice = csuper.acceptCash(price,num);
	total = total + totalPrice;
- 问题:
 加入新的促销手段,是可以使用简单工厂模式来解决,但它只解决对象创建的问题,而且由于工厂本身包扩所有的收费方式,如果是经常性的变动,每次维护或扩展都需要改动工厂,导致代码得重新编译部署,这该怎么办?
2.2 策略模式
策略模式:它定义了算法家族,分别封装,让他们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的用户。
对于如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法对象,这没有错,但算法本身只是一种策略,最重要的是这些算法是随时都可能相互替换的,这就是变化点,而封装变化点是我们面向对象的重要思维。

Strategy类,定义所有支持的算法的公共接口:
public abstract class Strategy {
	public abstract void algorithmInterface();
}
ConcreteStrategy类,封装了具体的算法或行为,继承Strategy:
// A B C都一样
public class ConcreteStrategyA extends Strategy{
	public void algorithmInterface(){
		System.out.println("算法A实现");
	}
}
✨Context类,用一个ConcreteStrategy对象来配置,维护一个对Strategy对象的引用。【策略模式的核心】
public class Context {
	Strategy  strategy;
	// 初始化时,传入具体的策略对象【这里就是传入ConcreteStrategy对象,列入ConcreteStrategyA】
	public Context(Strategy strategy){
		this.strategy = strategy;
	} 
	// 上下文接口
	public void contextInterface(){
		// 根据具体的策略对象,调用其算法的方法
		strategy.algorithmInterface();
	}
}
对UML在代码中聚合的个人理解:
 
 客户端代码
	Context context;
	
	// 由于实例化不同的策略,所以最终在调用context.contextInterface()时,
	// 获得的结果就不尽相同
	context = new Context(new ConcreteStrategyA()); // 子类 - 具体算法(策略)
	context.contextInterface();
2.3 简单工厂模式 和 策略模式 的区别✨✨
- 简单工厂模式:用Factory工厂类创建对象(策略,具体算法)
- 策略模式:用Context管理对象(策略,具体算法),不负责创建对象
 策略模式最主要的作用:相比Factory,Context不会因为同一个接口contextInterface()添加新的策略(算法,例如ConcreteStrategyD())而需要改动Context原本的代码。
【这里就根据上述例子理解,管理是Context获取抽象类(Strategy,对应客户端代码的new Context(new ConcreteStrategyA())),然后调用方法context.contextInterface()操作对象的方法】
2.4 策略模式实现✨
将商场收银软件使用策略模式实现,先看UML的变化。
 
 由上图,我们可以知道,抽象类部分没有改变,这里就不做代码展示,遗忘的可以看2.1节的代码,这里就展示 context类 和 客户端代码 具体怎么写。
Context类
public class CashContext {
	// 声明Strategy类对象 【UML中的抽象类 收费】
	private CashSuper cs;
	
	// 通过构造方法,传入具体的收费策略【Strategy】
	public Context(CashSuper csuper){
		// 传入的策略csuper交给CashContext用声明的cs具体管理
		this.cs = csuper;
	} 
	
	// 如果抽象方法有多个,这种管理方法也可以写多个【注意:每个子类都必须实现】
	// 管理cs对象统一的抽象方法【cs是图指的 被管理对象】
	public double getResult(double price,int num){
		// 根据传入不同的收费策略,获得具体实现的返回结果
		return this.cs.acceptCash(price,num);
	}
}
客户端主要代码
	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:
		...
	}
	// 通过Context的getResult方法的调用,可以得到收取费用的结果
	// 让具体算法与客户进行隔离【即使是工厂模式也一样】
	totalPrices = cc.getResult(price,num);
	total = total + totalPrices;
- 问题:
 这个样子写,就需要在客户端去判断用哪一个算法。
 【客户端代码相当于界面逻辑部分,应该与业务逻辑分开,这里客户端代码中创建各种策略,算是业务逻辑和界面逻辑耦合在一起(个人理解)】
2.5 策略与简单工厂结合✨
为了让业务逻辑和界面逻辑解耦,这里让策略模式与简单工厂模式结合,将判断的代码转移。
public class CashContext {
	// 声明Strategy类对象 【UML中的抽象类 收费】
	private CashSuper cs;
	
	// 通过构造方法,传入具体的收费策略【Strategy】
	// public Context(CashSuper csuper){ 【原本的写法】
	public Context(int cashType){
		// 根据类型,new一个策略交给CashContext用声明的cs具体管理
		switch(cashType){
			case 1:
				this.cs = new CashContext(new CashNormal());
				break;
			case 2:
				this.cs = new CashContext(new CashRebate(0.8d));
				break;
			case 3:
			...
	} 
	
	// 如果抽象方法有多个,这种管理方法也可以写多个【注意:每个子类都必须实现】
	// 管理cs对象统一的抽象方法【cs是图指的 被管理对象】
	public double getResult(double price,int num){
		// 根据传入不同的收费策略,获得具体实现的返回结果
		return this.cs.acceptCash(price,num);
	}
}
改动的个人理解:
 
 客户端代码:
	CashContext cc = new CashContext(discount);
	// 通过Context的getResult方法的调用,可以得到收取费用的结果
	// 让具体算法与客户进行隔离【即使是工厂模式也一样】
	totalPrices = cc.getResult(price,num);
	total = total + totalPrices;
根据上述内容可以发现,简单工厂模式并非只有建一个工厂类的做法
- 疑惑1:
 难道这个策略与工厂结合后,还是工厂模式?【小白的我就先不纠结了】
下面对比一下 简单工厂模式 与 策略模式与简单工厂结合
	// 简单工厂模式
	CashSuper csuper = CashFactory.createCashAccept(dscount);
	totalPrices = csuper.acceptCash(price,num);
	// 策略模式与简单工厂结合
	CashContext cc = new CashContext(discount);
	totalPrice = cc.getResult(price,num)

- 疑惑2:
 如果将客户端的判断代码转移到context里面,那context不需要改动的优势不就没有了吗
 【但在另一方面(如上图),又进行了另一方面的解耦】



















