大话设计模式:策略模式

策略模式:
策略模式--我理解的是将不变的东西提取出来,将变化的东西放在具体实现里,通过对具体实现的操作来实现扩展操作。
github地址:https://github.com/iearl/gof23/
例如:电影的票价对于不同用户的价格是不同的,可能存在学生票、会员票和普通票等,可以将计算价格的方法放到一个类中,专门设计一个类来计算票价,通过调用这个方法就可能计算不同角色对应得票价是多少。
/**
 * 观影类型不同得电影票价
 */
public class MovieTicket {
    private String type;//观影类型
    private double price;//票价

    public void setType(String type) {
        this.type = type;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    /**
     * 放回当前观影人类型对应得票价:
     * 学生票价八折
     * vip票价七折
     * 其余原价
     * @return
     */
    public double curTicketPrice(){
        if("student".equals(type)){
            System.out.println("学生票");
            return price*0.8;
        }else if ("vip".equals(type)){
            return price*0.7;
        }else{
            return price;
        }
    }

    @Override
    public String toString() {
        return "MovieTicket{" +
                "type='" + type + '\'' +
                ", price=" + price +
                '}';
    }
}
将计算价格得方法封装到一个类中

            这样做是最简单得,一个类通过if-else的判断就能完成不同角色对应价格判断(其实这是最简单的简单工厂设计模式,稍后将学习到)。

            但这样做有一个问题,当我们想要添加儿童这样的角色应该怎么办,改变现有的类?这违反的OCP即开闭原则。

           应该怎么办?这时再看策略模式的定义,我们可以将不变和变化的分别抽取出来

               将程序中不变的东西提取出来,置成抽象类或接口,很明显方法中的计算价格的方法抽取出来,抽象成接口或抽象类。

public interface IStrategy {
    double curTicketPrice(Context context);
}

              将程序中变化的东西作为接口的实现类,将不同角色对应价格的代码提取出来放在不同类中,子类根据自己情况来实现具体方法。例如儿童票5折。

public class StudentStrategy implements IStrategy {
    public double curTicketPrice(Context context) {
        return context.getPrice()*0.8;
    }
}

   如果添加儿童票,我们仅需要新增类来实现这个接口就好。不需要再修改原先的代码。

public class ChildStrategy implements IStrategy {
    public double curTicketPrice(Context context) {
        return context.getPrice()*0.5;
    }
}
Context context:这时什么东西,再之前通过if-else没见过过这个类,这个类有什么作用呢?我们看一下这个类的结构
public class Context {
    private IStrategy IStrategy;
    private double price;

    public double getMethod() {
        return IStrategy.curTicketPrice(this);
    }

    public Context(IStrategy IStrategy, double price) {
        this.IStrategy = IStrategy;
        this.price = price;
    }

    public IStrategy getIStrategy() { return IStrategy; }

    public void setIStrategy(IStrategy IStrategy) {  this.IStrategy = IStrategy; }

    public double getPrice() { return price; }
}

        可以看待这个类是原先类价格和抽象角色,再这个类中设置。这个类的作用是将价格和类型通过构造器设置进去,通过自身提供的getMethod方法来输出最后的价格。

        等等,这个类的具体作用是什么,感觉这个类可以不要,完全可以再客户端设置这些值,为什么单独的抽出来呢?

        原因很简单:

             在上一篇讲的设计模式六大原则中有一个最小知道原则,一个类应该尽量少的与外界通信,如果必须通信可以通过第三方类,这个context就是第三方类;

             这里的context还是单一原则的体现,体现引起类变化的因素只有一个那就是Context类,单一原则可看成定义与使用的分离。

    到现在为止,是否对策略模式有些了解呢?现在给出策略模式的准确定义:

策略模式:定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

    策略模式角色:

    策略接口角色:可以看作是将不变的地方抽取出来形成的抽象类或接口,约定一些策略算法。
策略具体角色:可以看作是将变化提取出来成为称为接口角色的实现类,具体实现策略类的算法。
上下文环境:负责客户端与策略之间的交互,持有策略的引用。 
策略模式再说明:
策略模式中的策略类可以看作是具体的if-else里面的操作,只是将相关一类算法实现替换扩展而不会影响以前的代码。
具体代码:再github上,地址:https://github.com/iearl/gof23/tree/master/src/main/java/coms/ants/behavior/strategy/simple
UML图,如下

解析UML图

IStratege:充当的是抽象策略角色,Context充当下上文环境角色,VipStartegy、StudentStrategy和ChildStrategy充当具体策略角色,Main充当客户端角色。

绿色的线表示继承:实心箭头和虚线

白色虚线表示依赖关系:使用关系,表示一个事物使用另外一个事物,特定的事物改变可能会影响使用该事物的其他事物。

白色实线+实心菱形表示组合关系:类整体与部分之间关系,整体对象控制成员对象的生命周期,两个同生共死。Context是整体。

策略模式:扩展 当新增的策略子类比抽象策略类功能多时

一:具体策略类

public class TempIStrategy implements IStrategy {
    private String account;

    public TempIStrategy(String account) {
        this.account = account;
    }

    @Override
    public double curTicketPrice(Context context) {
        System.out.println("账号是:"+account);
        return context.getPrice()*0.87;
    }
}

    具体做法是,子类在重写接口时,需要在自己策略类在添加有关操作,具体做法是新增子类添加什么就在具体实现类添加什么。

   这样做的好处时,只添加具体策略,缺点是和其他策略风格不配。

    使用具体策略的代码:https://github.com/iearl/gof23/

二:添加具体的上下文环境

public class TempContext extends Context {
    private String account;

    public String getAccount() {
        return account;
    }
    
    public TempContext(IStrategy iStrategy, double price,String account) {
        super(iStrategy, price);
        this.account = account;
    }
}

    将新增的账号在具体的上下文环境中添加进去,这样保证策略类一致。

策略模式:在JDK中


    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }
RejectedExecutionHandler 采用策略模式
具体4个策略类。

策略模式:优缺点

优点:相互替换扩展性好、避免多个if-else判断。
缺点:必须了解具体策略、增加策略类数量、算法在运行时只有一个不能适合多层次。
posted @ 2019-01-14 15:24  i孤独行者  阅读(574)  评论(1编辑  收藏  举报