---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
引入:商城收银软件,给出商品的价格,数量,要求算出商品的总价
(如果我们接到这道题,我们一般会这样写)
Strategy类,Product类,CalculateTotal类
Strategy类
public class Strategy {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Product product = new Product("桃子", 10, 0.8, 3);
Product product1 = new Product("苹果", 10, 0.8, 3);
CalculateTotal cal = new CalculateTotal();
cal.addProduct(product);
cal.addProduct(product1);
double total = cal.caculate();
System.out.println(total);
}
}
Product 类
public class Product {
private String name;
private int number;
private double discount;
private double unitPrice;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public double getDiscount() {
return discount;
}
public void setDiscount(double discount) {
this.discount = discount;
}
public Product(String name, int number, double discount, double unitPrice) {
super();
this.name = name;
this.number = number;
this.discount = discount;
this.unitPrice = unitPrice;
}
public double getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(double unitPrice) {
this.unitPrice = unitPrice;
}
public Product() {
}
}
CalculateTotal 类
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class CalculateTotal {
public Map<String, Product> map = new HashMap<String, Product>();
public void addProduct(Product p) {
map.put(p.getName(), p);
}
public double caculate() {
Set<String> keyset = map.keySet();
Iterator<String> it = keyset.iterator();
double total = 0;
while (it.hasNext()) {
String key = it.next();
Product product = map.get(key);
double unitPrice = product.getUnitPrice();
double discount = product.getDiscount();
int number = product.getNumber();
total = total + unitPrice * number * discount;
}
return total;
}
}
这种写法貌似很好,也符合了面向对象的设计原则,但是仔细想想,他并不灵活,假如对于同一件商品,列如桃子,现在国庆节,五折出售,国庆节过后,不打折,但是满100返还20,元旦到了,商家决定在打八折的基础上满50返还5元,对于这种情况,“优惠措施是多变的”,我们这种设计并不合适。
修改:策略模式
CashRetrun(满多少返还)类,CashRebate类(打折),CashNormal类(无折扣),CashSuper类(这三个类的父接口),Product(商品类),Client(客户端,维护对CashSuper类的一个引用)
CashReturn类
package strategy;
public class CashReturn implements CashSuper {
private int reCash;
private int cashCondition;
@Override
public double getResult(Product p) {
// TODO Auto-generated method stub
int result = p.getUnitPrice() * p.getNumber();
return result > cashCondition ? result - cashCondition : result;
}
public CashReturn(int reCash, int cashCondition) {
super();
this.reCash = reCash;
this.cashCondition = cashCondition;
}
}
CashRebate 类
package strategy;
public class CashRebate implements CashSuper {
private int cashRebate;
@Override
public double getResult(Product p) {
// TODO Auto-generated method stub
return p.getNumber() * p.getUnitPrice() * cashRebate;
}
public CashRebate(int cashRebate) {
super();
this.cashRebate = cashRebate;
}
}
CashNormal 类
package strategy;
public class CashNormal implements CashSuper {
@Override
public double getResult(Product p) {
return p.getNumber() * p.getUnitPrice();
}
}
CashSuper 类
package strategy;
public interface CashSuper {
double getResult(Product p);
}
Product 类
package strategy;
public class Product {
private String name;
private int number;
private int unitPrice;
public int getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(int unitPrice) {
this.unitPrice = unitPrice;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public Product(String name, int number) {
super();
this.name = name;
this.number = number;
}
public void setNumber(int number) {
this.number = number;
}
public Product() {
}
}
Client 类
package strategy;
import java.util.Scanner;
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
boolean flag = true;
int totalCash = 0;
CashSuper cashSuper = null;
Scanner scanner = new Scanner(System.in);
while (flag) {
System.out.println("输入商品数量:");
int number = scanner.nextInt();
System.out.println("请输入商品单价");
int unitPrice = scanner.nextInt();
Product product = new Product();
product.setNumber(number);
product.setUnitPrice(unitPrice);
System.out.println("请选择优惠策略1、不打折 2满则返还 3打折");
int strategy = scanner.nextInt();
if (strategy == 1) {
cashSuper = new CashNormal();
} else if (strategy == 2) {
System.out.println("请输入满足条件及返还金额");
int cashCondition = scanner.nextInt();
int cashReturn = scanner.nextInt();
cashSuper = new CashReturn(cashCondition, cashReturn);
} else if (strategy == 3) {
System.out.println("请输入打折率");
int cashRebate = scanner.nextInt();
cashSuper = new CashRebate(cashRebate);
}
totalCash = (int) (totalCash + cashSuper.getResult(product));
System.out.println("是否继续输入y/n");
String condition = scanner.next();
flag = condition.equals("y");
}
System.out.println(totalCash);
}
}
这就是策略模式,很好的解决了这类问题,但是客户端做了大多判断,我们针对这种情况可以进一步进行修改,(策略模式+简单工厂),将判断逻辑从Client层分离
CashFactory类
package strategy;
import java.util.Scanner;
public class CashFactory {
static CashSuper cashSuper = null;
static Scanner scanner = new Scanner(System.in);
public static CashSuper getResult(int strategy) {
if (strategy == 1) {
cashSuper = new CashNormal();
} else if (strategy == 2) {
System.out.println("请输入满足条件及返还金额");
int cashCondition = scanner.nextInt();
int cashReturn = scanner.nextInt();
cashSuper = new CashReturn(cashCondition, cashReturn);
} else if (strategy == 3) {
System.out.println("请输入打折率");
double cashRebate = scanner.nextDouble();
cashSuper = new CashRebate(cashRebate);
}
return cashSuper;
}
}
Client 类
package strategy;
import java.util.Scanner;
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
boolean flag = true;
int totalCash = 0;
CashSuper cashSuper = null;
Scanner scanner = new Scanner(System.in);
while (flag) {
System.out.println("输入商品数量:");
int number = scanner.nextInt();
System.out.println("请输入商品单价");
int unitPrice = scanner.nextInt();
Product product = new Product();
product.setNumber(number);
product.setUnitPrice(unitPrice);
System.out.println("请选择优惠策略1、不打折 2满则返还 3打折");
int strategy = scanner.nextInt();
cashSuper = CashFactory.getResult(strategy);
totalCash = (int) (totalCash + cashSuper.getResult(product));
System.out.println("是否继续输入y/n");
String condition = scanner.next();
flag = condition.equals("y");
}
System.out.println(totalCash);
}
}
总结:策略模式的优点与适用场合
优点:
1、策略模式是一种定义一系列算法的方法,所有的这些算法都完成相同的工作,只是实现不同,利用策略模式可以以相同的方式调用所有的算法,减少各种算法类与使用算法类之间的耦合性.
2、有利于简化各个算法的单元测试,每个算法的变动对其他算法没有影响
3、将行为封装在一个个单独的算法类中,可以避免在一个单独的类中使用多个条件判断
适用场合
策略模式是用来封装算法的。实践中可以用它来封装几乎任何类型的规则,只要在分析过程中,需要用到不同时间应用不同规则,就可以使用策略模式封装变化的可能性
策略模式的组成:
1、抽象策略角色:策略类,通常由一个接口或者抽象类实现
2、具体策略角色:包括了相关的算法和行为,可能不止一个具体策略角色
3、环境角色:持有一个策略类的引用,最终给客户端调用的
接下来老师用配置+反射完成,不过我觉得太过繁琐,而且针对这种优惠措施多变的情况,用配置+反射也并没有消除逻辑判断,所以后面的没写,现在附上核心代码。
在用反射时如何反射带参数的构造函数
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="+">demo4.AddFactory</entry>
<entry key="-">demo4.SubtractionFactory</entry>
</properties>
注意配置文件的格式,本来不打算写了,但仔细一想,虽然理解了,但实际操作可能又会发生好多问题,就又敲了一遍,果然如此。
CashFactory类
package strategy1;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.InvalidPropertiesFormatException;
import java.util.Properties;
import java.util.Scanner;
public class CashFactory {
static CashSuper cashSuper = null;
static Scanner scanner = new Scanner(System.in);
public static CashSuper getResult(int strategy) throws Exception {
Object[] args;
if (strategy == 1) {
cashSuper = new CashNormal();
} else if (strategy == 2) {
System.out.println("请输入满足条件及返还金额");
int cashCondition = scanner.nextInt();
int cashReturn = scanner.nextInt();
args = new Object[] { cashCondition, cashReturn };
cashSuper = (CashSuper) reflect(2, args);
} else if (strategy == 3) {
System.out.println("请输入打折率");
double cashRebate = scanner.nextDouble();
args = new Object[] { cashRebate };
cashSuper = (CashSuper) reflect(3, args);
}
return cashSuper;
}
public static Object reflect(int type, Object[] args) throws Exception {
Properties prop = new Properties();
prop.loadFromXML(new FileInputStream(new File("config.xml")));
String className = prop.getProperty(type + "");
System.out.println(args.length);
Class c = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0; i < args.length; i++) {
argsClass[i] = args[i].getClass();
}
Constructor cons = c.getConstructor(argsClass);
return cons.newInstance(args);
}
}
Client 类
package strategy1;
import java.util.Scanner;
public class Client {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
boolean flag = true;
int totalCash = 0;
CashSuper cashSuper = null;
Scanner scanner = new Scanner(System.in);
while (flag) {
System.out.println("输入商品数量:");
int number = scanner.nextInt();
System.out.println("请输入商品单价");
int unitPrice = scanner.nextInt();
Product product = new Product();
product.setNumber(number);
product.setUnitPrice(unitPrice);
System.out.println("请选择优惠策略1、不打折 2满则返还 3打折");
int strategy = scanner.nextInt();
cashSuper = CashFactory.getResult(strategy);
totalCash = (int) (totalCash + cashSuper.getResult(product));
System.out.println("是否继续输入y/n");
String condition = scanner.next();
flag = condition.equals("y");
}
System.out.println(totalCash);
}
}
本来打算这样就好了,可是一运行,竟然报错
可是仔细检查CashReturn有这样的构造函数啊
public CashReturn(int reCash, int cashCondition) {
super();
this.reCash = reCash;
this.cashCondition = cashCondition;
}
后找老师帮忙,得以解决
修改如下:
public CashReturn(Integer reCash, Integer cashCondition) {
super();
this.reCash = reCash;
this.cashCondition = cashCondition;
}
--------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------