还在使用if else写代码?试试 “策略模式” 吧!
我們使用的app大多都有分享的功能,我們可以選擇分享到不同的地方,比如微博、微信、QQ等等,雖然是同一個內容,但是分享到不同的平臺就會有不同的處理方式,比如要跳轉到不同的app或者直接復制鏈接等等。如果讓你來實現這個功能,你會如何實現呢?
如果你對設計模式不熟悉,那么第一反應就是有if...else或者switch語句來進行條件判斷,根據用戶的不同選擇而使用不同的處理方法。我們用代碼簡化地處理一下:
public?void?Share{ public?void?shareOptions(String?option){if(option.equals("微博")){//function1();//...}else?if(option.equals("微信")){//function2();//...}else?if(option.equals("朋友圈")){//function3();//...}else?if(option.equals("QQ")){//function4();//...}//... }如果只是寫一個這么簡單的功能,那么這樣的代碼也未嘗不可,因為這樣的代碼量不多,后續也不需要怎么拓展和修改,維護起來也不算麻煩。但是,我們工作中遇到的都是一些比較復雜的項目,要保證項目的可讀性、可維護性和可拓展性,就必須在代碼上下功夫了。
這里我們就要提到一個概念,那就是設計模式了。設計模式是指針對軟件開發過程中重復發生的問題的解決辦法。其中以被稱為Gang of Four(GoF)的4人整理出的23種設計模式最為有名。
當然,我不可能在這里把23種設計模式全部介紹完,就算全部介紹一遍,而且你還能全部記住,也不一定奏效。首先,你沒有必要全部記下來,因為這23種設計模式并非都是經常使用到的設計模式。其次,死記硬背下這些設計模式沒有任何意義,重要的是在自己腦海中理解設計模式是怎樣解決問題的。
今天我們就談談可以優化以上問題的設計模式,策略模式(Strategy ?Design Pattern)。
策略模式(Strategy ?Design Pattern)
策略指的是計策、謀略,而模式一般指人為整理而成的,在某種場景下重復發生的問題的解決辦法。在編程中,我們可以把策略看做是“算法”,而策略模式,按照GoF的定義,就是我們設計一些算法,把它們封裝起來,讓它們可以相互替換,這樣就可以輕松地切換不同的算法來解決同一個問題。
我們看一下策略模式中有哪些角色。
Strategy(策略)Strategy角色負責決定實現策略所必需的接口(API)。在示例程序中,由strategy接口扮演此角色。
ConcreteStrategy(具體的策略)ConcreteStrategy角色負責實現Strategy角色的接口(API),即負責實現具體的策略(戰略、方向、方法和算法)。
Context(上下文)負責使用Strategy角色。Context角色保存了ConcreteStrategy角色的實例,并使用ConcreteStrategy角色去實現需求(總之,還是要調用Strategy角色的接口(API))。
再看看策略模式的類圖:
介紹到這里,相信你對策略模式有了初步的認識,那我們就用策略模式來重構前面的代碼,讓你加深對策略模式的理解。
//定義策略接口 public?interface?DealStrategy{void?dealMythod(String?option); }//定義具體的策略1 public?class?DealSina?implements?DealStrategy{@overridepublic?void?dealMythod(String?option){//...} }//定義具體的策略2 public?class?DealWeChat?implements?DealStrategy{@overridepublic?void?dealMythod(String?option){//...} }//定義上下文,負責使用DealStrategy角色 public?static?class?DealContext{private?String?type;private?DealStrategy?deal;public? DealContext(String?type,DealStrategy?deal){this.type?=?type;this.deal?=?deal;}public?DealStrategy getDeal(){return?deal;}public?boolean?options(String?type){return?this.type.equals(type);} }public?void?Share{private?static?List<DealContext>?algs?=?new?ArrayList();//靜態代碼塊,先加載所有的策略static?{algs.add(new?DealContext("Sina",new?DealSina()));algs.add(new?DealContext("WeChat",new?DealWeChat()));} public?void?shareOptions(String?type){DealStrategy?dealStrategy?=?null;for?(DealContext deal?:?algs)?{if?(deal.options(type))?{dealStrategy?=?deal.getDeal();break;}??}dealStrategy.dealMythod(type);} }再回憶一下策略模式中的各個角色,代碼中的DealStrategy接口就是策略,DealSina和DealWeChat是具體的策略,DealContext就是使用策略的上下文。
所以這樣的代碼已經符合策略模式的代碼結構了。我們通過策略模式將策略的定義、創建、使用解耦,讓每一部分都不至于太復雜,也去除了if...else這樣的條件判斷語句,代碼的可維護性和可拓展性都提高了。
我們把可變的部分放到 了Share 類中的靜態代碼段中。如果有新的需求,要添加一個分享的方式時,只需要定義好具體的策略,然后修改 Share 類中的靜態代碼段,其他代碼都不需要修改。
策略模式還是比較常用的一種設計模式,比如java中給我定義好的Comparator 接口就是策略模式的一個實踐。
如果需要對某個類的排序,而該類沒有實現Comparable接口,那么可以建立一個實現Comparator接口的比較器即可。通過實現Comparator類來新建一個比較器,然后通過該比較器來對類進行排序。
//策略接口 public?interface?Comparator<T>?{int?compare(T?o1,?T?o2); }//需要我們來定義具體的策略 public?class?sorter?implements?Comparator{public?int?compare(String?s1,?String?s2)?{return?Integer.compare(s1.length(),?s2.length());} } //一般我們會用更簡潔的Lambda表達式來實現如果你使用的是Java ,想更符合開閉原則,并對反射有一定了解,那還可以通過反射來避免對類的修改。
你可以通過一個配置文件或者定義一個注解來標注定義的策略類;通過讀取配置文件或者搜索被標注的策略類,通過反射動態地加載這些策略類、創建策略對象;當我們新添加一個策略的時候,只需要將這個新添加的策略類添加到配置文件或者用定義的注解標注即可。
Strategy模式特意將算法與其他部分分離開來,只是定義了與算法相關的接口(APl),然后在程序中以委托的方式來使用算法。
這樣看起來程序好像變復雜了,其實不然。例如,當我們想要通過改善算法來提高算法的處理速度時,如果使用了Strategy模式,就不必修改Strategy角色的接口(API)了,僅僅修改ConcreteStrategy角色即可。
而且,使用委托這種弱關聯關系可以很方便地整體替換算法。例如,使用Strategy模式編寫棋類程序時,可以方便地根據棋手的選擇切換AI電腦的水平。
至此,我們可以小結出策略模式的使用場景:
一個項目中有許多類,它們之間的區別僅在于它們的行為,希望動態地讓一個對象在許多行為中選擇一種行為時;
一個項目需要動態地在幾種算法中選擇一種時;
一個對象有很多的行為,不想使用多重的條件選擇語句來選擇使用哪個行為時。
策略模式不僅僅可以優化if else代碼,其主要的作用還是解耦策略的定義、創建和使用,控制代碼的復雜度,讓每個部分都不至于過于復雜、代碼量過多。除此之外,對于復雜代碼來說,策略模式還能讓其滿足開閉原則,添加新策略的時候,最小化、集中化代碼改動,減少引入 bug 的風險。
可能有人會問:狀態模式也可以優化if else,那么策略模式和狀態模式又有什么不同呢?
讓我們來簡單回顧一下狀態模式的類圖:
使用策略模式和狀態模式都可以替換被委托對象,而且它們的類之間的關系也很相似。但是兩種模式的目的不同。
在策略模式中,ConcreteStrategy角色是表示算法的類。在Strategy模式中,可以替換被委托對象的類。當然如果沒有必要,也可以不替換。
而在狀態模式中,ConcreteState角色是表示“狀態”的類。在State模式中,每次狀態變化時,被委托對象的類都必定會被替換。
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的还在使用if else写代码?试试 “策略模式” 吧!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FZOJ 2014年11月份月赛 yta
- 下一篇: zoj 3696 Alien's Org