欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java函數(shù)式編程之通過(guò)行為參數(shù)化傳遞代碼

 更新時(shí)間:2023年08月27日 08:37:14   作者:jack_xu  
行為參數(shù)化就是可以幫助你處理頻繁變更的需求的一種軟件開(kāi)發(fā)模式,這篇文章將給大家詳細(xì)的介紹一下Java函數(shù)式編程之行為參數(shù)化傳遞代碼,感興趣的同學(xué)可以參考閱讀下

不斷變化的需求

  • 在軟件工程中,一個(gè)眾所周知的問(wèn)題就是,不管你做什么,用戶(hù)的需求肯定會(huì)變

  • 比如之前的蘋(píng)果的例子

    • 找綠色蘋(píng)果
    • 找紅色蘋(píng)果
    • 找大于150G的蘋(píng)果
    • 找大于150G的綠蘋(píng)果
    • 找大于150G的紅蘋(píng)果
    • 找大于150G且小于400G的紅蘋(píng)果
  • 按照上述經(jīng)常變動(dòng),且都需要的,那要寫(xiě)多少方法?

  • 我們將上述的“需求”看做是一種行為。那在進(jìn)行處理的時(shí)候,只需要傳遞這種“行為”,那么所有的行為方法都不需要寫(xiě)了,簡(jiǎn)直一勞永逸。

  • 我們稱(chēng)之為行為,而傳遞的行為叫做行為化參數(shù)。行為參數(shù)化就是可以幫助你處理頻繁變更的需求的一種軟件開(kāi)發(fā)模式。

  • 一言以蔽之,它意味著拿出一個(gè)代碼塊,把它準(zhǔn)備好卻不去執(zhí)行它。這個(gè)代碼塊以后可以被你程序的其他部分調(diào)用,這意味著你可以推遲這塊代碼的執(zhí)行。例如,你可以將代碼塊作為參數(shù)傳遞給另一個(gè)方法,稍后再去執(zhí)行它。這樣,這個(gè)方法的行為就基于那塊代碼被參數(shù)化了。

  • 假如你要處理一個(gè)集合,會(huì)寫(xiě)這樣的一個(gè)方法

    • 可以對(duì)列表中的每個(gè)元素做“某件事”
    • 可以在列表處理完后做“另一件事”
    • 遇到錯(cuò)誤時(shí)可以做“另外一件事”
  • 這就是行為化,如果還不理解,繼續(xù)往下看。

  • 打個(gè)比方吧:你的室友知道怎么開(kāi)車(chē)去超市,再開(kāi)回家。于是你可以告訴他去買(mǎi)一些東西,比如面包、奶酪、葡萄酒什么的。這相當(dāng)于調(diào)用一個(gè)goAndBuy方法,把購(gòu)物單作為參數(shù)。然而,有一天你在上班,你需要他去做一件他從來(lái)沒(méi)有做過(guò)的事情:從郵局取一個(gè)包裹。現(xiàn)在你就需要傳遞給他一系列指示了:去郵局,使用單號(hào),和工作人員說(shuō)明情況,取走包裹。你可以把這些指示用電子郵件發(fā)給他,當(dāng)他收到之后就可以按照指示行事了。你現(xiàn)在做的事情就更高級(jí)一些了,相當(dāng)于一個(gè)方法:go,它可以接受不同的新行為作為參數(shù),然后去執(zhí)行。

應(yīng)對(duì)不斷變化的需求

  • 這里會(huì)使用一個(gè)案例,并且逐步改善
  • 篩選綠蘋(píng)果
public static List<Apple> filterGreenApples(List<Apple> apples) {
    List<Apple> arrayList = new ArrayList<>();
    for (Apple apple : apples) {
        // 篩選
        if ("green".equals(apple.getColor())) {
            arrayList.add(apple);
        }
    }
    return arrayList;
}
  • 現(xiàn)在做的就是篩選綠蘋(píng)果的,現(xiàn)在需求變更,需要篩選紅蘋(píng)果,那還要把這個(gè)方法拷貝一下,把Green換成Red。很明顯,違反了DRY(Do not Repeat Youself)
  • 怎么做呢?把顏色作為參數(shù)
  • 因?yàn)橹皇歉淖冾伾?,所以把顏色傳遞進(jìn)去,可以省去一個(gè)方法
public static List<Apple> filterApplesByColor(List<Apple> apples, String color) {
    List<Apple> arrayList = new ArrayList<>();
    for (Apple apple : apples) {
        if (color != null && color.equals(apple.getColor())) {
            arrayList.add(apple);
        }
    }
    return arrayList;
}
  • 上述已經(jīng)完成了操作,此時(shí)又有新的需求:要區(qū)分重的蘋(píng)果和輕的蘋(píng)果,同理
public static List<Apple> filterApplesByWeight(List<Apple> apples, int Weight) {
    List<Apple> arrayList = new ArrayList<>();
    for (Apple apple : apples) {
        if (apple.getWeight() > Weight) {
            arrayList.add(apple);
        }
    }
    return arrayList;
}
  • 但是還是復(fù)制了代碼,對(duì)整個(gè)輸出行為有影響的只有那一行判斷條件
  • 再次嘗試:對(duì)你能所想到的每個(gè)屬性進(jìn)行篩選
public static List<Apple> filterApples(List<Apple> apples, int weight, String color, boolean flag) {
    List<Apple> arrayList = new ArrayList<>();
    for (Apple apple : apples) {
        if ((flag && apple.getWeight() > weight) || (!flag && color != null && color.equals(apple.getColor()))) {
            arrayList.add(apple);
        }
    }
    return arrayList;
}
  • 這種代碼已經(jīng)臟的不行了,傳遞weight、color、flag,用flag判斷到底哪一個(gè)屬性生效,那么可以遇見(jiàn)的是,如果Apple增加了其他的字段:甜度、水分、生產(chǎn)地......
  • 那么這個(gè)方法已經(jīng)無(wú)法維護(hù)了,如果需要更加復(fù)雜的查詢(xún),也無(wú)法編寫(xiě)。而且,向一個(gè)方法傳遞一個(gè)boolean是非常危險(xiǎn)的事情,有boolean就意味著有分支。

行為參數(shù)化

  • 經(jīng)過(guò)上面的案例,我們需要一種更好的方式來(lái)應(yīng)對(duì)變化的需求。

  • 現(xiàn)在對(duì)上述的需求進(jìn)行建模之前,先看一下可能的需求案例

    • 找綠色蘋(píng)果
    • 找紅色蘋(píng)果
    • 找大于150G的蘋(píng)果
    • 找大于150G的綠蘋(píng)果
    • 找大于150G的紅蘋(píng)果
    • 找大于150G且小于400G的紅蘋(píng)果
  • 這是上文提到的,我們將所有的過(guò)濾條件去掉,那么這些需求案例,就是 找滿(mǎn)足xxx屬性的蘋(píng)果

  • 那么建模如下:你考慮的是蘋(píng)果,需要根據(jù)Apple的某些屬性,返回一個(gè)滿(mǎn)足條件的boolean值。

  • 我們稱(chēng)之為謂詞,定義接口如下

public interface ApplePredicate {
    boolean test(Apple apple);
}
  • 然后可以使用ApplePredicate的多個(gè)實(shí)現(xiàn)來(lái)執(zhí)行不同的行為了
class AppleHeavyWeightPredicate implements ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return apple.getWeight() > 150;
    }
}
class AppleRedPredicate implements ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return "red".equals(apple.getColor());
    }
}
  • 這些實(shí)現(xiàn),就是不同的篩選條件(filter),也相當(dāng)于不同的策略,算法族就是 ApplePredicate
  • 但是怎么利用這種不同的實(shí)現(xiàn)呢?需要filterApples方法接受ApplePredicate對(duì)象,對(duì)Apple做條件測(cè)試。
  • 這就是行為參數(shù)化:讓方法接受多種行為(或戰(zhàn)略)作為參數(shù),并在內(nèi)部使用,來(lái)完成不同的行為。
  • 那么現(xiàn)在就來(lái)修改之前的代碼
public class Test5 {
    public static void main(String[] args) {
        System.out.println(filterApples(AppleClient.getApples(), new AppleHeavyWeightPredicate()));
        System.out.println(filterApples(AppleClient.getApples(), new AppleRedPredicate()));
    }
    public static List<Apple> filterApples(List<Apple> apples, ApplePredicate applePredicate) {
        List<Apple> arrayList = new ArrayList<>();
        for (Apple apple : apples) {
            if (applePredicate.test(apple)) {
                arrayList.add(apple);
            }
        }
        return arrayList;
    }
}
  • 現(xiàn)在你把filterApples方法迭代集合的邏輯與你要應(yīng)用到集合中每個(gè)元素的行為(這里是一個(gè)謂詞)區(qū)分開(kāi)了。
  • 這樣一來(lái),任何需求的變更,只需要增加相應(yīng)的實(shí)現(xiàn)類(lèi)即可。filterApples方法的行為取決于你通過(guò)ApplePredicate對(duì)象傳遞的代碼。但是會(huì)有一個(gè)問(wèn)題:類(lèi)膨脹
  • 代碼傳遞/行為
  • 在上述的實(shí)現(xiàn)中,我們發(fā)現(xiàn),對(duì)于整個(gè)測(cè)試,唯一重要的就是test方法的實(shí)現(xiàn),這個(gè)方法決定了需要怎么樣進(jìn)行過(guò)濾。
  • 所以在傳遞行為的時(shí)候,我們可以直接使用匿名內(nèi)部類(lèi)來(lái)傳遞,如下
public class Test5 {
    public static void main(String[] args) {
        System.out.println(filterApples(AppleClient.getApples(), new ApplePredicate() {
            @Override
            public boolean test(Apple apple) {
                return apple.getWeight() > 150;
            }
        }));
        System.out.println(filterApples(AppleClient.getApples(), new ApplePredicate() {
            @Override
            public boolean test(Apple apple) {
                return "red".equals(apple.getColor());
            }
        }));
    }
    public static List<Apple> filterApples(List<Apple> apples, ApplePredicate applePredicate) {
        List<Apple> arrayList = new ArrayList<>();
        for (Apple apple : apples) {
            if (applePredicate.test(apple)) {
                arrayList.add(apple);
            }
        }
        return arrayList;
    }
}
  • 但是多個(gè)很多無(wú)用的代碼,此時(shí)再將匿名內(nèi)部類(lèi)更改為L(zhǎng)ambda表達(dá)式即可
public class Test5 {
    public static void main(String[] args) {
        System.out.println(filterApples(AppleClient.getApples(), apple -> apple.getWeight() > 150));
        System.out.println(filterApples(AppleClient.getApples(), apple -> "red".equals(apple.getColor())));
    }
    public static List<Apple> filterApples(List<Apple> apples, ApplePredicate applePredicate) {
        List<Apple> arrayList = new ArrayList<>();
        for (Apple apple : apples) {
            if (applePredicate.test(apple)) {
                arrayList.add(apple);
            }
        }
        return arrayList;
    }
}
  • 如上,你只需要關(guān)注行為,也就是你需要test的實(shí)現(xiàn)即可。
  • 多種行為/一個(gè)參數(shù)
  • 正如我們先前解釋的那樣,行為參數(shù)化的好處在于你可以把迭代要篩選的集合的邏輯與對(duì)集合中每個(gè)元素應(yīng)用的行為區(qū)分開(kāi)來(lái)。這樣你可以重復(fù)使用同一個(gè)方法,給它不同的行為來(lái)達(dá)到不同的目的
  • 新需求,對(duì)蘋(píng)果進(jìn)行遍歷,然后對(duì)其進(jìn)行格式化輸出
  • 這個(gè)需求需要定義一個(gè)新的方法,prettyPrintApple,參照上面的篩選蘋(píng)果的案例,整體的執(zhí)行框架如下
public static void prettyPrintApple(List<Apple> apples,???){
    for (Apple apple : apples) {
        String output = ???.???(apple);
        System.out.println(output);
    }
}
  • 其中???的部分就是我們要填充的行為,其比較簡(jiǎn)單:輸入一個(gè)Apple,然后輸出一個(gè)String,那么就來(lái)定義這樣的一個(gè)接口 FormatApple
public interface AppleFormat {
    String accept(Apple apple);
}
  • 那么 prettyPrintApple 就可以實(shí)現(xiàn)了,如下
public static void prettyPrintApple(List<Apple> apples,AppleFormat appleFormat){
    for (Apple apple : apples) {
        String output = appleFormat.accept(apple);
        System.out.println(output);
    }
}
  • 這樣就可以表示多種行為了
public class Test6 {
    public static void main(String[] args) {
        prettyPrintApple(AppleClient.getApples(), apple -> {
            return "顏色:" + apple.getColor() + ",重量:" + apple.getWeight();
        });
        prettyPrintApple(AppleClient.getApples(), apple -> {
            return "顏色:" + apple.getColor();
        });
    }
    public static void prettyPrintApple(List<Apple> apples, AppleFormat appleFormat) {
        for (Apple apple : apples) {
            String output = appleFormat.accept(apple);
            System.out.println(output);
        }
    }
}
  • 到此為止,我們可以將類(lèi)、匿名類(lèi)、Lambda進(jìn)行行為參數(shù)化,替代了之前的案例中的值參數(shù)化。
  • 現(xiàn)在我們發(fā)現(xiàn)上述的ApplePredicate只能處理Apple,且filterApples也只能處理Apple,所以我們將這部分使用泛型進(jìn)行抽象化,如下
public interface Predicate<T> {
    boolean test(T t);
}
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
    List<T> result = new ArrayList<>();
    for (T e : list) {
        if (predicate.test(e)){
            result.add(e);
        }
    }
    return result;
}
  • 通過(guò)這樣的處理,所有類(lèi)型的數(shù)據(jù),都可以這樣進(jìn)行篩選出結(jié)果。
  • 其他的行為參數(shù)化案例
  • 對(duì)集合進(jìn)行排序,根據(jù)蘋(píng)果顏色排序或者根據(jù)大小排序
  • 在Java 8中,List自帶了一個(gè)sort方法(你也可以使用Collections.sort)來(lái)進(jìn)行排序
  • sort的行為可以用java.util.Comparator對(duì)象來(lái)參數(shù)化,它的接口如下
public interface Comparator<T>{
    int compare(T o1, T o2);
}
  • 因此,你可以隨時(shí)創(chuàng)建Comparator的實(shí)現(xiàn),用sort方法表現(xiàn)出不同的行為。比如,你可以使用匿名類(lèi),按照重量升序?qū)?kù)存排序
public class Test8 {
    public static void main(String[] args) {
        AppleClient.getApples().sort(new Comparator<Apple>() {
            @Override
            public int compare(Apple o1, Apple o2) {
                return o1.getWeight() - o2.getWeight();
            }
        });
    }
}
  • 或者是Lambda表達(dá)式
public class Test8 {
    public static void main(String[] args) {
        AppleClient.getApples().sort((o1, o2) -> o1.getWeight() - o2.getWeight());
    }
}
  • 如果對(duì)匿名類(lèi)轉(zhuǎn)Lambda表達(dá)式不熟悉,我們會(huì)在下面進(jìn)行講解。

小結(jié)

  • 行為參數(shù)化,就是一個(gè)方法接受多個(gè)不同的行為作為參數(shù),并在內(nèi)部使用它們,完成不同行為的能力。
  • 行為參數(shù)化可讓代碼更好地適應(yīng)不斷變化的要求,減輕未來(lái)的工作量。
  • 傳遞代碼,就是將新行為作為參數(shù)傳遞給方法。但在Java 8之前這實(shí)現(xiàn)起來(lái)很啰嗦。為接口聲明許多只用一次的實(shí)體類(lèi)而造成的啰嗦代碼,在Java 8之前可以用匿名類(lèi)來(lái)減少。
  • Java API包含很多可以用不同行為進(jìn)行參數(shù)化的方法,包括排序、線程和GUI處理。

以上就是Java函數(shù)式編程之通過(guò)行為參數(shù)化傳遞代碼的詳細(xì)內(nèi)容,更多關(guān)于Java行為參數(shù)化傳遞代碼的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Guava本地緩存的使用過(guò)程

    Guava本地緩存的使用過(guò)程

    文章介紹了使用Guava和Redis實(shí)現(xiàn)二級(jí)緩存的原因,以及如何通過(guò)Guava作為一級(jí)緩存,Redis作為二級(jí)緩存來(lái)減少數(shù)據(jù)庫(kù)壓力,提高緩存的可靠性,同時(shí),通過(guò)一個(gè)具體示例說(shuō)明了如何在微服務(wù)場(chǎng)景中使用Guava和Redis進(jìn)行二級(jí)緩存,最后,總結(jié)了Guava的參數(shù)機(jī)制
    2025-01-01
  • Java使用JSQLParser解析和操作SQL的技術(shù)指南

    Java使用JSQLParser解析和操作SQL的技術(shù)指南

    在開(kāi)發(fā)過(guò)程中,解析和操作?SQL?是一個(gè)常見(jiàn)的需求,JSQLParser?是一個(gè)強(qiáng)大的開(kāi)源?Java?庫(kù),用于解析?SQL?并提供語(yǔ)法樹(shù)操作功能,本文將詳細(xì)介紹如何使用?JSQLParser,并提供常見(jiàn)使用場(chǎng)景的代碼示例,需要的朋友可以參考下
    2025-04-04
  • Spring rest接口中的LocalDateTime日期類(lèi)型轉(zhuǎn)時(shí)間戳

    Spring rest接口中的LocalDateTime日期類(lèi)型轉(zhuǎn)時(shí)間戳

    這篇文章主要介紹了Spring rest接口中的LocalDateTime日期類(lèi)型轉(zhuǎn)時(shí)間戳的方法,Java程序中一般將日期類(lèi)型定義為L(zhǎng)ocalDateTime,數(shù)據(jù)庫(kù)中保存的時(shí)間是0時(shí)區(qū)的時(shí)間
    2023-03-03
  • 關(guān)于MyBatis中SqlSessionFactory和SqlSession簡(jiǎn)解

    關(guān)于MyBatis中SqlSessionFactory和SqlSession簡(jiǎn)解

    這篇文章主要介紹了MyBatis中SqlSessionFactory和SqlSession簡(jiǎn)解,具有很好的參考價(jià)值,希望大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • java中 IO 常用IO操作類(lèi)繼承結(jié)構(gòu)分析

    java中 IO 常用IO操作類(lèi)繼承結(jié)構(gòu)分析

    本篇文章小編為大家介紹,java中 IO 常用IO操作類(lèi)繼承結(jié)構(gòu)分析。需要的朋友參考下
    2013-04-04
  • Groovy編程入門(mén)攻略

    Groovy編程入門(mén)攻略

    這篇文章主要介紹了Groovy編程入門(mén)攻略,Groovy是一種同樣使用Java虛擬機(jī)的動(dòng)態(tài)語(yǔ)言,需要的朋友可以參考下
    2015-07-07
  • SpringBoot返回統(tǒng)一的JSON標(biāo)準(zhǔn)格式實(shí)現(xiàn)步驟

    SpringBoot返回統(tǒng)一的JSON標(biāo)準(zhǔn)格式實(shí)現(xiàn)步驟

    這篇文章主要介紹了SpringBoot返回統(tǒng)一的JSON標(biāo)準(zhǔn)格式,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • Java并發(fā)編程中的CyclicBarrier線程屏障詳解

    Java并發(fā)編程中的CyclicBarrier線程屏障詳解

    這篇文章主要介紹了Java并發(fā)編程中的CyclicBarrier線程屏障詳解,
    2023-12-12
  • Hikari連接池使用SpringBoot配置JMX監(jiān)控實(shí)現(xiàn)

    Hikari連接池使用SpringBoot配置JMX監(jiān)控實(shí)現(xiàn)

    Hikari是Spring Boot默認(rèn)的數(shù)據(jù)庫(kù)連接池。區(qū)別于C3P0直接通過(guò)連接池對(duì)象獲取各項(xiàng)狀態(tài)指標(biāo),Hikari需要通過(guò)JMX來(lái)獲取。本文就詳細(xì)的來(lái)介紹一下,感興趣的可以了解一下
    2021-07-07
  • springboot批量接收對(duì)象參數(shù),接收List方式

    springboot批量接收對(duì)象參數(shù),接收List方式

    在Spring Boot項(xiàng)目中,批量接收對(duì)象參數(shù)可以通過(guò)自定義對(duì)象和使用`@RequestBody`注解來(lái)實(shí)現(xiàn),首先,定義一個(gè)包含列表的自定義對(duì)象,然后在Controller中使用該對(duì)象接收前端傳遞的JSON數(shù)組,通過(guò)Postman模擬請(qǐng)求,可以成功批量接收并處理對(duì)象參數(shù)
    2025-02-02

最新評(píng)論