策略模式

Java基础

浏览数:128

2019-8-21

策略模式

一、概念

定义一系列的算法,把他们一个个封装起来,并且使他们可互相替换。本模式使得算法可独立于使用它的客户而变化。

二、使用场景

一个类定义了多种行为,并且这些行为在这个类的方法中以多个条件语句的形式出现,那么可以使用策略模式避免在类中使用大量的条件语句。

三、UML结构图

策略模式UML.png

通过一个持有算法的Context上下文对象,来封装一系列的算法。
Context对象并不负责具体决定哪个算法,而是把算法选择工作交给了Client端,Client端可以选择好具体的算法,把其设置到上下文对象中,让上下文对象持有客户端所选择的具体的策略。
当客户端通知Context类去执行某项他想要的业务功能时,Context类就会转到具体的算法中。

四、代码示例

1.不使用策略模式的代码及弊端:

PriceCalculator:

public class PriceCalculator {
    
    private static final int BUS = 1;
    private static final int SUBWAY = 2;
    
    public static void main(String[] args) {
        PriceCalculator calculator = new PriceCalculator();
        System.out.println("20km公交票价:"+calculator.calculatePrice(20,BUS));
        System.out.println("20km地铁票价:"+calculator.calculatePrice(20,SUBWAY));
    }

    /**
     * 公交车计价 10km之内1块钱;
     * 超过10km,每加一块钱可以乘5km
     * @param km
     * @return
     */
    private int busPrice(int km){
        //超过10km的距离
        int extraTotal = km - 10;
        //超过距离是5km的倍数
        int extraFactor = extraTotal / 5;
        //超过的距离对5km取余
        int fraction = extraTotal % 5;
        //计算价格
        int price = 1 + extraFactor % 5;
        
        return fraction > 0? ++price : price;
    } 
    
    /**
     * 地铁计价 : 6-12km:4块 ;12-22km:5块; 22-32km:6块;其他距离7块
     * @param km
     * @return
     */
    public int subwayPrice(int km){
        if(km <= 6){
            return 3;
        }else if(km > 6 && km < 12){
            return 4;
        }else if(km >12 && km < 22){
            return 5;
        }else if(km >22 && km < 32){
            return 6;
        }
        return 7;
    }
    
    /**
     * 根据不同类型方式计价
     * @param km
     * @param type
     * @return
     */
    int calculatePrice(int km,int type){
        if(type == BUS){
            return busPrice(km);
        }else if(type == SUBWAY){
            return subwayPrice(km);
        }
        return 0;
    }
    
    
}

弊端:
PriceCalculator 类很明显的一个问题就是不是单一职责,首先它承担计算公交和地铁乘坐价格的职责;另一个问题是通过if-else的形式来判断使用哪种计算形式,当增加一种出行方式时,如出租车,那么就需要在PriceCalculator中增加一个方法来计算出租车出行的价格,并且在calculatePrice(int km,int type)函数增加一个判断。

此种情况下,如果要增加一个出租车的类型时:

    private static final int TAXI = 3;
    
    private int taxiPrice(int km){
        return km * 2;
    }
    
    int calculatePrice(int km,int type){
        if(type == BUS){
            return busPrice(km);
        }else if(type == SUBWAY){
            return subwayPrice(km);
        }else if(type == TAXI){
            return taxiPrice(km);
        }
        return 0;
    }

2.使用策略模式实现:

AbstractStrategy:

public interface AbstractStrategy {
    
    //按距离计算价格
    int calculatePrice(int km);

}

BusStrategy:

public class BusStrategy implements AbstractStrategy{

    @Override
    public int calculatePrice(int km) {
        //超过10km的距离
        int extraTotal = km - 10;
        //超过距离是5km的倍数
        int extraFactor = extraTotal / 5;
        //超过的距离对5km取余
        int fraction = extraTotal % 5;
        //计算价格
        int price = 1 + extraTotal % 5;
        return fraction > 0 ? ++price : price;
    }

}

SubwayStrategy:

public class SubwayStrategy implements AbstractStrategy{

    @Override
    public int calculatePrice(int km) {
        if(km <= 6){
            return 3;
        }else if(km > 6 && km < 12){
            return 4;
        }else if(km > 12 && km < 22){
            return 5;
        }else if(km > 22 && km < 32){
            return 6;
        }
        return 7;
    }

}

TaxiStrategy:

public class TaxiStrategy implements AbstractStrategy{

    @Override
    public int calculatePrice(int km) {
        return km * 2;
    }

}

Context:

public class Context {
    
    //持有抽象接口的引用
    private AbstractStrategy strategy;
    
    //把抽象引用作为参数传进来
    public void setStrategy(AbstractStrategy strategy){
        this.strategy = strategy;
    }
    
    public int calculatePrice(int km){
        return strategy.calculatePrice(km);
    }
    
    public static void main(String[] strings){
        Context calculator = new Context();
        //只需要在客户端动态的传入类型,而不需要更改管理类
        //calculator.setStrategy(new BusStrategy());
        calculator.setStrategy(new TaxiStrategy());
        System.out.println("公交车20km价格:"+calculator.calculatePrice(20));
    }

}

五、策略模式的优点

  • 上下文(Context)和具体策略(ConcreteStrategy)是松耦合关系。
  • 策略模式满足“开-闭原则”

作者:JS_HCX