国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【設(shè)計(jì)模式與范式:行為型】61 | 策略模式(下):如何實(shí)現(xiàn)一個(gè)支持給不同大小文件排序的小程序?

這篇具有很好參考價(jià)值的文章主要介紹了【設(shè)計(jì)模式與范式:行為型】61 | 策略模式(下):如何實(shí)現(xiàn)一個(gè)支持給不同大小文件排序的小程序?。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

上一節(jié)課,我們主要介紹了策略模式的原理和實(shí)現(xiàn),以及如何利用策略模式來移除 if-else 或者 switch-case 分支判斷邏輯。今天,我們結(jié)合“給文件排序”這樣一個(gè)具體的例子,來詳細(xì)講一講策略模式的設(shè)計(jì)意圖和應(yīng)用場(chǎng)景。

除此之外,在今天的講解中,我還會(huì)通過一步一步地分析、重構(gòu),給你展示一個(gè)設(shè)計(jì)模式是如何“創(chuàng)造”出來的。通過今天的學(xué)習(xí),你會(huì)發(fā)現(xiàn),設(shè)計(jì)原則和思想其實(shí)比設(shè)計(jì)模式更加普適和重要,掌握了代碼的設(shè)計(jì)原則和思想,我們甚至可以自己創(chuàng)造出來新的設(shè)計(jì)模式。

話不多說,讓我們正式開始今天的學(xué)習(xí)吧!

問題與解決思路

假設(shè)有這樣一個(gè)需求,希望寫一個(gè)小程序,實(shí)現(xiàn)對(duì)一個(gè)文件進(jìn)行排序的功能。文件中只包含整型數(shù),并且,相鄰的數(shù)字通過逗號(hào)來區(qū)隔。如果由你來編寫這樣一個(gè)小程序,你會(huì)如何來實(shí)現(xiàn)呢?你可以把它當(dāng)作面試題,先自己思考一下,再來看我下面的講解。

你可能會(huì)說,這不是很簡(jiǎn)單嘛,只需要將文件中的內(nèi)容讀取出來,并且通過逗號(hào)分割成一個(gè)一個(gè)的數(shù)字,放到內(nèi)存數(shù)組中,然后編寫某種排序算法(比如快排),或者直接使用編程語言提供的排序函數(shù),對(duì)數(shù)組進(jìn)行排序,最后再將數(shù)組中的數(shù)據(jù)寫入文件就可以了。

但是,如果文件很大呢?比如有 10GB 大小,因?yàn)閮?nèi)存有限(比如只有 8GB 大小),我們沒辦法一次性加載文件中的所有數(shù)據(jù)到內(nèi)存中,這個(gè)時(shí)候,我們就要利用外部排序算法(具體怎么做,可以參看我的另一個(gè)專欄《數(shù)據(jù)結(jié)構(gòu)與算法之美》中的“排序”相關(guān)章節(jié))了。

如果文件更大,比如有 100GB 大小,我們?yōu)榱死?CPU 多核的優(yōu)勢(shì),可以在外部排序的基礎(chǔ)之上進(jìn)行優(yōu)化,加入多線程并發(fā)排序的功能,這就有點(diǎn)類似“單機(jī)版”的 MapReduce。

如果文件非常大,比如有 1TB 大小,即便是單機(jī)多線程排序,這也算很慢了。這個(gè)時(shí)候,我們可以使用真正的 MapReduce 框架,利用多機(jī)的處理能力,提高排序的效率。

代碼實(shí)現(xiàn)與分析

解決思路講完了,不難理解。接下來,我們看一下,如何將解決思路翻譯成代碼實(shí)現(xiàn)。
我先用最簡(jiǎn)單直接的方式實(shí)現(xiàn)將它實(shí)現(xiàn)出來。具體代碼我貼在下面了,你可以先看一下。因?yàn)槲覀兪窃谥v設(shè)計(jì)模式,不是講算法,所以,在下面的代碼實(shí)現(xiàn)中,我只給出了跟設(shè)計(jì)模式相關(guān)的骨架代碼,并沒有給出每種排序算法的具體代碼實(shí)現(xiàn)。感興趣的話,你可以自行實(shí)現(xiàn)一下。

public class Sorter {
  private static final long GB = 1000 * 1000 * 1000;
  public void sortFile(String filePath) {
    // 省略校驗(yàn)邏輯
    File file = new File(filePath);
    long fileSize = file.length();
    if (fileSize < 6 * GB) { // [0, 6GB)
      quickSort(filePath);
    } else if (fileSize < 10 * GB) { // [6GB, 10GB)
      externalSort(filePath);
    } else if (fileSize < 100 * GB) { // [10GB, 100GB)
      concurrentExternalSort(filePath);
    } else { // [100GB, ~)
      mapreduceSort(filePath);
    }
  }
  private void quickSort(String filePath) {
    // 快速排序
  }
  private void externalSort(String filePath) {
    // 外部排序
  }
  private void concurrentExternalSort(String filePath) {
    // 多線程外部排序
  }
  private void mapreduceSort(String filePath) {
    // 利用MapReduce多機(jī)排序
  }
}
public class SortingTool {
  public static void main(String[] args) {
    Sorter sorter = new Sorter();
    sorter.sortFile(args[0]);
  }
}

在“編碼規(guī)范”那一部分我們講過,函數(shù)的行數(shù)不能過多,最好不要超過一屏的大小。所以,為了避免 sortFile() 函數(shù)過長,我們把每種排序算法從 sortFile() 函數(shù)中抽離出來,拆分成 4 個(gè)獨(dú)立的排序函數(shù)。

如果只是開發(fā)一個(gè)簡(jiǎn)單的工具,那上面的代碼實(shí)現(xiàn)就足夠了。畢竟,代碼不多,后續(xù)修改、擴(kuò)展的需求也不多,怎么寫都不會(huì)導(dǎo)致代碼不可維護(hù)。但是,如果我們是在開發(fā)一個(gè)大型項(xiàng)目,排序文件只是其中的一個(gè)功能模塊,那我們就要在代碼設(shè)計(jì)、代碼質(zhì)量上下點(diǎn)兒功夫了。只有每個(gè)小的功能模塊都寫好,整個(gè)項(xiàng)目的代碼才能不差。

在剛剛的代碼中,我們并沒有給出每種排序算法的代碼實(shí)現(xiàn)。實(shí)際上,如果自己實(shí)現(xiàn)一下的話,你會(huì)發(fā)現(xiàn),每種排序算法的實(shí)現(xiàn)邏輯都比較復(fù)雜,代碼行數(shù)都比較多。所有排序算法的代碼實(shí)現(xiàn)都堆在 Sorter 一個(gè)類中,這就會(huì)導(dǎo)致這個(gè)類的代碼很多。而在“編碼規(guī)范”那一部分中,我們也講到,一個(gè)類的代碼太多也會(huì)影響到可讀性、可維護(hù)性。除此之外,所有的排序算法都設(shè)計(jì)成 Sorter 的私有函數(shù),也會(huì)影響代碼的可復(fù)用性。

代碼優(yōu)化與重構(gòu)

只要掌握了我們之前講過的設(shè)計(jì)原則和思想,針對(duì)上面的問題,即便我們想不到該用什么設(shè)計(jì)模式來重構(gòu),也應(yīng)該能知道該如何解決,那就是將 Sorter 類中的某些代碼拆分出來,獨(dú)立成職責(zé)更加單一的小類。實(shí)際上,拆分是應(yīng)對(duì)類或者函數(shù)代碼過多、應(yīng)對(duì)代碼復(fù)雜性的一個(gè)常用手段。按照這個(gè)解決思路,我們對(duì)代碼進(jìn)行重構(gòu)。重構(gòu)之后的代碼如下所示:

public interface ISortAlg {
  void sort(String filePath);
}
public class QuickSort implements ISortAlg {
  @Override
  public void sort(String filePath) {
    //...
  }
}
public class ExternalSort implements ISortAlg {
  @Override
  public void sort(String filePath) {
    //...
  }
}
public class ConcurrentExternalSort implements ISortAlg {
  @Override
  public void sort(String filePath) {
    //...
  }
}
public class MapReduceSort implements ISortAlg {
  @Override
  public void sort(String filePath) {
    //...
  }
}
public class Sorter {
  private static final long GB = 1000 * 1000 * 1000;
  public void sortFile(String filePath) {
    // 省略校驗(yàn)邏輯
    File file = new File(filePath);
    long fileSize = file.length();
    ISortAlg sortAlg;
    if (fileSize < 6 * GB) { // [0, 6GB)
      sortAlg = new QuickSort();
    } else if (fileSize < 10 * GB) { // [6GB, 10GB)
      sortAlg = new ExternalSort();
    } else if (fileSize < 100 * GB) { // [10GB, 100GB)
      sortAlg = new ConcurrentExternalSort();
    } else { // [100GB, ~)
      sortAlg = new MapReduceSort();
    }
    sortAlg.sort(filePath);
  }
}

經(jīng)過拆分之后,每個(gè)類的代碼都不會(huì)太多,每個(gè)類的邏輯都不會(huì)太復(fù)雜,代碼的可讀性、可維護(hù)性提高了。除此之外,我們將排序算法設(shè)計(jì)成獨(dú)立的類,跟具體的業(yè)務(wù)邏輯(代碼中的 if-else 那部分邏輯)解耦,也讓排序算法能夠復(fù)用。這一步實(shí)際上就是策略模式的第一步,也就是將策略的定義分離出來。

實(shí)際上,上面的代碼還可以繼續(xù)優(yōu)化。每種排序類都是無狀態(tài)的,我們沒必要在每次使用的時(shí)候,都重新創(chuàng)建一個(gè)新的對(duì)象。所以,我們可以使用工廠模式對(duì)對(duì)象的創(chuàng)建進(jìn)行封裝。按照這個(gè)思路,我們對(duì)代碼進(jìn)行重構(gòu)。重構(gòu)之后的代碼如下所示:

public class SortAlgFactory {
  private static final Map<String, ISortAlg> algs = new HashMap<>();
  static {
    algs.put("QuickSort", new QuickSort());
    algs.put("ExternalSort", new ExternalSort());
    algs.put("ConcurrentExternalSort", new ConcurrentExternalSort());
    algs.put("MapReduceSort", new MapReduceSort());
  }
  public static ISortAlg getSortAlg(String type) {
    if (type == null || type.isEmpty()) {
      throw new IllegalArgumentException("type should not be empty.");
    }
    return algs.get(type);
  }
}
public class Sorter {
  private static final long GB = 1000 * 1000 * 1000;
  public void sortFile(String filePath) {
    // 省略校驗(yàn)邏輯
    File file = new File(filePath);
    long fileSize = file.length();
    ISortAlg sortAlg;
    if (fileSize < 6 * GB) { // [0, 6GB)
      sortAlg = SortAlgFactory.getSortAlg("QuickSort");
    } else if (fileSize < 10 * GB) { // [6GB, 10GB)
      sortAlg = SortAlgFactory.getSortAlg("ExternalSort");
    } else if (fileSize < 100 * GB) { // [10GB, 100GB)
      sortAlg = SortAlgFactory.getSortAlg("ConcurrentExternalSort");
    } else { // [100GB, ~)
      sortAlg = SortAlgFactory.getSortAlg("MapReduceSort");
    }
    sortAlg.sort(filePath);
  }
}

經(jīng)過上面兩次重構(gòu)之后,現(xiàn)在的代碼實(shí)際上已經(jīng)符合策略模式的代碼結(jié)構(gòu)了。我們通過策略模式將策略的定義、創(chuàng)建、使用解耦,讓每一部分都不至于太復(fù)雜。不過,Sorter 類中的 sortFile() 函數(shù)還是有一堆 if-else 邏輯。這里的 if-else 邏輯分支不多、也不復(fù)雜,這樣寫完全沒問題。但如果你特別想將 if-else 分支判斷移除掉,那也是有辦法的。我直接給出代碼,你一看就能明白。實(shí)際上,這也是基于查表法來解決的,其中的“algs”就是“表”。

public class Sorter {
  private static final long GB = 1000 * 1000 * 1000;
  private static final List<AlgRange> algs = new ArrayList<>();
  static {
    algs.add(new AlgRange(0, 6*GB, SortAlgFactory.getSortAlg("QuickSort")));
    algs.add(new AlgRange(6*GB, 10*GB, SortAlgFactory.getSortAlg("ExternalSort")));
    algs.add(new AlgRange(10*GB, 100*GB, SortAlgFactory.getSortAlg("ConcurrentExternalSort")));
    algs.add(new AlgRange(100*GB, Long.MAX_VALUE, SortAlgFactory.getSortAlg("MapReduceSort")));
  }
  public void sortFile(String filePath) {
    // 省略校驗(yàn)邏輯
    File file = new File(filePath);
    long fileSize = file.length();
    ISortAlg sortAlg = null;
    for (AlgRange algRange : algs) {
      if (algRange.inRange(fileSize)) {
        sortAlg = algRange.getAlg();
        break;
      }
    }
    sortAlg.sort(filePath);
  }
  private static class AlgRange {
    private long start;
    private long end;
    private ISortAlg alg;
    public AlgRange(long start, long end, ISortAlg alg) {
      this.start = start;
      this.end = end;
      this.alg = alg;
    }
    public ISortAlg getAlg() {
      return alg;
    }
    public boolean inRange(long size) {
      return size >= start && size < end;
    }
  }
}

現(xiàn)在的代碼實(shí)現(xiàn)就更加優(yōu)美了。我們把可變的部分隔離到了策略工廠類和 Sorter 類中的靜態(tài)代碼段中。當(dāng)要添加一個(gè)新的排序算法時(shí),我們只需要修改策略工廠類和 Sort 類中的靜態(tài)代碼段,其他代碼都不需要修改,這樣就將代碼改動(dòng)最小化、集中化了。
你可能會(huì)說,即便這樣,當(dāng)我們添加新的排序算法的時(shí)候,還是需要修改代碼,并不完全符合開閉原則。有什么辦法讓我們完全滿足開閉原則呢?

對(duì)于 Java 語言來說,我們可以通過反射來避免對(duì)策略工廠類的修改。具體是這么做的:我們通過一個(gè)配置文件或者自定義的 annotation 來標(biāo)注都有哪些策略類;策略工廠類讀取配置文件或者搜索被 annotation 標(biāo)注的策略類,然后通過反射了動(dòng)態(tài)地加載這些策略類、創(chuàng)建策略對(duì)象;當(dāng)我們新添加一個(gè)策略的時(shí)候,只需要將這個(gè)新添加的策略類添加到配置文件或者用 annotation 標(biāo)注即可。還記得上一節(jié)課的課堂討論題嗎?我們也可以用這種方法來解決。

對(duì)于 Sorter 來說,我們可以通過同樣的方法來避免修改。我們通過將文件大小區(qū)間和算法之間的對(duì)應(yīng)關(guān)系放到配置文件中。當(dāng)添加新的排序算法時(shí),我們只需要改動(dòng)配置文件即可,不需要改動(dòng)代碼。

重點(diǎn)回顧

好了,今天的內(nèi)容到此就講完了。我們一塊來總結(jié)回顧一下,你需要重點(diǎn)掌握的內(nèi)容。

一提到 if-else 分支判斷,有人就覺得它是爛代碼。如果 if-else 分支判斷不復(fù)雜、代碼不多,這并沒有任何問題,畢竟 if-else 分支判斷幾乎是所有編程語言都會(huì)提供的語法,存在即有理由。遵循 KISS 原則,怎么簡(jiǎn)單怎么來,就是最好的設(shè)計(jì)。非得用策略模式,搞出 n 多類,反倒是一種過度設(shè)計(jì)。

一提到策略模式,有人就覺得,它的作用是避免 if-else 分支判斷邏輯。實(shí)際上,這種認(rèn)識(shí)是很片面的。策略模式主要的作用還是解耦策略的定義、創(chuàng)建和使用,控制代碼的復(fù)雜度,讓每個(gè)部分都不至于過于復(fù)雜、代碼量過多。除此之外,對(duì)于復(fù)雜代碼來說,策略模式還能讓其滿足開閉原則,添加新策略的時(shí)候,最小化、集中化代碼改動(dòng),減少引入 bug 的風(fēng)險(xiǎn)。

實(shí)際上,設(shè)計(jì)原則和思想比設(shè)計(jì)模式更加普適和重要。掌握了代碼的設(shè)計(jì)原則和思想,我們能更清楚的了解,為什么要用某種設(shè)計(jì)模式,就能更恰到好處地應(yīng)用設(shè)計(jì)模式。文章來源地址http://www.zghlxwxcb.cn/news/detail-497133.html

課堂討論

  • 在過去的項(xiàng)目開發(fā)中,你有沒有用過策略模式,都是為了解決什么問題才使用的?
  • 你可以說一說,在什么情況下,我們才有必要去掉代碼中的 if-else 或者 switch-case 分支邏輯呢?

到了這里,關(guān)于【設(shè)計(jì)模式與范式:行為型】61 | 策略模式(下):如何實(shí)現(xiàn)一個(gè)支持給不同大小文件排序的小程序?的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • [設(shè)計(jì)模式]行為型模式-策略模式

    策略模式定義了一系列算法,并將每個(gè)算法封裝起來,使它們可以互相替換,且算法的變換不會(huì)影響使用算法的客戶。 在項(xiàng)目開發(fā)中,我們經(jīng)常要根據(jù)不同的場(chǎng)景,采取不同的措施,也就是不同的策略。假設(shè)我們需要對(duì)a、b這兩個(gè)整數(shù)進(jìn)行計(jì)算,根據(jù)條件的不同,需要執(zhí)行不

    2024年02月19日
    瀏覽(32)
  • 【設(shè)計(jì)模式 行為型】策略模式

    它允許在運(yùn)行時(shí)根據(jù)需要選擇算法的行為。該模式通過將算法封裝成獨(dú)立的類,使得它們可以相互替換,而不影響使用算法的客戶端代碼。 策略模式主要包含以下角色: 環(huán)境(Context):環(huán)境對(duì)象持有一個(gè)策略對(duì)象的引用,它提供了一個(gè)接口用于執(zhí)行具體的算法。 抽象策略(

    2024年01月20日
    瀏覽(24)
  • 【設(shè)計(jì)模式--行為型--策略模式】

    【設(shè)計(jì)模式--行為型--策略模式】

    定義 該模式定義了一系列算法,并將每個(gè)算法封裝起來,使他們可以相互替換,且算法的變化不會(huì)影響使用算法的客戶。策略模式屬于對(duì)象行為模式,它通過對(duì)算法進(jìn)行封裝,把使用算法的責(zé)任和算法的實(shí)現(xiàn)分隔開,并委派給不同的對(duì)象對(duì)這些算法進(jìn)行管理。 結(jié)構(gòu) 抽象策略

    2024年02月04日
    瀏覽(55)
  • 行為型設(shè)計(jì)模式——策略模式

    行為型設(shè)計(jì)模式——策略模式

    策略模式非常簡(jiǎn)單,只需要將策略或者某個(gè)算法定義成一個(gè)類,然后傳給需要使用的對(duì)象即可。**定義:**該模式定義了一系列算法,并將每個(gè)算法封裝起來,使它們可以相互替換,且算法的變化不會(huì)影響使用算法的客戶。策略模式屬于對(duì)象行為模式,它通過對(duì)算法進(jìn)行封裝,

    2024年01月23日
    瀏覽(25)
  • 【設(shè)計(jì)模式與范式:行為型】58 | 模板模式(上):剖析模板模式在JDK、Servlet、JUnit等中的應(yīng)用

    上兩節(jié)課我們學(xué)習(xí)了第一個(gè)行為型設(shè)計(jì)模式,觀察者模式。針對(duì)不同的應(yīng)用場(chǎng)景,我們講解了不同的實(shí)現(xiàn)方式,有同步阻塞、異步非阻塞的實(shí)現(xiàn)方式,也有進(jìn)程內(nèi)、進(jìn)程間的實(shí)現(xiàn)方式。除此之外,我還帶你手把手實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的 EventBus 框架。 今天,我們?cè)賹W(xué)習(xí)另外一種行

    2024年02月09日
    瀏覽(21)
  • 【設(shè)計(jì)模式與范式:行為型】69 | 訪問者模式(下):為什么支持雙分派的語言不需要訪問者模式?

    上一節(jié)課中,我們學(xué)習(xí)了訪問者模式的原理和實(shí)現(xiàn),并且還原了訪問者模式誕生的思維過程??傮w上來講,這個(gè)模式的代碼實(shí)現(xiàn)比較難,所以應(yīng)用場(chǎng)景并不多。從應(yīng)用開發(fā)的角度來說,它的確不是我們學(xué)習(xí)的重點(diǎn)。 不過,我們前面反復(fù)說過,學(xué)習(xí)我的專欄,并不只是讓你掌握

    2024年02月10日
    瀏覽(23)
  • 設(shè)計(jì)模式(十四):行為型之策略模式

    設(shè)計(jì)模式(十四):行為型之策略模式

    設(shè)計(jì)模式系列文章 設(shè)計(jì)模式(一):創(chuàng)建型之單例模式 設(shè)計(jì)模式(二、三):創(chuàng)建型之工廠方法和抽象工廠模式 設(shè)計(jì)模式(四):創(chuàng)建型之原型模式 設(shè)計(jì)模式(五):創(chuàng)建型之建造者模式 設(shè)計(jì)模式(六):結(jié)構(gòu)型之代理模式 設(shè)計(jì)模式(七):結(jié)構(gòu)型之適配器模式 設(shè)計(jì)模式(八):結(jié)構(gòu)型之裝

    2024年02月09日
    瀏覽(26)
  • 設(shè)計(jì)模式(4)--對(duì)象行為(9)--策略

    設(shè)計(jì)模式(4)--對(duì)象行為(9)--策略

    1. 意圖 ? ? 定義一系列的算法,把它們一個(gè)個(gè)封裝起來,并且使它們可相互替換。 ? ? 本模式使得算法可獨(dú)立于使用它的客戶而變化。 2. 三種角色 ? ?抽象策略(Strategy)、具體策略(Concrete Strategy)、上下文環(huán)境(Context) 3. 優(yōu)點(diǎn) ? ? 3.1 可重用的相關(guān)算法系列。 ? ? 3.2 一個(gè)替代繼

    2024年02月04日
    瀏覽(21)
  • 軟件設(shè)計(jì)模式與體系結(jié)構(gòu)-設(shè)計(jì)模式-行為型軟件設(shè)計(jì)模式-策略模式

    軟件設(shè)計(jì)模式與體系結(jié)構(gòu)-設(shè)計(jì)模式-行為型軟件設(shè)計(jì)模式-策略模式

    策略模式(Strategy Pattern)是一種行為型設(shè)計(jì)模式,它定義了一系列算法,并將每個(gè)算法封裝在獨(dú)立的類中,使它們可以相互替換。策略模式使得算法可以獨(dú)立于使用它們的客戶端而變化。 下面是一個(gè)使用策略模式的簡(jiǎn)單代碼示例,以解釋其工作原理: 在上述示例中,策略模

    2024年02月13日
    瀏覽(26)
  • 【面試題】如何理解 前端設(shè)計(jì)模式-測(cè)策略模式?

    前端面試題庫 ( 面試必備) ?? ? ? ? ? ? 推薦:★★★★★ 地址:前端面試題庫 【國慶頭像】- 國慶愛國 程序員頭像!總有一款適合你! 策略(Strategy)模式的定義:該模式定義了一 系列算法 ,并將每個(gè)算法封裝起來,使他們可以相互替換,且算法的變化不會(huì)影響使用

    2024年02月07日
    瀏覽(27)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包