注:示例来自《大话设计模式》
以商场收银软件为例 营业员根据客户所购买商品的单价和数量 进行收费 下面是代码实现
package Test02;
import java.util.Scanner;
public class Program {
public static void main(String[] args) {
//声明一个double变量total来计算总计
double total = 0.0d;
String answer;
do {
System.out.println("请输入商品单价:");
String txtPrice = new Scanner(System.in).nextLine();
System.out.println("请输入商品数量:");
String txtNum = new Scanner(System.in).nextLine();
//声明一个double变量totalPrices来计算每个商品的单价(txtPrice)*数量(txtNum)后的合计
double totalPrices = Double.parseDouble(txtPrice) * Double.parseDouble(txtNum);
//将每个商品合计计入总计
total = total + totalPrices;
System.out.println("单价:"+txtPrice+" 数量:"+txtNum+" 合计:"+totalPrices);
System.out.println("是否继续输入商品? y继续 任意键退出");
answer = new Scanner(System.in).nextLine();
} while("y".equals(answer));
System.out.println(total);
}
}
现在如果商场要对商品搞活动 进行商品打八折或七折等 该如何实现呢 可以加一个打折的选项 代码如下
package Test02;
import java.util.Scanner;
public class Program {
public static void main(String[] args) {
//声明一个double变量total来计算总计
double total = 0.0d;
String answer;
do {
System.out.println("请输入商品单价:");
String txtPrice = new Scanner(System.in).nextLine();
System.out.println("请输入商品数量:");
String txtNum = new Scanner(System.in).nextLine();
System.out.println("请输入打折条件:0代表正常收费 1代表打八折 2代表打七折 3代表打五折");
String select = new Scanner(System.in).nextLine();
double totalPrices = 0d;
switch (select) {
case "0":
totalPrices = Double.parseDouble(txtPrice) * Double.parseDouble(txtNum);
break;
case "1":
totalPrices = Double.parseDouble(txtPrice) * Double.parseDouble(txtNum) * 0.8;
break;
case "2":
totalPrices = Double.parseDouble(txtPrice) * Double.parseDouble(txtNum) * 0.7;
break;
case "3":
totalPrices = Double.parseDouble(txtPrice) * Double.parseDouble(txtNum) * 0.5;
break;
}
total = total + totalPrices;
System.out.println("单价:"+txtPrice+" 数量:"+txtNum+" 合计:"+totalPrices);
System.out.println("是否继续输入商品? y继续 任意键退出");
answer = new Scanner(System.in).nextLine();
} while("y".equals(answer));
System.out.println(total);
}
}
上面的重复代码太多 如果需要增加 满300返100的促销算法 又该如何实现呢 下面用简单工厂模式实现
现金收费抽象类
package Test02;
//现金收取父类
abstract class CashSuper {
//抽象方法:收取现金,参数为原价,返回为当前价
public abstract double acceptCash(double money);
}
正常收费子类
package Test02;
//正常收费,继承CashSuper
public class CashNormal extends CashSuper {
@Override
public double acceptCash(double money) {
return money;
}
}
打折收费子类
package Test02;
//打折收费,继承CashSuper
public class CashRebate extends CashSuper {
private double moneyRebate = 1d;
//初始化时,必需要输入折扣率,如八折,就是0.8
public CashRebate(String moneyRebate)
{
this.moneyRebate = Double.parseDouble(moneyRebate);
}
@Override
public double acceptCash(double money) {
return money * moneyRebate;
}
}
返利收费子类
package Test02;
//返利收费,继承CashSuper
public class CashReturn extends CashSuper {
private double moneyCondition = 0.0d;
private double moneyReturn = 0.0d;
//初始化时必须要输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneyReturn为100
public CashReturn(String moneyCondition, String moneyReturn)
{
this.moneyCondition = Double.parseDouble(moneyCondition);
this.moneyReturn = Double.parseDouble(moneyReturn);
}
@Override
public double acceptCash(double money) {
double result = money;
//若大于返利条件,则需要减去返利值
if (money >= moneyCondition)
result = money - Math.floor(money / moneyCondition) * moneyReturn;
return result;
}
}
现金收费工厂类
package Test02;
//现金收取工厂
public class CashFactory {
//根据条件返回相应的对象
public static CashSuper createCashAccept(String type)
{
CashSuper cs = null;
switch (type)
{
case "0":
cs = new CashNormal();
break;
case "1":
CashReturn cr1 = new CashReturn("300", "100");
cs = cr1;
break;
case "2":
CashRebate cr2 = new CashRebate("0.8");
cs = cr2;
break;
}
return cs;
}
}
客户端代码
package Test02;
import java.util.Scanner;
public class Program {
public static void main(String[] args) {
//声明一个double变量total来计算总计
double total = 0.0d;
String answer;
do {
System.out.println("请输入商品单价:");
String txtPrice = new Scanner(System.in).nextLine();
System.out.println("请输入商品数量:");
String txtNum = new Scanner(System.in).nextLine();
System.out.println("请输入打折条件:0代表正常收费 1代表满300减100 2代表打八折");
String select = new Scanner(System.in).nextLine();
//利用简单工厂模式根据输入的选项,生成相应的对象
CashSuper csuper = CashFactory.createCashAccept(select);
double totalPrices = 0d;
//通过多态,可以得到收取费用的结果
totalPrices = csuper.acceptCash(Double.parseDouble(txtPrice) * Double.parseDouble(txtNum));
total = total + totalPrices;
System.out.println("单价:"+txtPrice+" 数量:"+txtNum+" 合计:"+totalPrices);
System.out.println("是否继续输入商品? y继续 任意键退出");
answer = new Scanner(System.in).nextLine();
} while("y".equals(answer));
System.out.println(total);
}
}
工厂模式主要是解决对象的创建问题 商场可能经常性地更改打折额度和返利额度 每次维护都要改动这个工厂 以致代码需要重新编译部署 所以用它不是最好的办法 下面用策略模式看看如何实现
CashSuper CashNormal CashRebate CashReturn这几个类不用改 添加一个CashContext类
package Test02;
//收费策略Context
public class CashContext {
//声明一个现金收费父类对象
private CashSuper cs;
//设置策略行为,参数为具体的现金收费子类(正常,打折或返利)
public CashContext(CashSuper csuper)
{
this.cs = csuper;
}
//得到现金促销计算结果(利用了多态机制,不同的策略行为导致不同的结果)
public double GetResult(double money)
{
return cs.acceptCash(money);
}
}
客户端代码改为
package Test02;
import java.util.Scanner;
public class Program {
public static void main(String[] args) {
//声明一个double变量total来计算总计
double total = 0.0d;
String answer;
do {
System.out.println("请输入商品单价:");
String txtPrice = new Scanner(System.in).nextLine();
System.out.println("请输入商品数量:");
String txtNum = new Scanner(System.in).nextLine();
System.out.println("请输入打折条件:0代表正常收费 1代表满300减100 2代表打八折");
String select = new Scanner(System.in).nextLine();
CashContext cc = null;
switch (select)
{
case "0":
cc = new CashContext(new CashNormal());
break;
case "1":
cc = new CashContext(new CashReturn("300", "100"));
break;
case "2":
cc = new CashContext(new CashRebate("0.8"));
break;
}
double totalPrices = 0d;
totalPrices = cc.GetResult(Double.parseDouble(txtPrice) * Double.parseDouble(txtNum));
total = total + totalPrices;
System.out.println("单价:"+txtPrice+" 数量:"+txtNum+" 合计:"+totalPrices);
System.out.println("是否继续输入商品? y继续 任意键退出");
answer = new Scanner(System.in).nextLine();
} while("y".equals(answer));
System.out.println(total);
}
}
用了策略模式后 我们发现回到了老路 判断代码回到了客户端 该怎么把这个判断从客户端转移走呢 简单工厂+策略模式
改造后的CashContext
package Test02;
//收费策略Context
public class CashContext {
//声明一个现金收费父类对象
private CashSuper cs;
//根据条件返回相应的对象
public CashContext(String type)
{
switch (type)
{
case "0":
CashNormal cs0 = new CashNormal();
cs = cs0;
break;
case "1":
CashReturn cr1 = new CashReturn("300", "100");
cs = cr1;
break;
case "2":
CashRebate cr2 = new CashRebate("0.8");
cs = cr2;
break;
}
}
//得到现金促销计算结果(利用了多态机制,不同的策略行为导致不同的结果)
public double GetResult(double money)
{
return cs.acceptCash(money);
}
}
客户端代码
package Test02;
import java.util.Scanner;
public class Program {
public static void main(String[] args) {
//声明一个double变量total来计算总计
double total = 0.0d;
String answer;
do {
System.out.println("请输入商品单价:");
String txtPrice = new Scanner(System.in).nextLine();
System.out.println("请输入商品数量:");
String txtNum = new Scanner(System.in).nextLine();
System.out.println("请输入打折条件:0代表正常收费 1代表满300减100 2代表打八折");
String select = new Scanner(System.in).nextLine();
//利用简单工厂模式根据输入的选项,生成相应的对象
CashContext csuper = new CashContext(select);
double totalPrices = 0d;
totalPrices = csuper.GetResult(Double.parseDouble(txtPrice) * Double.parseDouble(txtNum));
total = total + totalPrices;
System.out.println("单价:"+txtPrice+" 数量:"+txtNum+" 合计:"+totalPrices);
System.out.println("是否继续输入商品? y继续 任意键退出");
answer = new Scanner(System.in).nextLine();
} while("y".equals(answer));
System.out.println(total);
}
}
简单工厂模式需要让客户端认识两个类 CashSuper CushFactory 而策略模式与简单工厂结合的用法 客户端只需认识一个类CashContext就可以了 耦合更加降低
策略模式:它定义了算法家族 分别封装起来 让它们之间可以互相替换 此模式让算法的变化 不会影响到使用算法的客户
个人理解策略模式是对多态的诠释 根据对象的不同 选择不同的算法进行实现
缺点:
1、客户端必须知道所有的策略类 并自行决定使用哪一个策略类 这就意味着客户端必须理解这些算法的区别 以便适时选择恰当的算法类 换言之 策略模式只适用于客户端知道算法或行为的情况
2、由于策略模式把每个具体的策略实现都单独封装成为类 如果备选的策略很多的话 那么这些类的数目就非常多了