策略模式(Strategy Pattern)
什么时候用策略模式:一个系统需要动态地在几种算法中方选择一种时。
1.银行收银软件(由单价、数量,向客户收费)
package computer;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Cash cash = new Cash();
boolean flag = true;
while (flag) {
cash.buttonOK();
if (cash.totalPrice > 10) {
flag = false;
}
}
System.out.println("=======================================");
System.out.print("购物清单:" + "\n"+cash.list);
System.out.println("总价:" + cash.totalPrice);
}
}
class Cash {
public String list = "";
public double totalPrice = 0.00;
public void buttonOK() {
Scanner cin = new Scanner(System.in);
System.out.print("请输入单价:");
String price = cin.next();
System.out.print("请输入数量:");
String num = cin.next();
double xiaoji = Double.parseDouble(price) * Integer.parseInt(num);
// 清单
list += "单价:" + price + ",数量:" + num + ",小计" + xiaoji + "\n";
// 将每个小计计入总计
totalPrice += xiaoji;
}
}
问题:商场搞活动折扣不断变化,则程序也不断变化。
改进:输入折扣。
2.改进:(输入折扣)
package computer;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Cash cash = new Cash();
boolean flag = true;
while (flag) {
cash.buttonOK();
if (cash.totalPrice > 10) {
flag = false;
}
}
System.out.println("=======================================");
System.out.print("购物清单:" + "\n" + cash.list);
System.out.println("总价:" + cash.totalPrice);
}
}
class Cash {
public String list = "";
public double totalPrice = 0.00;
public void buttonOK() {
Scanner cin = new Scanner(System.in);
System.out.print("请输入单价:");
String price = cin.next();
System.out.print("请输入数量:");
String num = cin.next();
System.out.print("请输入所享折扣:");
String zhekou = cin.next();
double xiaoji = Double.parseDouble(price) * Integer.parseInt(num) * Double.parseDouble(zhekou);
// 清单
list += "单价:" + price + ",数量:" + num + ",折扣" + zhekou + ",小计" + xiaoji + "\n";
// 将每个小计计入总计
totalPrice += xiaoji;
}
}
缺点:在添加满300减100的促销算法等程序会变得复杂。
3.改进:(用简单工厂模式)
一个父类 多个子类分别为打折子类、返利子类…再加工厂类
package computer;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner cin=new Scanner(System.in);
boolean flag=true;
String list="";
double totalPrice=0.00;
while(flag) {
System.out.print("请输入单价:");
String price=cin.next();
System.out.print("请输入数量:");
String num=cin.next();
System.out.print("请输入折扣类型(1无折扣 2打折 3 满减):");
String type=cin.next();
double discount=0;
double basePrice=0;
double returnPrice=0;
if(type.equals("2")) {
System.out.print("请输入折扣:");
discount=Double.parseDouble(cin.next());
}
if(type.equals("3")) {
System.out.print("请输入返现基础金额:");
basePrice=Double.parseDouble(cin.next());
System.out.print("请输入返现金额:");
returnPrice=Double.parseDouble(cin.next());
}
//单价*金额
double xiaoji=Double.parseDouble(price)*Double.parseDouble(num);
CashSuper cs=CashAcceptFactory.createCashAccept(type, discount, basePrice, returnPrice);
//处理后的价格
xiaoji=cs.acceptCash(xiaoji);
list+="单价"+price+",数量"+num+",折扣"+discount+",小计"+xiaoji+"\n";
totalPrice+=xiaoji;
if(totalPrice>10) {
flag=false;
}
}
System.out.println("=====================================================");
System.out.print("清单:\n"+list);
System.out.println("总价:"+totalPrice);
}
}
//抽象现金收取类
abstract class CashSuper{
//抽象方法(参数为原价,返回当前价)
public abstract double acceptCash(double money);
}
//正常收费类
class CashNormal extends CashSuper{
public double acceptCash(double money) {
return money;
}
}
//打折收费类
class CashRebate extends CashSuper{
private double discount=0.00;
public CashRebate(double discount){
this.discount=discount;
}
//继承的方法
public double acceptCash(double money) {
return money*discount;
}
public double getDiscount() {
return discount;
}
public void setDiscount(double discount) {
this.discount = discount;
}
}
//返利收费类
class CashReturn extends CashSuper{
//基础金额
private double baseCash;
//返现金额
private double returnCash;
//构造设置基础金额和返现金额
public CashReturn(double baseCash, double returnCash) {
this.baseCash = baseCash;
this.returnCash = returnCash;
}
//继承的方法
public double acceptCash(double money) {
double result=money;
if(money>baseCash) {
result=money-Math.floor(money/baseCash)*returnCash;
}
return result;
}
public double getBaseCash() {
return baseCash;
}
public void setBaseCash(double baseCash) {
this.baseCash = baseCash;
}
public double getReturnCash() {
return returnCash;
}
public void setReturnCash(double returnCash) {
this.returnCash = returnCash;
}
}
//现金收取工厂
class CashAcceptFactory{
//根据参数生成相应的对象(类型,折扣,基础金额,返现金额)
public static CashSuper createCashAccept(String type,double discount,double basePrice,double returnPrice) {
CashSuper cs=null;
if(type.equals("1"))
cs=new CashNormal();
else if(type.equals("2"))
cs=new CashRebate(discount);
else if(type.equals("3"))
cs=new CashReturn(basePrice,returnPrice);
return cs;
}
}
缺点:每次增加减少收费方式的时候都要对工厂类进行改动,不符合开放封闭原则。
4.改进:(策略模式下的收费系统)
将根据type判断是那个收费类的职责交给客户端,即工厂类中不能出现具体的收费方法类,用它们的父类代替,并为其传入具体的收费方式对象。
package computer;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner cin=new Scanner(System.in);
CashSuper cs=null;
boolean flag=true;
String list="";
double totalPrice=0.00;
double price=0,num=0,discount=0;
while(flag) {
System.out.print("请输入单价:");
price=Double.parseDouble(cin.next());
System.out.print("请输入数量:");
num=Double.parseDouble(cin.next());
System.out.print("请输入折扣类型(1无折扣 2打折 3 满减):");
String type=cin.next();
if(type.equals("1")) {
cs=new CashNormal();
}
else if(type.equals("2")) {
System.out.print("请输入折扣:");
discount=Double.parseDouble(cin.next());
cs=new CashRebate(discount);
}
else if(type.equals("3")) {
System.out.print("请输入返现基础金额:");
String basePrice=cin.next();
System.out.print("请输入返现金额:");
String returnPrice=cin.next();
cs=new CashReturn(Double.parseDouble(basePrice),Double.parseDouble(returnPrice));
}
double xiaoji=price*num;
CashAcceptFactory caf=new CashAcceptFactory(cs);
xiaoji=caf.getResult(xiaoji);
list+="单价"+price+",数量"+num+",折扣"+discount+",小计"+xiaoji+"\n";
totalPrice+=xiaoji;
if(totalPrice>10)
flag=false;
}
System.out.println("=====================================================");
System.out.print("清单:\n"+list);
System.out.println("总价:"+totalPrice);
}
}
//抽象现金收取类
abstract class CashSuper{
//抽象方法(参数为原价,返回当前价)
public abstract double acceptCash(double money);
}
//正常收费类
class CashNormal extends CashSuper{
public double acceptCash(double money) {
return money;
}
}
//打折收费类
class CashRebate extends CashSuper{
private double discount=0.00;
public CashRebate(double discount){
this.discount=discount;
}
//继承的方法
public double acceptCash(double money) {
return money*discount;
}
public double getDiscount() {
return discount;
}
public void setDiscount(double discount) {
this.discount = discount;
}
}
//返利收费类
class CashReturn extends CashSuper{
//基础金额
private double baseCash;
//返现金额
private double returnCash;
//构造设置基础金额和返现金额
public CashReturn(double baseCash, double returnCash) {
this.baseCash = baseCash;
this.returnCash = returnCash;
}
//继承的方法
public double acceptCash(double money) {
double result=money;
if(money>baseCash) {
result=money-Math.floor(money/baseCash)*returnCash;
}
return result;
}
public double getBaseCash() {
return baseCash;
}
public void setBaseCash(double baseCash) {
this.baseCash = baseCash;
}
public double getReturnCash() {
return returnCash;
}
public void setReturnCash(double returnCash) {
this.returnCash = returnCash;
}
}
//现金收取工厂
class CashAcceptFactory{
//现金收费父类对象
private CashSuper cs=null;
//构造方法传入具体的收费类对象
public CashAcceptFactory(CashSuper cs) {
this.cs=cs;
}
//通过cs调用收费计算方法,得到收费结果
public double getResult(double money) {
return cs.acceptCash(money);
}
}
此时若要增加收费方法,只需要添加新的具体收费类、修改客户端即可。
5.策略模式:
(与简单工厂类似)
package computer;
public class Test {
public static void main(String[] args) {
Context context;
context=new Context(new ConcreteStrategyA());
context.contextInterface();
context=new Context(new ConcreteStrategyB());
context.contextInterface();
context=new Context(new ConcreteStrategyC());
context.contextInterface();
}
}
//所有算法的公共接口
abstract class Strategy{
//抽象方法
public abstract void algorithmInterface();
}
//具体算法A
class ConcreteStrategyA extends Strategy{
//继承的方法
public void algorithmInterface() {
System.out.println("算法A实现。");
}
}
//具体算法B
class ConcreteStrategyB extends Strategy{
//继承的方法
public void algorithmInterface() {
System.out.println("算法B实现。");
}
}
//具体算法C
class ConcreteStrategyC extends Strategy{
//继承的方法
public void algorithmInterface() {
System.out.println("算法C实现。");
}
}
//工厂
class Context{
//抽象类对象
Strategy strategy;
//构造方法初始化抽象类对象
public Context(Strategy strategy) {
this.strategy = strategy;
}
//调用具体策略对象的方法的方法
public void contextInterface() {
strategy.algorithmInterface();
}
}
简单工厂模式与策略模式的对比:
简单工厂模式中用户在使用的是工厂类返回的类
策略模式中用户使用的是工厂类
优点:拓展性好、符合开放封闭原则。
缺点:分支判断放回客户端,改变需求算法时,还是要修改客户端。所有策略类都对外暴露。
6.策略模式与简单工厂结合:
解决:其他都不变,改变工厂类,将分支判断放到工厂类中。
package computer;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner cin=new Scanner(System.in);
CashSuper cs=null;
boolean flag=true;
String list="";
double totalPrice=0.00;
double price=0,num=0,discount=0;
while(flag) {
System.out.print("请输入单价:");
price=Double.parseDouble(cin.next());
System.out.print("请输入数量:");
num=Double.parseDouble(cin.next());
double xiaoji=price*num;
CashContext cc=new CashContext();
xiaoji=cc.getResult(xiaoji);
list+="单价"+price+",数量"+num+",折扣"+discount+",小计"+xiaoji+"\n";
totalPrice+=xiaoji;
if(totalPrice>10)
flag=false;
}
System.out.println("=====================================================");
System.out.print("清单:\n"+list);
System.out.println("总价:"+totalPrice);
}
}
//抽象现金收取类
abstract class CashSuper{
//抽象方法(参数为原价,返回当前价)
public abstract double acceptCash(double money);
}
//正常收费类
class CashNormal extends CashSuper{
public double acceptCash(double money) {
return money;
}
}
//打折收费类
class CashRebate extends CashSuper{
private double discount=0.00;
public CashRebate(double discount){
this.discount=discount;
}
//继承的方法
public double acceptCash(double money) {
return money*discount;
}
public double getDiscount() {
return discount;
}
public void setDiscount(double discount) {
this.discount = discount;
}
}
//返利收费类
class CashReturn extends CashSuper{
//基础金额
private double baseCash;
//返现金额
private double returnCash;
//构造设置基础金额和返现金额
public CashReturn(double baseCash, double returnCash) {
this.baseCash = baseCash;
this.returnCash = returnCash;
}
//继承的方法
public double acceptCash(double money) {
double result=money;
if(money>baseCash) {
result=money-Math.floor(money/baseCash)*returnCash;
}
return result;
}
public double getBaseCash() {
return baseCash;
}
public void setBaseCash(double baseCash) {
this.baseCash = baseCash;
}
public double getReturnCash() {
return returnCash;
}
public void setReturnCash(double returnCash) {
this.returnCash = returnCash;
}
}
//现金收取工厂
class CashContext{
//现金收费父类对象
private CashSuper cs=null;
//构造方法分支判断
public CashContext() {
Scanner cin=new Scanner(System.in);
System.out.print("请输入折扣类型(1无折扣 2打折 3 满减):");
String type=cin.next();
double discount=0;
double basePrice=0;
double returnPrice=0;
if(type.equals("1")) {
cs=new CashNormal();
}
else if(type.equals("2")) {
System.out.print("请输入折扣:");
discount=Double.parseDouble(cin.next());
cs=new CashRebate(discount);
}
else if(type.equals("3")) {
System.out.print("请输入返现基础金额:");
basePrice=Double.parseDouble(cin.next());
System.out.print("请输入返现金额:");
returnPrice=Double.parseDouble(cin.next());
cs=new CashReturn(basePrice,returnPrice);
}
}
//通过cs调用收费计算方法,得到收费结果
public double getResult(double money) {
return cs.acceptCash(money);
}
}
当增加新地收费方法时,只需要添加新的具体收费类、修改工厂类构造函数即可。
简单工厂类地客户端需要知道两个类:收费父类和工厂类;
与简单工厂模式结合的策略模式的客户端只需要认识一个类:工厂类。不需要认识父类,降低模块之间的耦合度。