概述
Strategy --- 策略模式:
模式动机:
在软件开发中,常遇到类似问题,实现某一个功能的途径有很多,如查找,排序等,一种常用的方法是"硬编码"(Hard Coding)在一个类中,如果需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,用过if...else... 等条件判断语句来进行选择。这两种实现方法我们都可以称之为硬编码,如果要新增一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。在这个算法类中封装了大量的查找算法,该类代码将教负责,维护教为困难。
为了解决这些问题,可以定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法,在这里,每一个封装算法的类我们都可以称之为策略(Strategy),为了保证这些策略的一致性,一般会用一个抽象的策略类来做算法的定义,而具体每种算法则对应于一个具体的策略类。
模式定义:
策略模式: 属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有公公接口的独立的类中,从而使得它们可以相互替换。
策略模式:使得算法可以在不影响到客户端的情况下发生变化。
策略模式: 是对算法的包装, 是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说, 就是:"准备一组算法,并将每一个算法封装起来,使得它们可以互换" 。
模式结构:
Context: 环境角色,持有一个Strategy的引用。
Strategy: 抽象策略角色,这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
ConreteStrategy: 具体策略类,保证了相关的算法或行为。
环境角色类:
public class Context {
// 持有一个具体策略的对象
private Strategy strategy;
/**
* 构造函数, 传入一个具体策略对象
*
* @params strategy 具体策略对象
*/
public Context(Strategy strategy) {
this.strategy = strategy;
}
/**
* 策略方法
*/
public void contextInterface() {
strategy.strategyInterface();
}
}
抽象策略类:
public interface Strategy {
/**
* 策略方法
*/
void strategyInterface();
}
具体策略类:
public class ConcreteStrategyA implements Strategy {
@Override
public void strategyInterface() {
// 相关业务
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void strategyInterface() {
// 相关业务
}
}
public class ConcreteStrategyC implements Strategy {
@Override
public void strategyInterface() {
// 相关业务
}
}
实例: 收银软件项目
需求: 收银员根据客户购买产品的单价和数量,想客户打印小票。
简单实现:
public class Cash {
public String list = "";
public Double totalPrice = 0.00;
public void buttonOk() {
Scanner scanner = new Scanner(System.in);
System.out.println("输入单价:");
String price = scanner.nextLine();
scanner = new Scanner(System.in);
System.out.println("输入数量:");
String num = scanner.nextLine();
scanner = new Scanner(System.in);
System.out.println("输入折扣:");
String discount = scanner.nextLine();
Double account = Double.parseDouble(price) * Integer.parseInt(num) * Double.parseDouble(discount) / 10;
list += "单价:" + price + ", 数据:" + num + ", 折扣:" + discount + "n";
totalPrice += account;
}
public static void main(String[] args) {
Cash cash = new Cash();
boolean flag = true;
while (flag) {
cash.buttonOk();
if (cash.totalPrice > 20) {
flag = false;
}
}
System.out.println("=============");
System.out.println("清单:n" + cash.list);
System.out.println("总价:" + cash.totalPrice);
}
}
缺点:
面相对象的角度思考,这个类将前端输入和业务逻辑混合在一块了,不利于维护, 扩展,复用,也不灵活。
如果之后要扩展,增加新的活动,则每次都需要修改原来的代码。
使用简单的工厂模式实现:
首先需要一个工厂类,可以实现,根据不同的类型,返回不同的现金收费方式。
/**
* 现金收费方式类
*/
public abstract class CashFee {
public abstract double acceptCash(double money);
}
// 定义现金收费方式: 正常收费, 折扣, 满减
/**
* 正常收费
*/
public class NormalCashFee extends CashFee {
@Override
public double acceptCash(double money) {
return money;
}
}
/**
* 折扣
*/
public class DiscountCashFee extends CashFee {
private double discount = 0.00;
public DiscountCashFee(double discount) {
this.discount = discount / 10;
}
@Override
public double acceptCash(double money) {
return this.discount * money;
}
public double geDiscount() {
return discount;
}
public void setDiscount(double discount) {
this.discount = discount;
}
}
/**
* 满减
*/
public class ReturnCashFee extends CashFee {
private double baseCash;
private double returnCash;
public ReturnCashFee(double baseCash, double returnCash) {
this.baseCash = baseCash;
this.returnCash = returnCash;
}
public void setBaseCash(double baseCash) {
this.baseCash = baseCash;
}
public double getBaseCash() {
return this,baseCash;
}
public void setReturnCash(double returnCash) {
this.returnCash = returnCash;
}
public double getReturnCash() {
return this.returnCash;
}
@Override
public double acceptCash(double money) {
return money - Math.floor(money / baseCash) * returnCash;
}
}
// 定义工厂类,用于生产各种各样的现金收费方式
public class CashFeeFactory {
public static CashFee createCashFee(int type, double discount, double baseCash, double returnCash) {
CashFee cashFee = null;
switch (type) {
case 1:
cashFee = new NormalCashFee();
break;
case 2:
cashFee = new DiscountCashFee(discount);
break;
case 3:
cashFee = new ReturnCashFee(baseCash, returnCash);
break;
default:
break;
}
return cashFee;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("输入单价:");
String price = scanner.nextLine();
scanner = new Scanner(System.in);
System.out.println("输入数量:");
String num = scanner.nextLine();
scanner = new Scanner(System.in);
System.out.println("输入折扣类型(1 无折扣 2 打折 3 满减):");
String type = scanner.nextLine();
double discount = 0.0d;
double basePrice = 0;
double returnPrice = 0;
if ("2".equals(type)) {
scanner = new Scanner(System.in);
System.out.println("输入折扣:");
discount = Double.parseDouble(scanner.nextLine());
}
if ("3".equals(type)) {
scanner = new Scanner(System.in);
System.out.println("基础金额:");
basePrice = Double.parseDouble(scanner.nextLine());
scanner = new Scanner(System.in);
System.out.println("返还现金:");
returnPrice = Double.parseDouble(scanner.nextLine());
}
Double money = Double.parseDouble(price) * Integer.parseInt(num);
CashFee cashFee = CashFeeFactory.createCashFee(Integer.parseInt(type), discount, basePrice, returnPrice);
System.out.println("总价:" + cashFee.acceptCash(money));
}
}
简单工厂模式的优缺点:
优点:
1. 业务逻辑和前端展示分离开了。业务逻辑的修改,不影响前端代码的展示。
2. 每一个业务逻辑单独一个雷,修改或者添加一个类,不会影响到其他的类。
3. 使用工厂类封装了业务逻辑类,前端不需要知道到底每种业务怎么实现,只需要知道他的父类即可。
缺点:
1. 如果活动很频繁,经常会搞各种各样的活动,那么业务逻辑就会有很多种,每次都要增加一个类。
2. 每增加一个类都要丢改工厂类,修改很频繁。将简单工厂模式和策略模式结合起来:
// 增加 一个上下文
public class CashContext {
private CashFee cashFee;
/**
* 构建不同的收费模式
* @param type [description]
* @param discount [description]
* @param baseCash [description]
* @param returnCash [description]
*/
public CashContext(int type, double discount, double baseCash, double returnCash) {
switch(type) {
case 1: {
this.cashFee = new NormalCashFee();
break:
}
case 2: {
this.cashFee = new DiscountCashFee(discount);
break;
}
case 3: {
this.cashFee = new ReturnCashFee(baseCash, returnCash);
break;
}
default:
break;
}
}
/**
* 获取结果
*/
public double getResult(double money) {
return cashFee.acceptCash(money);
}
/**
* 测试类
* @param args [description]
*/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("输入单价:");
String price = scanner.nextLine();
scanner = new Scanner(System.in);
System.out.println("输入数量:");
String num = scanner.nextLine();
scanner = new Scanner(System.in);
System.out.println("输入折扣类型(1 无折扣 2 打折 3 满减):");
String type = scanner.nextLine();
double discount = 0.0d;
double basePrice = 0;
double returnPrice = 0;
if ("2".equals(type)) {
scanner = new Scanner(System.in);
System.out.println("输入折扣:");
discount = Double.parseDouble(scanner.nextLine());
}
if ("3".equals(type)) {
scanner = new Scanner(System.in);
System.out.println("基础金额:");
basePrice = Double.parseDouble(scanner.nextLine());
scanner = new Scanner(System.in);
System.out.println("返还现金:");
returnPrice = Double.parseDouble(scanner.nextLine());
}
Double money = Double.parseDouble(price) * Integer.parseInt(num);
CashContext cashContext = new CashContext(Integer.parseInt(type), discount, basePrice, returnPrice);
System.out.println("总价:" + cashContext.getResult(money));
}
}
对比简单工厂设计模式 和 策略模式+工厂模式的区别:
CashFee cashFee = CashFeeFactory. createCashFee(Integer.parseInt(type), discount, baseCash, returnCash);
CashContext cashContext = new CashContext(Integer.parseInt(type), discount, baseCash, returnCash);
对于客户端而言, 简单工厂设计模式,客户端要知道两个类, CashFee 和 CashFeeFactory, 而 策略模式 + 简单工厂模式,客户端只需要知道 CashContext 类即可, 降低了耦合性。
总结策略模式:
策略模式: 分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。 原则就是:分离变化部分,封装接口, 基于接口变成各种功能。此模式让行为算法的变化独立于算法的使用者。
策略模式应用分析:
策略模式分析:
策略模式是对算法的封装,他把算法的责任和算法本身分割开,委派给不同地对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。一句话说"转变一组算法,并将每一个算法封装起来,使得它们可以互换"。
重心:
不是如何实现算法,而是如何组织,调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
注意点:
1. 分析项目中变化部分与不变部分
2. 多用组合少用继承;用行为类组合,而不是行为的继承。更有弹性。
最后
以上就是炙热彩虹为你收集整理的设计模式 --- 策略模式的全部内容,希望文章能够帮你解决设计模式 --- 策略模式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复