<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          if else終結(jié)者 —— 策略模式

          共 19221字,需瀏覽 39分鐘

           ·

          2021-06-08 13:12

          公眾號關(guān)注 “GitHub今日熱榜
          設(shè)為 “星標(biāo)”,帶你挖掘更多開發(fā)神器!







          你是不是還在寫著大量的if else語句,if else 不僅難以維護不易擴展,而且使代碼臃腫不堪,想不想讓你的業(yè)務(wù)代碼更加的健壯,更易擴展,那你一定要學(xué)一學(xué)今天的主角策略模式。學(xué)會了策略模式的使用讓會你的代碼更加優(yōu)雅。老板看了給你加薪。同事看了對你仰慕。策略模式大家用了都說好。


          閱讀完本篇文章你將了解到什么是策略模式,策略模式的優(yōu)缺點,以及策略模式在源碼中的應(yīng)用。


          策略模式引入


          在軟件開發(fā)中,我們常常會遇到這樣的情況,實現(xiàn)某一個功能有多條途徑,每一條途徑對應(yīng)一種算法,此時我們可以使用一種設(shè)計模式來實現(xiàn)靈活地選擇解決途徑,也能夠方便地增加新的解決途徑。


          譬如商場購物場景中,有些商品按原價賣,商場可能為了促銷而推出優(yōu)惠活動,有些商品打九折,有些打八折,有些則是返現(xiàn)10元等。而優(yōu)惠活動并不影響結(jié)算之外的其他過程,只是在結(jié)算的時候需要根據(jù)優(yōu)惠方案結(jié)算。

          再比如不同的人出去旅游出行的交通方式也不同,經(jīng)濟條件好的會選擇高鐵飛機,而普通人可能會選擇綠皮火車。


          富豪老王打算去西藏旅游,老王定了豪華酒店,并且定了機票當(dāng)天直達(dá)。而普通人老張也要去西藏旅游,他打算選擇乘坐高鐵出行。而學(xué)生黨的我小汪肯定會選擇綠皮火車,主要是為了看路邊的風(fēng)景,而不是因為窮。


          下面我們用代碼來描述一下上訴場景:


          public class Travel {
              private String vehicle;//出行方式
              private String name;

              public String getName() {
                  return name;
              }

              public Travel(String name) {
                  this.name = name;
              }

              public void setName(String name) {
                  this.name = name;
              }

              public void setVehicle(String vehicle) {
                  this.vehicle = vehicle;
              }

              public String getVehicle() {
                  return vehicle;
              }

              public void TravelTool(){
                  if(name.equals("小汪")){
                      setVehicle("綠皮火車");
                  }else if(name.equals("老張")){
                      setVehicle("高鐵");
                  }else if(name.equals("老王")){
                      setVehicle("飛機");
                  }
                  System.out.println(name+"選擇坐"+getVehicle()+"去西藏旅游");
              }

          }

          public class Test {
              public static void main(String[] args) {
                  Travel travel1 = new Travel("小汪");
                  Travel travel2 = new Travel("老王");
                  Travel travel3 = new Travel("老張");
                  travel1.TravelTool();
                  travel2.TravelTool();
                  travel3.TravelTool();

              }
          }
          小汪選擇坐綠皮火車去西藏旅游
          老王選擇坐飛機去西藏旅游
          老張選擇坐高鐵去西藏旅游


          以上代碼雖然完成了我們的需求,但是存在以下問題:


          Travel類的TravelTool方法非常龐大,它包含各種人的旅行實現(xiàn)代碼,在代碼中出現(xiàn)了較長的 if…else… 語句,假如日后小汪發(fā)達(dá)了也想體驗一下做飛機去西藏旅游,那就要去修改TravelTool方法。違反了 “開閉原則”,系統(tǒng)的靈活性和可擴展性較差。


          算法的復(fù)用性差,如果在另一個系統(tǒng)中需要重用某些算法,只能通過對源代碼進(jìn)行復(fù)制粘貼來重用,無法單獨重用其中的某個或某些算法。


          策略模式


          策略模式的介紹


          策略模式(Strategy Pattern)中,定義算法族,分別封裝起來,讓他們之間可以互相替換,此模式讓算法的變化獨立于使用算法的客戶

              

          這算法體現(xiàn)了幾個設(shè)計原則,第一、把變化的代碼從不變的代碼中分離出來;第二、針對接口編程而不是具體類(定義了策略接口);第三、多用組合/聚合,少用繼承(客戶通過組合方式使用策略)。


          策略模式的原理類圖



          角色分析


          Context(環(huán)境類):環(huán)境類是使用算法的角色,它在解決某個問題(即實現(xiàn)某個方法)時可以采用多種策略。在環(huán)境類中維持一個對抽象策略類的引用實例,用于定義所采用的策略。


          Strategy(抽象策略類):它為所支持的算法聲明了抽象方法,是所有策略類的父類,它可以是抽象類或具體類,也可以是接口。環(huán)境類通過抽象策略類中聲明的方法在運行時調(diào)用具體策略類中實現(xiàn)的算法。


          ConcreteStrategy(具體策略類):它實現(xiàn)了在抽象策略類中聲明的算法,在運行時,具體策略類將覆蓋在環(huán)境類中定義的抽象策略類對象,使用一種具體的算法實現(xiàn)某個業(yè)務(wù)處理。


          我們下面用策略模式來改進(jìn)一下上面旅行的代碼例子。


          抽象策略類 Discount


          public abstract class AbstractTravle {
              private String vehicle;
              private String name;

              public AbstractTravle(String vehicle, String name) {
                  this.vehicle = vehicle;
                  this.name = name;
              }

              public String getVehicle() {
                  return vehicle;
              }

              public String getName() {
                  return name;
              }

              public abstract void TravelTool();
          }


          ConcreteStrategy(具體策略類)


          public class XiaoWang extends AbstractTravle{
              public XiaoWang(String vehicle, String name) {
                  super(vehicle, name);
              }

              @Override
              public void TravelTool() {
                  System.out.println(getName()+"選擇坐"+getVehicle()+"去西藏旅游");
              }
          }
          public class LaoWang extends AbstractTravle{
              public LaoWang(String vehicle, String name) {
                  super(vehicle, name);
              }

              @Override
              public void TravelTool() {
                  System.out.println(getName()+"選擇坐"+getVehicle()+"去西藏旅游");
              }
          }
          public class LaoZhang extends AbstractTravle{
              public LaoZhang(String vehicle, String name) {
                  super(vehicle, name);
              }
              @Override
              public void TravelTool() {
                  System.out.println(getName()+"選擇坐"+getVehicle()+"去西藏旅游");
              }

          }


          環(huán)境類


          public class Context {
              private AbstractTravle abstractTravle;

              public Context(AbstractTravle abstractTravle) {
                  this.abstractTravle = abstractTravle;
              }
              public void TravelTool() {
                  System.out.println(abstractTravle.getName()+"選擇坐"+abstractTravle.getVehicle()+"去西藏旅游");
              }
          }


          public class Test {
              public static void main(String[] args) {
                  Context context1 = new Context(new LaoWang("飛機", "老王"));
                  context1.TravelTool();
                  Context context2 = new Context(new LaoZang("高鐵", "老張"));
                  context2.TravelTool();
                  Context context3 = new Context(new XiaoWang("綠皮火車", "小汪"));
                  context3.TravelTool();
              }
          }
          老王選擇坐飛機去西藏旅游
          老張選擇坐高鐵去西藏旅游
          小汪選擇坐綠皮火車去西藏旅游


          策略模式總結(jié)


          策略模式的主要優(yōu)點如下:


          • 策略模式提供了對 “開閉原則” 的完美支持,用戶可以在不修改原有系統(tǒng)的基礎(chǔ)上選擇算法或行為,也可以靈活地增加新的算法或行為。

          • 策略模式提供了管理相關(guān)的算法族的辦法。策略類的等級結(jié)構(gòu)定義了一個算法或行為族,恰當(dāng)使用繼承可以把公共的代碼移到抽象策略類中,從而避免重復(fù)的代碼。

          • 策略模式提供了一種可以替換繼承關(guān)系的辦法。如果不使用策略模式而是通過繼承,這樣算法的使用就和算法本身混在一起,不符合 “單一職責(zé)原則”,而且使用繼承無法實現(xiàn)算法或行為在程序運行時的動態(tài)切換。

          • 使用策略模式可以避免多重條件選擇語句。多重條件選擇語句是硬編碼,不易維護。

          • 策略模式提供了一種算法的復(fù)用機制,由于將算法單獨提取出來封裝在策略類中,因此不同的環(huán)境類可以方便地復(fù)用這些策略類。


          策略模式的主要缺點如下:


          • 客戶端必須知道所有的策略類,并自行決定使用哪一個策略類。這就意味著客戶端必須理解這些算法的區(qū)別,以便適時選擇恰當(dāng)?shù)乃惴āQ言之,策略模式只適用于客戶端知道所有的算法或行為的情況。

          • 策略模式將造成系統(tǒng)產(chǎn)生很多具體策略類,任何細(xì)小的變化都將導(dǎo)致系統(tǒng)要增加一個新的具體策略類。

          • 無法同時在客戶端使用多個策略類,也就是說,在使用策略模式時,客戶端每次只能使用一個策略類,不支持使用一個策略類完成部分功能后再使用另一個策略類來完成剩余功能的情況。


          適用場景


          1. 一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種,那么可以將這些算法封裝到一個個的具體算法類中,而這些具體算法類都是一個抽象算法類的子類。換言之,這些具體算法類均有統(tǒng)一的接口,根據(jù) “里氏代換原則” 和面向?qū)ο蟮亩鄳B(tài)性,客戶端可以選擇使用任何一個具體算法類,并只需要維持一個數(shù)據(jù)類型是抽象算法類的對象。

          2. 一個對象有很多的行為,如果不用恰當(dāng)?shù)哪J剑@些行為就只好使用多重條件選擇語句來實現(xiàn)。此時,使用策略模式,把這些行為轉(zhuǎn)移到相應(yīng)的具體策略類里面,就可以避免使用難以維護的多重條件選擇語句。

          3. 不希望客戶端知道復(fù)雜的、與算法相關(guān)的數(shù)據(jù)結(jié)構(gòu),在具體策略類中封裝算法與相關(guān)的數(shù)據(jù)結(jié)構(gòu),可以提高算法的保密性與安全性。


          源碼分析策略模式的典型應(yīng)用


          Java Comparator 中的策略模式


          java.util.Comparator 接口是比較器接口,可以通過 Collections.sort(List,Comparator) 和 Arrays.sort(Object[],Comparator) 對集合和數(shù)據(jù)進(jìn)行排序,下面為示例程序


          @Data
          @AllArgsConstructor
          public class Student {
              private Integer id;
              private String name;

              @Override
              public String toString() {
                  return "{id=" + id + ", name='" + name + "'}";
              }
          }


          // 降序
          public class DescSortor implements Comparator<Student> {
              @Override
              public int compare(Student o1, Student o2) {
                  return o2.getId() - o1.getId();
              }
          }

          // 升序
          public class AscSortor implements Comparator<Student> {
              @Override
              public int compare(Student o1, Student o2) {
                  return o1.getId() - o2.getId();
              }
          }


          通過 Arrays.sort() 對數(shù)組進(jìn)行排序


          public class Test1 {
              public static void main(String[] args) {
                  Student[] students = {
                          new Student(3, "張三"),
                          new Student(1, "李四"),
                          new Student(4, "王五"),
                          new Student(2, "趙六")
                  };
                  toString(students, "排序前");
                  
                  Arrays.sort(students, new AscSortor());
                  toString(students, "升序后");
                  
                  Arrays.sort(students, new DescSortor());
                  toString(students, "降序后");
              }

              public static void toString(Student[] students, String desc){
                  for (int i = 0; i < students.length; i++) {
                      System.out.print(desc + ": " +students[i].toString() + ", ");
                  }
                  System.out.println();
              }
          }
          排序前: {id=3, name='張三'}, 排序前: {id=1, name='李四'}, 排序前: {id=4, name='王五'}, 排序前: {id=2, name='趙六'},
          升序后: {id=1, name='李四'}, 升序后: {id=2, name='趙六'}, 升序后: {id=3, name='張三'}, 升序后: {id=4, name='王五'},
          降序后: {id=4, name='王五'}, 降序后: {id=3, name='張三'}, 降序后: {id=2, name='趙六'}, 降序后: {id=1, name='李四'},


          通過 Collections.sort() 對集合List進(jìn)行排序


          public class Test2 {
              public static void main(String[] args) {
                  List<Student> students = Arrays.asList(
                          new Student(3, "張三"),
                          new Student(1, "李四"),
                          new Student(4, "王五"),
                          new Student(2, "趙六")
                  );
                  toString(students, "排序前");
                  
                  Collections.sort(students, new AscSortor());
                  toString(students, "升序后");

                  Collections.sort(students, new DescSortor());
                  toString(students, "降序后");
              }

              public static void toString(List<Student> students, String desc) {
                  for (Student student : students) {
                      System.out.print(desc + ": " + student.toString() + ", ");
                  }
                  System.out.println();
              }
          }
          排序前: {id=3, name='張三'}, 排序前: {id=1, name='李四'}, 排序前: {id=4, name='王五'}, 排序前: {id=2, name='趙六'},
          升序后: {id=1, name='李四'}, 升序后: {id=2, name='趙六'}, 升序后: {id=3, name='張三'}, 升序后: {id=4, name='王五'},
          降序后: {id=4, name='王五'}, 降序后: {id=3, name='張三'}, 降序后: {id=2, name='趙六'}, 降序后: {id=1, name='李四'},


          我們向 Collections.sort() 和 Arrays.sort() 分別傳入不同的比較器即可實現(xiàn)不同的排序效果(升序或降序)


          這里 Comparator 接口充當(dāng)了抽象策略角色,兩個比較器 DescSortor 和 AscSortor 則充當(dāng)了具體策略角色,Collections 和 Arrays 則是環(huán)境角色


          Spring Resource 中的策略模式


          Spring 把所有能記錄信息的載體,如各種類型的文件、二進(jìn)制流等都稱為資源,譬如最常用的Spring配置文件。


          在 Sun 所提供的標(biāo)準(zhǔn) API 里,資源訪問通常由 java.NET.URL 和文件 IO 來完成,尤其是當(dāng)我們需要訪問來自網(wǎng)絡(luò)的資源時,通常會選擇 URL 類。


          URL 類可以處理一些常規(guī)的資源訪問問題,但依然不能很好地滿足所有底層資源訪問的需要,比如,暫時還無法從類加載路徑、或相對于 ServletContext 的路徑來訪問資源,雖然 Java 允許使用特定的 URL 前綴注冊新的處理類(例如已有的 http: 前綴的處理類),但是這樣做通常比較復(fù)雜,而且 URL 接口還缺少一些有用的功能,比如檢查所指向的資源是否存在等。


          Spring 改進(jìn)了 Java 資源訪問的策略,Spring 為資源訪問提供了一個 Resource 接口,該接口提供了更強的資源訪問能力,Spring 框架本身大量使用了 Resource 接口來訪問底層資源。


          public interface Resource extends InputStreamSource {
              boolean exists(); // 返回 Resource 所指向的資源是否存在
              boolean isReadable(); // 資源內(nèi)容是否可讀
              boolean isOpen(); // 返回資源文件是否打開
              URL getURL() throws IOException;
              URI getURI() throws IOException;
              File getFile() throws IOException; // 返回資源對應(yīng)的 File 對象
              long contentLength() throws IOException;
              long lastModified() throws IOException;
              Resource createRelative(String var1) throws IOException;
              String getFilename();
              String getDescription(); // 返回資源的描述信息
          }


          Resource 接口是 Spring 資源訪問策略的抽象,它本身并不提供任何資源訪問實現(xiàn),具體的資源訪問由該接口的實現(xiàn)類完成——每個實現(xiàn)類代表一種資源訪問策略。

          Spring 為 Resource 接口提供的部分實現(xiàn)類如下:


          • UrlResource:訪問網(wǎng)絡(luò)資源的實現(xiàn)類。

          • ClassPathResource:訪問類加載路徑里資源的實現(xiàn)類。

          • FileSystemResource:訪問文件系統(tǒng)里資源的實現(xiàn)類。

          • ServletContextResource:訪問相對于 ServletContext 路徑里的資源的實現(xiàn)類:

          • InputStreamResource:訪問輸入流資源的實現(xiàn)類。

          • ByteArrayResource:訪問字節(jié)數(shù)組資源的實現(xiàn)類。

          • WritableResource:寫資源文件


          類圖如下:



          可以看到 AbstractResource 資源抽象類實現(xiàn)了 Resource 接口,為子類通用的操作提供了具體實現(xiàn),非通用的操作留給子類實現(xiàn),所以這里也應(yīng)用了模板方法模式。(只不過缺少了模板方法)


          Resource 不僅可在 Spring 的項目中使用,也可直接作為資源訪問的工具類使用。意思是說:即使不使用 Spring 框架,也可以使用 Resource 作為工具類,用來代替 URL。


          譬如我們可以使用 UrlResource 訪問網(wǎng)絡(luò)資源。


          也可以通過其它協(xié)議訪問資源,file: 用于訪問文件系統(tǒng);http: 用于通過 HTTP 協(xié)議訪問資源;ftp: 用于通過 FTP 協(xié)議訪問資源等。


          public class Test {
              public static void main(String[] args) throws IOException {
                  UrlResource ur = new UrlResource("http://image.laijianfeng.org/hello.txt");

                  System.out.println("文件名:" + ur.getFilename());
                  System.out.println("網(wǎng)絡(luò)文件URL:" + ur.getURL());
                  System.out.println("是否存在:" + ur.exists());
                  System.out.println("是否可讀:" + ur.isReadable());
                  System.out.println("文件長度:" + ur.contentLength());

                  System.out.println("\n--------文件內(nèi)容----------\n");
                  byte[] bytes = new byte[47];
                  ur.getInputStream().read(bytes);
                  System.out.println(new String(bytes));
              }
          }
          文件名:hello.txt
          網(wǎng)絡(luò)文件URL:http://image.laijianfeng.org/hello.txt
          是否存在:true
          是否可讀:true
          文件長度:47

          --------文件內(nèi)容----------

          hello world!
          welcome to http://laijianfeng.org


          Spring Bean 實例化中的策略模式


          Spring實例化Bean有三種方式:構(gòu)造器實例化、靜態(tài)工廠實例化、實例工廠實例化


          <?xml version="1.0" encoding="UTF-8"?>
          <beans xmlns="http://www.springframework.org/schema/beans"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


              <bean id="person" class="com.demo.Person"></bean>
              
              <bean id="personWithParam" class="com.demo.Person">
                  <constructor-arg name="name" value="小旋鋒"/>
              </bean>
              
              <bean id="personWirhParams" class="com.demo.Person">
                      <constructor-arg name="name" value="小旋鋒"/>
                      <constructor-arg name="age" value="22"/>
              </bean>
          </beans>


          具體實例化Bean的過程中,Spring中角色分工很明確,創(chuàng)建對象的時候先通過 ConstructorResolver 找到對應(yīng)的實例化方法和參數(shù),再通過實例化策略 InstantiationStrategy 進(jìn)行實例化,根據(jù)創(chuàng)建對象的三個分支( 工廠方法、有參構(gòu)造方法、無參構(gòu)造方法 ), InstantiationStrategy 提供了三個接口方法:


          public interface InstantiationStrategy {
            // 默認(rèn)構(gòu)造方法
            Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) throws BeansException;

            // 指定構(gòu)造方法
            Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner, Constructor<?> ctor,
                Object[] args) throws BeansException;

            // 指定工廠方法
            Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner, Object factoryBean,
                Method factoryMethod, Object[] args) throws BeansException;
          }


          InstantiationStrategy 為實例化策略接口,扮演抽象策略角色,有兩種具體策略類,分別為 SimpleInstantiationStrategy 和 CglibSubclassingInstantiationStrategy。



          在 SimpleInstantiationStrategy 中對這三個方法做了簡單實現(xiàn),如果工廠方法實例化直接用反射創(chuàng)建對象,如果是構(gòu)造方法實例化的則判斷是否有 MethodOverrides,如果有無 MethodOverrides 也是直接用反射,如果有 MethodOverrides 就需要用 cglib 實例化對象,SimpleInstantiationStrategy 把通過 cglib 實例化的任務(wù)交給了它的子類 CglibSubclassingInstantiationStrategy。



          出處:blog.csdn.net/weixin_44991304/article/details/117355320









          關(guān)注GitHub今日熱榜,專注挖掘好用的開發(fā)工具,致力于分享優(yōu)質(zhì)高效的工具、資源、插件等,助力開發(fā)者成長!








          點個在看,你最好


          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99视频网 | 视频在线三区 | 东京热AV无码国产东京热AⅤ | 中国一级特黄大片学生 | 免费视频99 |