注:示例来自《大话设计模式》

以商场收银软件为例 营业员根据客户所购买商品的单价和数量 进行收费 下面是代码实现

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、由于策略模式把每个具体的策略实现都单独封装成为类 如果备选的策略很多的话 那么这些类的数目就非常多了