设计模式之策略模式

1,992 阅读4分钟

以前完整的看过《大话设计模式》,虽然完整看过,也做过笔记,但现在依然很多已经很模糊。这段时间趁着离职,有时间,打算重新过一遍,该篇将介绍策略模式。

一、定义

定义(维基百科):策略模式作爲一種軟件設計模式,指對象有某個行爲,但是在不同的場景中,該行爲有不同的實現算法。比如每個人都要“交個人所得稅”,但是“在美國交個人所得稅”和“在中國交個人所得稅”就有不同的算稅方法。

策略模式,从模式名称中我们就可以窥探一二。无非就是一个动作可以有不同的策略,有不同的计算方法,我们将计算方法进行封装。如上的例子,美国税务计算、中国的税务计算采用了不同的计算方式(不同策略)。还有一个《大话设计模式》中的例子也很好的说明该设置模式,商场折扣,上次可能是满减,这次可能是直接打折,这就是两种不同的策略,那么我们在代码里该怎样设计,才会更清晰抽象这个问题呢?接下来我们就来看看策略模式是怎么抽象该类型的问题的。

二、策略模式UML图

我们看看策略模式是有怎样设计结构的。图片来源于维基百科。可以看到,策略模式定义了一个策略的接口Stragegy,该接口定义了一个方法,即为该策略的操作方法。再实现不同的策略类ConcreteStrategyA、ConcreteStrategyB,继承自策略接口。接着在上下文类Context类持有策略的引用,调用策略。


三、以商场打折为例子,我们来实现先策略模式。

1、先定义策略接口

package com.design.strategy;

/** * 商场打折接口 */
public interface IShopDiscountStrategy {    
    public int discount(int count);
}

2、再定义具体的策略

package com.design.strategy;
/** * 满减计算策略 */
public class DiscountFull implements IShopDiscountStrategy 
{    
    //满的金额    
    private int fullCount;    
    //减的金额    
    private int disCash;    
    public DiscountFull(int fullCount,int disCash){         
        this.fullCount = fullCount;        
        this.disCash = disCash;    
    }    
    @Override    
    public int discount(int count) {            
        if (count >= fullCount){            
            return count -disCash;        
        }        
        return count;    
    }
}

package com.design.strategy;
/** * 打折策略,打7折 */
public class DiscountPercent implements IShopDiscountStrategy 
{    
    //折扣率    
    private float disPer;    
    public DiscountPercent(float disPer){        
        this.disPer = disPer;    
    }    
    @Override    
    public int discount(int count) {        
        return (int) (count * disPer);    
    }
}

3、再定义上下文类,持有具体的策略引用。

package com.design.strategy;
public class DiscountContext {    
    private IShopDiscountStrategy iShopDiscountStrategy;    
    public DiscountContext(IShopDiscountStrategy iShopDiscountStrategy) {        
        this.iShopDiscountStrategy = iShopDiscountStrategy;    
    }    
    public int discount(int count){        
        return iShopDiscountStrategy.discount(count);    
    }
}

到此,策略模式编写的打折场景已经编写完毕,在来看看怎么调用

package com.design.strategy;public class StrategyTestMain {
    public static void main(String[] args) {        
        //使用满减策略计算实收总额        
        DiscountContext discountContext = new DiscountContext(new DiscountFull(200, 100));
        System.out.println(discountContext.discount(200));        
        //使用折扣策略计算实收总额        
        DiscountContext discountContext1 = new DiscountContext(new DiscountPercent(0.7f)); 
        System.out.println(discountContext1.discount(200));    
    }
}

四、优缺点

优点

1、简化单元测试

从例子我们就可以看出来每个策略都有单独的类来实现,我们可以当度对策略进行测试。

2、扩展性好

我们要定义新的策略,只需要编写一个新的策略,并对新策略进行测试即可,不修改原来已经存在策略的代码。

3、避免使用多重条件转移语句

策略模式中使用的策略是有客户端选择的,策略相关的类不需要通过多重条件去选择策略。

缺点

1、客户端需自行决定使用的策略

客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

2、定义的类比较多

每个策略都得定义一个类来实现。

结语

策略模式暂时总结到这里。看起来策略模式还是比较简单的,就是将不同的策略进行封装。实际适用于一个操作有不同的策略的应用场景中。如JDK中创建线程池,线程池任务满时,对提交的任务做处理就使用了策略模式。可以在创建线程池时,传入相应的处理策略。如下,最后一个参数传入一个处理策略。策略接口为RejectedExecutionHandler,具体的实现有:DiscardOldestPolicy,AbortPolicy....

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,
        long keepAliveTime,TimeUnit unit,
        BlockingQueue<Runnable> workQueue,
        RejectedExecutionHandler handler)