策略模式做爲一種軟件設計模式,指對象有某個行爲,可是在不一樣的場景中,該行爲有不一樣的實現算法。好比每一個人都要「交我的所得稅」,不一樣的收入要按的稅率是不同的。java
簡單截取一個類圖web
首先是一個基礎版的demo算法
package com.example.test.stratergy; public interface TaxRate { Double TaxPayable(Double income); }
package com.example.test.stratergy.strategyClass; import com.example.test.stratergy.RateAnnotation; import com.example.test.stratergy.TaxRate; public class Under3000 implements TaxRate { @Override public Double TaxPayable(Double income) { return income *0.03; } }
package com.example.test.stratergy.strategyClass; import com.example.test.stratergy.RateAnnotation; import com.example.test.stratergy.TaxRate; public class Between3000And12000 implements TaxRate { @Override public Double TaxPayable(Double income) { return (income-3000)*0.1 + 3000*0.03; } }
package com.example.test.stratergy.strategyClass; import com.example.test.stratergy.RateAnnotation; import com.example.test.stratergy.TaxRate; public class Exceed12000 implements TaxRate { @Override public Double TaxPayable(Double income) { return (income-12000)*0.2 + 9000*0.1 + 3000*0.03; } }
package com.example.test.stratergy; import com.example.test.stratergy.strategyClass.Between3000And12000; import com.example.test.stratergy.strategyClass.Exceed12000; import com.example.test.stratergy.strategyClass.Under3000; public class RatePayer { private TaxRate taxRate; public double tax(double income) { if (income > 0 && income <= 3000) { taxRate= new Under3000(); } else if (income > 3000 && income <= 12000) { taxRate= new Between3000And12000(); } else { taxRate= new Exceed12000(); } return taxRate.TaxPayable(income); } }
這樣咱們便實現了標準版的策略模式,可是這樣的程序在咱們的策略類少的狀況下或許還能夠,策略類多的時候就避免不了大量的ifelse,這樣的代碼很不優雅,而且每次須要去修改Context類,違反了開閉原則。因此咱們須要對其作改進,以加強代碼的可維護性。spring
自定義一個註解,來標識各個策略類設計模式
package com.example.test.stratergy; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented public @interface RateAnnotation { public double min()default 0d; public double max() default 99999999d; }
在策略類分別加上註解app
package com.example.test.stratergy.strategyClass; import com.example.test.stratergy.RateAnnotation; import com.example.test.stratergy.TaxRate; @RateAnnotation(min = 0,max = 3000) public class Under3000 implements TaxRate { @Override public Double TaxPayable(Double income) { return income *0.03; } }
package com.example.test.stratergy.strategyClass; import com.example.test.stratergy.RateAnnotation; import com.example.test.stratergy.TaxRate; @RateAnnotation(min =3000,max = 12000) public class Between3000And12000 implements TaxRate { @Override public Double TaxPayable(Double income) { return (income-3000)*0.1 + 3000*0.03; } }
package com.example.test.stratergy.strategyClass; import com.example.test.stratergy.RateAnnotation; import com.example.test.stratergy.TaxRate; @RateAnnotation(min=12000) public class Exceed12000 implements TaxRate { @Override public Double TaxPayable(Double income) { return (income-12000)*0.2 + 9000*0.1 + 3000*0.03; } }
定義一個工廠類,負責加載策略類,解析其上的註解,根據條件返回對應的策略框架
package com.example.test.stratergy; import java.io.File; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.List; public class StrategyFactory { private static final String strategyPackage = "com.example.test.stratergy.Impl"; private List<Class<? extends TaxRate>> classList = new ArrayList<>(); private ClassLoader loader = this.getClass().getClassLoader(); private StrategyFactory(){ init(); } public TaxRate cteateTaxRateStrategy(double income){ System.out.println(classList.size()); for (Class<? extends TaxRate> clazz: classList) { RateAnnotation rateAnnotation = handleAnnotation(clazz); if(income >rateAnnotation.min() && income <=rateAnnotation.max()) { try { return clazz.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } return null; } private RateAnnotation handleAnnotation(Class<? extends TaxRate> clazz){ Annotation[] annotations = clazz.getDeclaredAnnotations(); if(annotations==null || annotations.length ==0){ return null; } for (Annotation annotation:annotations) { if(annotation instanceof RateAnnotation){ return (RateAnnotation)annotation; } } return null; } public void init(){ File[] TaxRateClasses = getFiles(); Class<TaxRate> TaxRateClass = null; try { TaxRateClass = ( Class<TaxRate>)loader.loadClass(TaxRate.class.getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } for (File file: TaxRateClasses) { try { Class<?> clazz = loader.loadClass(strategyPackage + "."+file.getName().replace(".class","")); if(TaxRate.class.isAssignableFrom(clazz) && clazz !=TaxRateClass){ classList.add((Class<? extends TaxRate>)clazz); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } private File[] getFiles(){ try { File file = new File(loader.getResource(strategyPackage.replace(".","/")).toURI()); return file.listFiles((File filepath) ->{ if(filepath.getName().endsWith(".class")){ return true; }else{ return false; } }); } catch (Exception e) { e.printStackTrace(); } return null; } public static StrategyFactory getInstance(){ return InnerFactory.strategyFactory; } private static class InnerFactory{ private static StrategyFactory strategyFactory = new StrategyFactory(); } }
修改RatePayer ide
package com.example.test.stratergy; public class RatePayer { private TaxRate taxRate; public double tax(double income){ taxRate = StrategyFactory.getInstance().cteateTaxRateStrategy(income); return taxRate.TaxPayable(income); } }
這樣咱們就去除了ifelse的結構,而且程序的擴展性很強,若是有新的策略,只須要在工廠類加載的包中添加對應的類便可。在目前的java項目開發中,相信你們都是基於spring框架的,那麼咱們就能夠利用spring的ioc技術來實現優雅的策略模式。測試
首先咱們的策略類加上@Service註解,由spring來管理this
package com.example.test.service.Impl; import com.example.test.stratergy.RateAnnotation; import com.example.test.service.TaxRate; import org.springframework.stereotype.Service; @Service @RateAnnotation(min = 0,max = 3000) public class Under3000 implements TaxRate { @Override public Double TaxPayable(Double income) { return income *0.03; } }
package com.example.test.service.Impl; import com.example.test.stratergy.RateAnnotation; import com.example.test.service.TaxRate; import org.springframework.stereotype.Service; @Service @RateAnnotation(min =3000,max = 12000) public class Between3000And12000 implements TaxRate { @Override public Double TaxPayable(Double income) { return (income-3000)*0.1 + 3000*0.03; } }
package com.example.test.service.Impl; import com.example.test.stratergy.RateAnnotation; import com.example.test.service.TaxRate; import org.springframework.stereotype.Service; @Service @RateAnnotation(min=12000) public class Exceed12000 implements TaxRate { @Override public Double TaxPayable(Double income) { return (income-12000)*0.2 + 9000*0.1 + 3000*0.03; } }
而後再須要使用的地方,經過@autowire注入全部的策略類
package com.example.test.controller; import com.example.test.service.TaxRate; import com.example.test.stratergy.RateAnnotation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * @author * @description測試Controller */ @RestController public class TestController { @Autowired private List<TaxRate> list; @GetMapping("/test") public void tax(Double income){ System.out.println(list.size()); for (TaxRate taxRate:list) { RateAnnotation rateAnnotation = taxRate.getClass().getDeclaredAnnotation(RateAnnotation.class); if(income> rateAnnotation.min() && income<= rateAnnotation.max()) { System.out.println("應繳稅款" + taxRate.TaxPayable(income)+ "元"); return; } } System.out.println(11111111); } }
以上即是策略模式的使用,下面說下策略模式的優缺點
優勢:
一、 策略模式提供了管理相關的算法族的辦法。策略類的等級結構定義了一個算法或行爲族。恰當使用繼承能夠把公共的代碼轉移到父類裏面,從而避免重複的代碼。
二、 策略模式提供了能夠替換繼承關係的辦法。繼承能夠處理多種算法或行爲。若是不是用策略模式,那麼使用算法或行爲的環境類就可能會有一些子類,每個子類提供一個不一樣的算法或行爲。可是,這樣一來算法或行爲的使用者就和算法或行爲自己混在一塊兒。決定使用哪種算法或採起哪種行爲的邏輯就和算法或行爲的邏輯混合在一塊兒,從而不可能再獨立演化。繼承使得動態改變算法或行爲變得不可能。
三、 使用策略模式能夠避免使用多重條件轉移語句。多重轉移語句不易維護,它把採起哪種算法或採起哪種行爲的邏輯與算法或行爲的邏輯混合在一塊兒,通通列在一個多重轉移語句裏面,比使用繼承的辦法還要原始和落後。
缺點
一、客戶端必須知道全部的策略類,並自行決定使用哪個策略類。這就意味着客戶端必須理解這些算法的區別,以便適時選擇恰當的算法類。換言之,策略模式只適用於客戶端知道全部的算法或行爲的狀況。
二、 策略模式形成不少的策略類,每一個具體策略類都會產生一個新類。有時候能夠經過把依賴於環境的狀態保存到客戶端裏面,而將策略類設計成可共享的,這樣策略類實例能夠被不一樣客戶端使用。換言之,可使用享元模式來減小對象的數量。
策略模式應用場景:
一、 多個類只區別在表現行爲不一樣,可使用Strategy模式,在運行時動態選擇具體要執行的行爲。 二、 須要在不一樣狀況下使用不一樣的策略(算法),或者策略還可能在將來用其它方式來實現。 三、 對客戶隱藏具體策略(算法)的實現細節,彼此徹底獨立。