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

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

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

不斷變化的需求

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

  • 比如之前的蘋果的例子

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

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

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

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

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

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

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

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

  • 這里會(huì)使用一個(gè)案例,并且逐步改善
  • 篩選綠蘋果
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)在做的就是篩選綠蘋果的,現(xiàn)在需求變更,需要篩選紅蘋果,那還要把這個(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ū)分重的蘋果和輕的蘋果,同理
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ù)制了代碼,對整個(gè)輸出行為有影響的只有那一行判斷條件
  • 再次嘗試:對你能所想到的每個(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è)屬性生效,那么可以遇見的是,如果Apple增加了其他的字段:甜度、水分、生產(chǎn)地......
  • 那么這個(gè)方法已經(jīng)無法維護(hù)了,如果需要更加復(fù)雜的查詢,也無法編寫。而且,向一個(gè)方法傳遞一個(gè)boolean是非常危險(xiǎn)的事情,有boolean就意味著有分支。

行為參數(shù)化

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

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

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

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

  • 我們稱之為謂詞,定義接口如下

public interface ApplePredicate {
    boolean test(Apple apple);
}
  • 然后可以使用ApplePredicate的多個(gè)實(shí)現(xiàn)來執(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對象,對Apple做條件測試。
  • 這就是行為參數(shù)化:讓方法接受多種行為(或戰(zhàn)略)作為參數(shù),并在內(nèi)部使用,來完成不同的行為。
  • 那么現(xiàn)在就來修改之前的代碼
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ū)分開了。
  • 這樣一來,任何需求的變更,只需要增加相應(yīng)的實(shí)現(xiàn)類即可。filterApples方法的行為取決于你通過ApplePredicate對象傳遞的代碼。但是會(huì)有一個(gè)問題:類膨脹。
  • 代碼傳遞/行為
  • 在上述的實(shí)現(xiàn)中,我們發(fā)現(xiàn),對于整個(gè)測試,唯一重要的就是test方法的實(shí)現(xiàn),這個(gè)方法決定了需要怎么樣進(jìn)行過濾。
  • 所以在傳遞行為的時(shí)候,我們可以直接使用匿名內(nè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è)很多無用的代碼,此時(shí)再將匿名內(nèi)部類更改為Lambda表達(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ù)化的好處在于你可以把迭代要篩選的集合的邏輯與對集合中每個(gè)元素應(yīng)用的行為區(qū)分開來。這樣你可以重復(fù)使用同一個(gè)方法,給它不同的行為來達(dá)到不同的目的
  • 新需求,對蘋果進(jìn)行遍歷,然后對其進(jìn)行格式化輸出
  • 這個(gè)需求需要定義一個(gè)新的方法,prettyPrintApple,參照上面的篩選蘋果的案例,整體的執(zhí)行框架如下
public static void prettyPrintApple(List<Apple> apples,???){
    for (Apple apple : apples) {
        String output = ???.???(apple);
        System.out.println(output);
    }
}
  • 其中???的部分就是我們要填充的行為,其比較簡單:輸入一個(gè)Apple,然后輸出一個(gè)String,那么就來定義這樣的一個(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);
        }
    }
}
  • 到此為止,我們可以將類、匿名類、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;
}
  • 通過這樣的處理,所有類型的數(shù)據(jù),都可以這樣進(jìn)行篩選出結(jié)果。
  • 其他的行為參數(shù)化案例
  • 對集合進(jìn)行排序,根據(jù)蘋果顏色排序或者根據(jù)大小排序
  • 在Java 8中,List自帶了一個(gè)sort方法(你也可以使用Collections.sort)來進(jìn)行排序
  • sort的行為可以用java.util.Comparator對象來參數(shù)化,它的接口如下
public interface Comparator<T>{
    int compare(T o1, T o2);
}
  • 因此,你可以隨時(shí)創(chuàng)建Comparator的實(shí)現(xiàn),用sort方法表現(xiàn)出不同的行為。比如,你可以使用匿名類,按照重量升序?qū)齑媾判?/li>
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());
    }
}
  • 如果對匿名類轉(zhuǎn)Lambda表達(dá)式不熟悉,我們會(huì)在下面進(jìn)行講解。

小結(jié)

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

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

相關(guān)文章

  • Guava本地緩存的使用過程

    Guava本地緩存的使用過程

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

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

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

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

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

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

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

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

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

    Groovy編程入門攻略

    這篇文章主要介紹了Groovy編程入門攻略,Groovy是一種同樣使用Java虛擬機(jī)的動(dòng)態(tài)語言,需要的朋友可以參考下
    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)格式,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(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ù)庫連接池。區(qū)別于C3P0直接通過連接池對象獲取各項(xiàng)狀態(tài)指標(biāo),Hikari需要通過JMX來獲取。本文就詳細(xì)的來介紹一下,感興趣的可以了解一下
    2021-07-07
  • springboot批量接收對象參數(shù),接收List方式

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

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

最新評(píng)論