Java设计模式篇(三)--策略模式详解_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > Java设计模式篇(三)--策略模式详解

Java设计模式篇(三)--策略模式详解

 2018/10/24 15:18:39  zhaodengfeng1989  程序员俱乐部  我要评论(0)
  • 摘要:在说策略模式之前,我们先来分析一个需求。一、需求分析及实现过程某商场经常有促销活动,第一次是满200送80,第二次是所有商品打8折,第三次是所有商品打9折,第四次是满300送120等等。如果让你设计你怎么做?我们都在说面向对象编程,但是并不是类越多越好。分类的目的是为了抽象,将所有共性(相同的属性和功能)的对象抽象成一个类。我们先来找共性,打折类的需求我们可以抽象成一个需求,因为他们的共性就是折扣率和原价。满多少送多少类可以抽象成一个需求,因为他们的共性就是总价和扣除价。正常价是原价
  • 标签:详解 Java 模式 设计 设计模式

在说策略模式之前,我们先来分析一个需求。

一、需求分析实现过程

某商场经常有促销活动,第一次是满200送80,第二次是所有商品打8折,第三次是所有商品打9折,第四次是满300送120等等。如果让你设计你怎么做?

我们都在说面向对象编程,但是并不是类越多越好。分类的目的是为了抽象,将所有共性(相同的属性和功能)的对象抽象成一个类。

我们先来找共性,打折类的需求我们可以抽象成一个需求,因为他们的共性就是折扣率和原价。

满多少送多少类可以抽象成一个需求,因为他们的共性就是总价和扣除价。

正常价是原价。

从需求中我们可以看到,我们现在有三类需求,这三类需求分别抽象成三个共性类:正常价格类,折扣类,满多少送多少类。

我们想一下,这三个类有什么相同点?就是不管是哪个类,既然是卖商品,最后都要算总价,因此,我们可以定义一个父类cashSuper,提供统一方法接口cashTotal。因此,我们抽象出的结构类图如下:


?

我们按照上述类图,来定义各类:

父类CashSuper,提供总价接口。?

class="父类CashSuper ">public interface CashSuper {
    double cashTotal(double total);
}

?正常价类,实现父类接口,总价计算方式为商品原总价。

public class CashNomal implements CashSuper {

    public double cashTotal(double total) {
        return total;
    }
}

?折扣类,实现父类接口,总价计算方式为 折扣*商品原总价:

public class CashDiscount implements CashSuper {
    private double discount;

    public CashDiscount(String discount){
        this.discount = Double.parseDouble(discount);
    }

    public double cashTotal(double total) {
        //最后的商品总价是 原总价*折扣
        return total*discount;
    }
}

?返利类,实现父类接口,总价计算方式为 商品原总价-根据返利条件算出的返利金额:

public class CashReturn implements CashSuper {
    //返利条件
    private double returnCondition;
    //返利金额
    private double returnMoney;

    //对于消费满多少送多少的,我们需要知道返利的条件(比如满300),我们还需要满足条件后赠费的金额
    public CashReturn(String returnCondition,String returnMoney){
        this.returnCondition = Double.parseDouble(returnCondition);
        this.returnMoney = Double.parseDouble(returnMoney);
    }

    public double cashTotal(double total) {
        //最后的商品总价是 总价减去返利价
        if(total>this.returnCondition){
            total = total - Math.floor(total/returnCondition)*returnMoney;
            return total;
        }
        return total;
    }
}

?

好了,根据UML类图我们建立了基本的类,并且都实现了CashSuper中的总价计算方法。这么多类,每个类的的总价计算方法不尽相同,我们下一步怎么做?

我们看看类图,CashSuper是所有实现的父接口类,让一个对象持有这个接口类的实现对象,我们是不是可以不需要关注具体实现呢?我们尝试下。先修改类图。


?定义一个CashContext类,它持有CashSuper类,在获取总价时,让他负责去找对应的实现,我们不关心实现内容,具体如下:

public class CashContext{
    //持有各种促销手段的父类对象
    private CashSuper cs;

    //根据具体的促销策略,我们初始化父类实现
    public CashContext(CashSuper cs){
        this.cs = cs;
    }
    //直接将原总价传递,让具体的实现去处理得到最后的总价
    public double getTotal(double total) {
        return cs.cashTotal(total);
    }
}

?但是这么做,客户端就需要承担初始化CashContext的任务,如下:

public static void main(String[] args){
        int strategyType = 1;
        double oldTotal = 888;

        CashContext cc = null;
        //1.代表正常收费 2.代表打八折 3.代表满300送100
        switch (strategyType){
            case 1:
                cc = new CashContext(new CashNomal());
                break;
            case 2:
                cc = new CashContext(new CashDiscount("0.8"));
                break;
            case 3:
                cc = new CashContext(new CashReturn("300","100"));
                break;
        }

        System.out.println("最后的总价是:"+cc.getTotal(oldTotal));
    }

那么有没有更好的方式,将这些判断逻辑转移,而使客户端对外统一?我们看到,上面的判断代码主要是为了获取不同的CashSuper,那么我们就可以把生产CashSuper过程使用简单工厂模式去做。我们先修改CashContext类,让它自己去根据类型去获取CashSuper,代码如下

public class CashContext{
    //持有各种促销手段的父类对象
    private CashSuper cs;

    //根据具体的促销策略,我们初始化父类实现
    public CashContext(int strategyType){
        //1.代表正常收费 2.代表打八折 3.代表满300送100
        switch (strategyType){
            case 1:
                cs = new CashNomal();
                break;
            case 2:
                cs = new CashDiscount("0.8");
                break;
            case 3:
                cs = new CashReturn("300","100");
                break;
        }
        this.cs = cs;
    }
    //直接将原总价传递,让具体的实现去处理得到最后的总价
    public double getTotal(double total) {
        return cs.cashTotal(total);
    }
}

?这样,我们的客户端代码就很简单了,只需要传入促销策略类型,其他的事情就不需要管了:

public static void main(String[] args){
        //1.代表正常收费 2.代表打八折 3.代表满300送100
        int strategyType = 3;
        double oldTotal = 888;

        CashContext cc = new CashContext(strategyType);
        System.out.println("最后的总价是:"+cc.getTotal(oldTotal));
    }

?

二、总结

我们回过头来,什么是策略模式。策略模式定义了一组算法,分别封装起来,让它们之间可以互相替换,而算法的变化不会影响客户的使用。每一种算法的要完成的工作是相同的,只是实现不同,因此我们可以抽象出公共的算法接口类Strategy。那么为什么要增加Context类?为了功能层次清晰,因为Context类拥有了Strategy类的实现对象,只要提供了策略类Strategy的具体实现对象,Context类就可以统一对外提供客户端想要的结果。这样,外部看起来是透明统一的。这样,后期维护时候,不需要修改客户端代码,只需要修改统一对外的Context类。也就是说,策略模式对外的统一对象就是Context。

?

我们看下策略模式的结构类图:


?

当然,让Context类中承担判断也是让人很不爽的,一堆判断,后期增加促销类型时,虽然客户端不用管,但是也得修改这里,所以,我们在说到反射时,再优化这里的代码。

?

?

?

?

?

?

上一篇: Java中serializable序列化 下一篇: 没有下一篇了!
发表评论
用户名: 匿名