策略模式

172 阅读3分钟

需求

某市公交公司要求做一个公交收费系统,针对不同公交方式采取不同收费算法。

解决方案

解决方案 V1.0

客户端 Client类

public class Client {

    public static void main(String[] args){
        PublicChargeAPI api = new PublicChargeAPI();
        api.calculatePriceOfBus(10);
        api.calculatePriceOfMetro(20);
    }
    
}

公交系统收费计算API PublicChargeAPI

public class PublicChargeAPI {

    public int calculatePriceOfBus(int distance){
        // 此处省略计算巴士价格代码
        return 0;
    }

    public int calculatePriceOfMetro(int distance){
        // 此处省略计算地铁价格代码
        return 0;
    }

}

方案缺点 没有统一的收费计算入口,无法做到共用逻辑的控制

改进方案 V1.1

客户端 Client类

public class Client {

    public static void main(String[] args){
        PublicChargeAPI api = new PublicChargeAPI();
        api.calculatePrice(10, PublicChargeAPI.BUS);
        api.calculatePrice(20, PublicChargeAPI.METRO);
    }
    
}

公交系统收费计算API PublicChargeAPI

public class PublicChargeAPI {

    public static final int BUS = 1;
    public static final int METRO = 2;

    public int calculatePrice(int distance, int type){
        if (type == BUS){
            return calculatePriceOfBus(distance);
        } else if (type == METRO){
            return calculatePriceOfMetro(distance);
        }
        return -1;
    }

    private int calculatePriceOfBus(int distance){
        // 此处省略计算巴士价格代码
        return 0;
    }

    private int calculatePriceOfMetro(int distance){
        // 此处省略计算地铁价格代码
        return 0;
    }

}

方案缺点  所有收费方式写在一个类里面,紧耦合,不符合单一职责原则。

改进方案 V1.2

客户端 Client类

同上

公交系统收费计算API PublicChargeAPI

public class PublicChargeAPI {

    public static final int BUS = 1;
    public static final int METRO = 2;

    public int calculatePrice(int distance, int type){
    
        if (type == BUS){
            BusCharge bc = new BusCharge();
            return bc.calculate(distance);
        } else if (type == METRO){
            MetroCharge mc = new MetroCharge();
            return mc.calculate(distance);
        }
        return -1;
    }

}

巴士收费方式类 BusCharge

public class BusCharge {

    public int calculate(int distance){
        // 此处省略计算巴士价格代码
        return 0;
    }

}

地铁收费方式类 MetroCharge

public class MetroCharge {

    public int calculate(int distance){
        // 此处省略计算地铁价格代码
        return 0;
    }

}

方案缺点

每次增加一种收费方式不仅要新添一个收费类,还要修改“PublicChargeAPI”类的“calculatePrice”方法,在其中新增一个“else”分支,这不符合“开闭原则”。之所以会这样是因为客户端“Client”类只做了一次“模糊选择”,选择了“1”或“2”等,并没有明确指明要用哪个收费类,所以在“PublicChargeAPI”类中必须根据“1”或“2”等来选择具体用哪个收费类

改进方案 V2.0(策略模式)

客户端 Client类

public class Client {

    public static void main(String[] args){
        PublicChargeAPI api = new PublicChargeAPI();
        api.setPublicChargeStrategy(new ImplBusCharge());
        api.calculatePrice(10);

        api.setPublicChargeStrategy(new ImplMetroCharge());
        api.calculatePrice(20);
    }

}

公交系统价格计算API PublicChargeAPI

public class PublicChargeAPI {

    IPublicCharge mChargeStrategy;

    public void setPublicChargeStrategy(IPublicCharge chargeStrategy){
        mChargeStrategy = chargeStrategy;
    }

    public int calculatePrice(int distance){
        return mChargeStrategy.calculate(distance);
    }

}

抽象收费接口 IPublicCharge

public interface IPublicCharge {
    int calculate(int distance);
}

巴士收费具体类 ImplBusCharge

public class ImplBusCharge implements IPublicCharge {

    @Override
    public int calculate(int distance) {
        // 此处省略计算巴士价格代码
        return 0;
    }

}

地铁收费具体类 ImplMetroCharge

public class ImplMetroCharge implements IPublicCharge {

    @Override
    public int calculate(int distance) {
        // 此处省略计算地铁价格代码
        return 0;
    }

}

方案缺点

此种方案也有缺点,相较于其他方案“Client”类除了需要认识“PublicChargeAPI”类还要认识“IPublicCharge”、“ImplBusCharge”、“ImplMetroCharge”等,这不符合“迪米特法则”(最少知识原则,一个对象要尽可能少的引用其他对象)。

总结

在调用同一个接口时如果有可能发生算法替换推荐使用策略模式,没有完美的设计模式。