1、策略模式(Strategy):定义了一组算法,将每个算法都封装其类,并且使他们之间可以互换。
2、UML结构图如下:
其中,Context是上下文,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用;Strategy是策略类,用于定义所有支持算法的公共接口;ConcreteStrategy是具体策略类,封装了具体的算法或行为,继承于Strategy。
3、使用场景
- 多个类只有算法或行为上稍有不同的场景;
- 算法需要*切换的场景;
- 需要屏蔽算法规则的场景。
4、优缺点
优点
- 算法可以*切换;
- 避免使用多重条件判断(如果不用策略模式我们可能会使用多重条件语句,不利于维护);
- 扩展性良好,增加一个策略只需实现接口即可。
缺点
- 策略类数量会增多,每个策略都是一个类,复用的可能性很小;
- 所有的策略类都需要对外暴露。
5、应用实例
- 商场促销方式,打折、满减等;
- 出行方式,自行车、汽车等,每一种出行方式都是一个策略;
- Java AWT中的LayoutManager,即布局管理器。
6、注意事项
- 如果一个系统的策略多于四个,就需要考虑使用混合模式来解决策略类膨胀的问题。
7、代码实例
(1)上下文类
首先声明一个CashSuper对象,通过构造方法,传入具体的收费策略,getResult()方法的功能为根据收费策略的不同获得计算结果。
1 /** 2 * @author it-小林 3 * @desc 上下文类 4 * @date 2021年09月28日 9:57 5 */ 6 public class CashContext { 7 8 private CashSuper cashSuper; 9 10 public CashContext(CashSuper cashSuper) { 11 this.cashSuper = cashSuper; 12 } 13 14 public double getResult(double money){ 15 return cashSuper.acceptCash(money); 16 } 17 }
(2)现金收费抽象类
策略类,为抽象类,抽象出收费的方法供子类实现。
1 /** 2 * @author it-小林 3 * @desc 现金收费抽象类 4 * @date 2021年09月28日 9:46 5 */ 6 public abstract class CashSuper { 7 8 public abstract double acceptCash(double money); 9 10 }
(3)正常收费子类
没有任何活动的情况,正常收费,返回原价。
1 /** 2 * @author it-小林 3 * @desc 正常收费子类 4 * @date 2021年09月28日 9:48 5 */ 6 public class CashNormal extends CashSuper{ 7 8 9 @Override 10 public double acceptCash(double money) { 11 return money; 12 } 13 }
(4)打折收费子类
打折活动,根据折扣返回打折后的价格。
1 /** 2 * @author it-小林 3 * @desc 打折收费子类 4 * @date 2021年09月28日 9:49 5 */ 6 public class CashRebate extends CashSuper { 7 8 private double moneyRebate = 1; //折扣 9 10 public CashRebate(double moneyRebate) { 11 this.moneyRebate = moneyRebate; 12 } 13 14 @Override 15 public double acceptCash(double money) { 16 return money * moneyRebate; 17 } 18 }
(5)返利收费子类
返利活动,输入返利条件和返利值,比如满300返100,moneyCoditation为300,moneyReturn为100。
result = money - Math.floor(money / moneyConditation) * moneyReturn; 的意思为,如果当前金额大于等于返利条件,则使用当前金额减去返利值。
1 /** 2 * @author it-小林 3 * @desc 返利收费子类 4 * @date 2021年09月28日 9:53 5 */ 6 public class CashReturn extends CashSuper { 7 8 private double moneyConditation = 0.0; //返利条件 9 private double moneyReturn = 0.0d; //返利值 10 11 public CashReturn(double moneyConditation, double moneyReturn) { 12 this.moneyConditation = moneyConditation; 13 this.moneyReturn = moneyReturn; 14 } 15 16 @Override 17 public double acceptCash(double money) { 18 double result = money; 19 if(money >= moneyConditation){ 20 result = money - moneyReturn; 21 } 22 return result; 23 } 24 }
(6)测试类
1 /** 2 * @author it-小林 3 * @desc 客户端 4 * @date 2021年09月28日 9:58 5 */ 6 public class Client { 7 public static void main(String[] args) { 8 CashContext context = null; 9 10 Scanner scanner = new Scanner(System.in); 11 System.out.println("请输入打折方式(1/2/3):"); 12 int in = scanner.nextInt(); 13 String type = ""; 14 15 switch (in) { 16 case 1: 17 context = new CashContext(new CashNormal()); 18 type += "正常收费"; 19 break; 20 case 2: 21 context = new CashContext(new CashReturn(300, 200)); 22 type += "满300返100"; 23 break; 24 case 3: 25 context = new CashContext(new CashRebate(0.8)); 26 type += "打8折"; 27 break; 28 default: 29 System.out.println("请输入1/2/3"); 30 break; 31 } 32 33 double totalPrices = 0; 34 System.out.print("请输入单价:"); 35 double price = scanner.nextDouble(); 36 System.out.print("请输入数量:"); 37 double num = scanner.nextDouble(); 38 totalPrices = context.getResult(price * num); 39 System.out.println("单价:" + price + ",数量:" + num + ",类型:" + type + ",合计:" + totalPrices); 40 } 41 }
运行结果