函數(shù)式編程讓你忘記設(shè)計(jì)模式

本文是一篇《Java 8實(shí)戰(zhàn)》的閱讀筆記,閱讀大約需要5分鐘羔挡。

有點(diǎn)標(biāo)題黨签财,但是這確實(shí)是我最近使用Lambda表達(dá)式的感受。設(shè)計(jì)模式是過去的一些好的經(jīng)驗(yàn)和套路的總結(jié)淹朋,但是好的語言特性可以讓開發(fā)者不去考慮這些設(shè)計(jì)模式笙各。面向?qū)ο蟪R姷脑O(shè)計(jì)模式有策略模式、模板方法础芍、觀察者模式杈抢、責(zé)任鏈模式以及工廠模式,使用Lambda表達(dá)式(函數(shù)式編程思維)有助于避免面向?qū)ο箝_發(fā)中的那些固定代碼仑性。下面我們挑選了策略模式和職責(zé)鏈模式兩個(gè)案例進(jìn)行分析惶楼。

案例1:策略模式

策略設(shè)計(jì)模式

當(dāng)我們解決一個(gè)問題有不同的解法的時(shí)候,又不希望客戶感知到這些解法的細(xì)節(jié)诊杆,這種情況下適合使用策略模式歼捐。策略模式包括三個(gè)部分:

  • 解決問題的算法(上圖中的Strategy);
  • 一個(gè)或多個(gè)該類算法的具體實(shí)現(xiàn)(上圖中的ConcreteStrategyA晨汹、ConcreteStrategyB和ConcreteStrategyC)
  • 一個(gè)或多個(gè)客戶使用場景(上圖中的ClientContext)

面向?qū)ο笏悸?/h3>

首先定義策略接口豹储,表示排序策略:

public interface ValidationStrategy {
    boolean execute(String s);
}

然后定義具體的實(shí)現(xiàn)類(即不同的排序算法):

public class IsAllLowerCase implements ValidationStrategy {
    @Override
    public boolean execute(String s) {
        return s.matches("[a-z]+");
    }
}

public class IsNumberic implements ValidationStrategy {
    @Override
    public boolean execute(String s) {
        return s.matches("\\d+");
    }
}

最后定義客戶使用場景,代碼如下圖所示宰缤。Validator是為客戶提供服務(wù)時(shí)使用的上下文環(huán)境颂翼,每個(gè)Valiator對象中都封裝了具體的Strategy對象晃洒,在實(shí)際工作中,我們可以通過更換具體的Strategy對象來進(jìn)行客戶服務(wù)的升級朦乏,而且不需要讓客戶進(jìn)行升級球及。

public class Validator {

    private final ValidationStrategy strategy;

    public Validator(ValidationStrategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 給客戶的接口
     */
    public boolean validate(String s) {
        return strategy.execute(s);
    }
}

public class ClientTestDrive {

    public static void main(String[] args) {
        Validator numbericValidator = new Validator(new IsNumberic());
        boolean res1 = numbericValidator.validate("7780");
        System.out.println(res1);

        Validator lowerCaseValidator = new Validator(new IsAllLowerCase());
        boolean res2 = lowerCaseValidator.validate("aaaddd");
        System.out.println(res2);
    }
}

函數(shù)式編程思路

如果使用Lambda表達(dá)式考慮,你會(huì)發(fā)現(xiàn)ValidationStrategy就是一個(gè)函數(shù)接口(還與Predicate<String>具有同樣的函數(shù)描述)呻疹,那么就不需要定義上面那些實(shí)現(xiàn)類了吃引,可以直接用下面的代碼替換,原因是Lambda表達(dá)式內(nèi)部已經(jīng)對這些類進(jìn)行了一定的封裝刽锤。

public class ClientTestDrive {

    public static void main(String[] args) {
        Validator numbericValidator = new Validator((String s) -> s.matches("\\d+"));
        boolean res1 = numbericValidator.validate("7789");
        System.out.println(res1);

        Validator lowerCaseValidator = new Validator((String s) -> s.matches("[a-z]+"));
        boolean res2 = lowerCaseValidator.validate("aaaddd");
        System.out.println(res2);
    }
}

案例2:責(zé)任鏈模式

在某些場景下镊尺,需要對一個(gè)對象做一系列的工作,這些工作分別是由不同的類完成的并思,這時(shí)候就比較適合使用責(zé)任鏈模式庐氮。責(zé)任鏈模式的主要組成部分包括三個(gè):

  • 管理操作序列的抽象類,在該抽象類里有會(huì)有一個(gè)對象記錄當(dāng)前對象的后繼操作對象宋彼;
  • 一些具體的操作對象弄砍,這些操作對象會(huì)以一個(gè)鏈表的形式組織起來
  • 一個(gè)使用該模式的客戶端組件,該組件只需要跟一個(gè)組件打交道就好输涕,不需要跟很多個(gè)操作對象耦合在一起音婶。


    責(zé)任鏈模式

面向?qū)ο笏悸?/h3>

首先看下我們這里定義了一個(gè)抽象類ProcessingObject,其中successor字段用于管理該對象的后繼操作對象莱坎;handle接口作為對外提供服務(wù)的接口衣式;handleWork作為實(shí)際處理對象的操作方法。

public abstract class ProcessingObject<T> {

    protected ProcessingObject<T> successor;
    
    public void setSuccessor(ProcessingObject<T> successor) {
        this.successor = successor;
    }

    public T handler(T input) {
        T r = handleWork(input);
        if (successor != null) {
            return successor.handler(r);
        }
        return r;
    }

    abstract protected T handleWork(T input);
}

接下來可以定義兩個(gè)具體的操作對象檐什,如下面代碼所示碴卧。PS:這里《Java 8實(shí)戰(zhàn)》書中用的是replaceAll方法是不太合適的,這個(gè)點(diǎn)可以參考我們之前的文章——020:舉幾個(gè)String的API以及案例乃正。

public class HeaderTextProcessing extends ProcessingObject<String> {
    @Override
    protected String handleWork(String input) {
        return "From Raoul, Mario and Alan: " + input;
    }
}

public class SpellCheckerProcessing extends ProcessingObject<String> {
    @Override
    protected String handleWork(String input) {
        return input.replace("labda", "lambda");
    }
}

最后螟深,你就可以在Client中將這上面兩個(gè)具體的操作類對象構(gòu)成一個(gè)操作序列,參見下面的代碼:

public class Client {
    public static void main(String[] args) {
        ProcessingObject<String> p1 = new HeaderTextProcessing();
        ProcessingObject<String> p2 = new SpellCheckerProcessing();

        p1.setSuccessor(p2);

        String result = p1.handler("Aren't labdas really sexy?!!");
        System.out.println(result);
    }
}

函數(shù)式編程思路

如果使用函數(shù)式編程思維烫葬,那么職責(zé)鏈模式就直接了——y=f(x)和z=g(x)這兩個(gè)方法都是要對x做處理,那么如果將這兩個(gè)函數(shù)組合在一起凡蜻,就會(huì)形成r=f(g(x))的情況搭综,也就是可以使用Lambda表達(dá)式中的addThen來串聯(lián)起多個(gè)處理過程。

public class ClientWithLambda {
    public static void main(String[] args) {
        UnaryOperator<String> headerProcessing = (String text) -> "From Raoul, Mario and Alan: " + text;

        UnaryOperator<String> spellCheckProcessing = (String text) -> text.replace("labda", "lambda");

        Function<String, String> function = headerProcessing.andThen(spellCheckProcessing);

        String result = function.apply("Aren't labdas really sexy?!!");
        System.out.println(result);

        UnaryOperator<String> hhhhhProcessing = (String text) -> text.concat("hhhh");
        Function<String, String> function1 = function.andThen(hhhhhProcessing);
        String result1 = function1.apply("Aren't labdas really sexy?!!");
        System.out.println(result1);
    }
}

上面是利用Java原生的Lambda表達(dá)式實(shí)現(xiàn)的職責(zé)鏈模式划栓,我們也可以使用前面一篇文章——vavr:讓你像寫Scala一樣寫Java中介紹過的vavr庫來實(shí)現(xiàn)兑巾,代碼如下所示:

public class ClientWithVavr {
    public static void main(String[] args) {
        Function1<String, String> headerProcessing = (String text) -> "From Raoul, Mario and Alan: " + text;
        Function1<String, String> specllCheckProcessing = (String text) -> text.replace("labda", "lambda");

        Function1<String, String> function = headerProcessing.compose(specllCheckProcessing);
        String result = function.apply("Aren't labdas really sexy?!!");
        System.out.println(result);
    }
}

總結(jié)

可以看出,函數(shù)式編程思維跟面向?qū)ο缶幊趟季S的思考方式是不同的忠荞,表達(dá)力更強(qiáng)蒋歌,因此帅掘,作為開發(fā)者是時(shí)候認(rèn)真學(xué)習(xí)下函數(shù)式編程思維了,作為Java開發(fā)者堂油,我準(zhǔn)備先從Lambda表達(dá)式開始學(xué)起修档,然后嘗試學(xué)習(xí)下Scala或Kotlin兩門語言中的函數(shù)式變成特性。

參考資料

  1. 《Java編程實(shí)戰(zhàn)》
  2. 《設(shè)計(jì)模式之禪》

本號(hào)專注于后端技術(shù)府框、JVM問題排查和優(yōu)化吱窝、Java面試題、個(gè)人成長和自我管理等主題迫靖,為讀者提供一線開發(fā)者的工作和成長經(jīng)驗(yàn)院峡,期待你能在這里有所收獲。
javaadu
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末系宜,一起剝皮案震驚了整個(gè)濱河市照激,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盹牧,老刑警劉巖俩垃,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異欢策,居然都是意外死亡吆寨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門踩寇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啄清,“玉大人,你說我怎么就攤上這事俺孙±弊洌” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵睛榄,是天一觀的道長荣茫。 經(jīng)常有香客問我,道長场靴,這世上最難降的妖魔是什么啡莉? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮旨剥,結(jié)果婚禮上咧欣,老公的妹妹穿的比我還像新娘。我一直安慰自己轨帜,他們只是感情好魄咕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蚌父,像睡著了一般哮兰。 火紅的嫁衣襯著肌膚如雪毛萌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天喝滞,我揣著相機(jī)與錄音阁将,去河邊找鬼。 笑死囤躁,一個(gè)胖子當(dāng)著我的面吹牛冀痕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狸演,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼言蛇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宵距?” 一聲冷哼從身側(cè)響起腊尚,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎满哪,沒想到半個(gè)月后婿斥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哨鸭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年民宿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片像鸡。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡活鹰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出只估,到底是詐尸還是另有隱情志群,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布蛔钙,位于F島的核電站锌云,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏吁脱。R本人自食惡果不足惜桑涎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望兼贡。 院中可真熱鬧石洗,春花似錦、人聲如沸紧显。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽孵班。三九已至涉兽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間篙程,已是汗流浹背枷畏。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虱饿,地道東北人拥诡。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像氮发,于是被迫代替她去往敵國和親渴肉。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容