簡介
策略模式:策略模式可以定制目標對象的行為,它通過傳入不同的策略實現,來配置目標對象的行為。使用策略模式,就是為了定制目標對象在某個關鍵點的行為。
策略模式中的角色:
- 上下文類:持有一個策略類的引用,最終給客戶端調用
- 策略接口:定義規范,所有的策略類都要實現
- 具體策略累:實現具體算法
優缺點:
- 優點:
- 避免多重if else語句
- 策略類之間可以自由切換;
- 增加一個新的策略只需要添加一個具體的策略類即可;
- 缺點:客戶端必須知道所有的策略類,并自行決定使用哪一個策略類
策略模式的實現
案例:售貨員和優惠策略,用戶可以配置售貨員使用的優惠策略
第一步:上下文類
public class SaleMan {// 上下文類持有策略接口的實例,用戶通過傳入策略接口的不同實現,來配置上下文類的行為private final Strategy strategy;public SaleMan() { }public SaleMan(Strategy strategy) {this.strategy = strategy;}public void saleManShow() {System.out.print("售貨員促銷商品時使用的優惠策略:");strategy.show();}
}
第二步:策略接口
public interface Strategy {void show();
}
第三步:具體的策略類
// 策略1
public class StrategyA implements Strategy {@Overridepublic void show() {System.out.println("買一送一");}
}// 策略2
public class StrategyB implements Strategy{@Overridepublic void show() {System.out.println("滿200減50");}
}// 策略3
public class StrategyC implements Strategy{@Overridepublic void show() {System.out.println("滿1000減200");}
}
測試:
public class Client {public static void main(String[] args) {SaleMan saleMan = new SaleMan(new StrategyA());saleMan.saleManShow();}
}
總結:在這個案例中,用戶在創建售貨員實例時,可以配置售貨員使用的優惠策略
策略模式本質上就是把上下文類中的某個關鍵流程提取出來,抽象出接口和實現類,接口就是關鍵流程要做什么,實現類就是關鍵流程的不同實現。如果不使用策略模式,這些策略都要放到上下文類中,那么用戶需要傳入參數來指定走哪條鏈路,這會導致大量的if else。策略模式可以讓目標類更簡潔。
使用案例
jdk源碼案例:線程池的拒絕策略
1、上下文類
public class ThreadPoolExecutor extends AbstractExecutorService {// 1、上下文類持有策略接口的實例// 拒絕策略private volatile RejectedExecutionHandler handler;// 2、用戶通過構造方法來指定上下文類使用哪個拒絕策略public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) { // 拒絕策略// 這里省略了大量代碼,重點關注,外部傳入策略接口的實例,來指定線程池在無法// 執行任務時該怎么做this.handler = handler;}// 3、拒絕策略在上下文類中的執行。線程池執行任務的機制public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command); else if (workerCountOf(recheck) == 0)addWorker(null, false);}else if (!addWorker(command, false))reject(command); // 重點看這里,如果無法執行任務,就執行拒絕策略}// 執行拒絕策略final void reject(Runnable command) {handler.rejectedExecution(command, this);}
}
2、策略接口
public interface RejectedExecutionHandler {// 策略方法,任務無法執行時線程池該怎么辦void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
3、具體的策略實現
// 策略1:拋異常,這是默認的拒絕策略
public static class AbortPolicy implements RejectedExecutionHandler {public AbortPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {// 拋異常throw new RejectedExecutionException("Task " + r.toString() +" rejected from " +e.toString());}
}// 策略2:如果線程池沒有關閉,由調用者來執行任務
public static class CallerRunsPolicy implements RejectedExecutionHandler {public CallerRunsPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {r.run();}}
}// 策略3:如果線程池沒有關閉,丟棄隊列中最老的任務
public static class DiscardOldestPolicy implements RejectedExecutionHandler {public DiscardOldestPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {e.getQueue().poll(); // 阻塞隊列的頭結點出隊e.execute(r); // 執行當前異步任務}}
}// 策略4:丟失任務,靜默,不拋異常
public static class DiscardPolicy implements RejectedExecutionHandler {public DiscardPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
}
在學習線程池時,需要了解線程池的拒絕策略,并且選擇一個合適的拒絕策略,這里就是原因,使用了策略模式,用戶需要了解所有策略,并且指定使用哪種策略。