函數(shù)式編程是如何提升代碼的擴(kuò)展性
軟件的發(fā)展大致經(jīng)歷三個(gè)階段
第一階段(20世紀(jì)40年代中期到50年代中期),主要是科學(xué)與工程計(jì)算,處理對(duì)象為數(shù)值數(shù)據(jù),以個(gè)體方式使用機(jī)器(或匯編)語(yǔ)言編制程序
第二階段(20世紀(jì)50年代中期到60年代后期),從高級(jí)程序設(shè)計(jì)語(yǔ)言出現(xiàn)到軟件工程提出以前。這個(gè)階段研究對(duì)象增加了并發(fā)程序,并著重研究高級(jí)程序設(shè)計(jì)語(yǔ)言、編譯程序、操作系統(tǒng)以及各種支撐軟件和應(yīng)用軟件
第三階段(20世紀(jì)60年代后期軟件工程提出以來(lái)),由于大型軟件的開(kāi)發(fā)耗時(shí)耗力,任務(wù)重,需要采用合作的方式才能完成,所以引入軟件工程的理念來(lái)管理項(xiàng)目。
從工程學(xué)角度來(lái)講,我們常說(shuō)的軟件工程一般采用面向?qū)ο缶幊?,差別在與使用的編程語(yǔ)言不同,有人習(xí)慣用java,有人喜歡C#,各有各的特色,除了語(yǔ)法上略有差異,其本質(zhì)都差不多,所以你可能會(huì)經(jīng)常聽(tīng)到有人說(shuō),只要你精通了一門(mén)語(yǔ)言,再學(xué)習(xí)其他語(yǔ)言,會(huì)感覺(jué)如有神助,基本也是這個(gè)道理。
面向?qū)ο缶幊?/strong>
首先我們來(lái)看下面向?qū)ο缶幊痰娜筇匦?/span>
封裝
繼承
多態(tài)
面向?qū)ο缶幊淌且环N具有對(duì)象概念的程序編程范型,它可能包含數(shù)據(jù)、屬性、代碼與方法。對(duì)象則指的是類(lèi)的實(shí)例。它將對(duì)象作為程序的基本單元,將程序和數(shù)據(jù)封裝其中,以提高軟件的可重用性、靈活性和可擴(kuò)展性,對(duì)象里的程序可以訪問(wèn)及修改對(duì)象相關(guān)聯(lián)的數(shù)據(jù)。在面向?qū)ο缶幊汤铮?jì)算機(jī)程序會(huì)被設(shè)計(jì)成彼此相關(guān)的對(duì)象。
對(duì)象按照?qǐng)?zhí)行角色,可以分為數(shù)據(jù)對(duì)象、行為對(duì)象。我們常理解的面向?qū)ο缶幊痰哪J?,比如:y=f(x),其中x、y都是數(shù)據(jù)對(duì)象,通過(guò)行為對(duì)象F的方法運(yùn)算得到了加工后的對(duì)象y。
我們具體看個(gè)示例:
/*** @Author onlyone* <p>* 活動(dòng)模型*/public class Activity {private Long id; // 活動(dòng)idprivate String name; // 名稱(chēng)private String desc; // 描述private Date time; // 活動(dòng)時(shí)間private String publisher; // 發(fā)布人}
按活動(dòng)id查找一個(gè)活動(dòng),代碼一般會(huì)這么寫(xiě)
public Activity queryById(List<Activity> activityList, String id) {for (Activity activity : activityList) {if (id.equals(activity.getId())) {return activity;}}return null;}
如果此時(shí)業(yè)務(wù)提出了一個(gè)新的需求,按名稱(chēng)來(lái)查詢活動(dòng),得嘞,又得重新造個(gè)輪子
public Activity queryByName(List<Activity> activityList, String name) {for (Activity activity : activityList) {if (name.equals(activity.getId())) {return activity;}}return null;}
過(guò)了幾天,業(yè)務(wù)產(chǎn)品又來(lái)找你了,想根據(jù)時(shí)間來(lái)查詢活動(dòng),此時(shí),你是不是有種崩潰的感覺(jué)。

重構(gòu)是我們腦海閃現(xiàn)的第一念想,如何重構(gòu),卻讓我們陷入一臉懵逼的茫然狀態(tài)。三個(gè)需求,處理邏輯各不相同,如何復(fù)用抽???
我們需要改變我們的思維方式,誰(shuí)規(guī)定調(diào)用方法傳入的實(shí)參一定是數(shù)值型對(duì)象,如果傳入一個(gè)函數(shù)表達(dá)式,能不能解決這個(gè)問(wèn)題?
是不是有種豁然開(kāi)朗的感覺(jué)。

函數(shù)式編程
函數(shù)式編程第一個(gè)需要了解的概念就是函數(shù):
函數(shù)可以按需創(chuàng)建
函數(shù)可以當(dāng)作實(shí)參傳給另一個(gè)方法
函數(shù)可以當(dāng)作另一個(gè)方法的返回值
JDK 8 開(kāi)始引入函數(shù)式編程,并提供了很多預(yù)定義接口類(lèi),如 Predicates 用于判斷,函數(shù) Functions,生產(chǎn) Suppliers,消費(fèi) Consumers,比較 Comparators。
代碼示例:https://github.com/aalansehaiyang/java8-tutorial
本文的重構(gòu)思路就是采用Predicate接口,我們先來(lái)看些內(nèi)部結(jié)構(gòu)
@FunctionalInterfacepublic interface Predicate<T> {boolean test(T t);}
/*** @Author onlyone* 面向函數(shù)編程*/public class FuncitionProgram {private List<Activity> activityList;/*** 基礎(chǔ)查詢骨架*/Activity queryByPredicate(Predicate<Activity> predicate) {for (Activity activity : activityList) {if (predicate.test(activity)) {return activity;}}return null;}}
按id查詢活動(dòng),傳入對(duì)應(yīng)的Predicate表達(dá)式
public Activity queryById(String id) {return queryByPredicate(activity -> id.equals(activity.getId()));}
按名稱(chēng)查詢活動(dòng),傳入對(duì)應(yīng)的Predicate表達(dá)式
public Activity queryByName(String name) {return queryByPredicate(activity -> name.equals(activity.getName()));}
是不是有種”牛逼“的感覺(jué)。低調(diào),優(yōu)化是永無(wú)止境,有沒(méi)有更好的優(yōu)化方式。
作為一名架構(gòu)師,我們?cè)谧鱿到y(tǒng)架構(gòu)時(shí),為了滿足其高并發(fā)、擴(kuò)展性,一般會(huì)講究一個(gè)拆分原則,將一個(gè)復(fù)雜的業(yè)務(wù)域問(wèn)題拆分成一個(gè)個(gè)業(yè)務(wù)子域,降低系統(tǒng)的復(fù)雜度,也能滿足其后續(xù)的靈活擴(kuò)展。按照這個(gè)思路,我們繼續(xù)優(yōu)化,將Predicate 函數(shù)獨(dú)立出來(lái)。
// id判斷函數(shù)public Predicate<Activity> idPredicate(String id) {return activity -> id.equals(activity.getId());}// 名稱(chēng)判斷函數(shù)public Predicate<Activity> namePredicate(String name) {return activity -> name.equals(activity.getName());}
我們提供了`原子化`函數(shù),具體怎么組裝,由上層的調(diào)用方根據(jù)業(yè)務(wù)需要自己來(lái)拼接。
// 按id查詢活動(dòng)queryByPredicate(idPredicate(id));// 按名稱(chēng)查詢活動(dòng)queryByPredicate(namePredicate(name));// 按id、名稱(chēng)組合條件查詢活動(dòng)queryByPredicate(idPredicate(id).and(namePredicate(name)));
畫(huà)外音:
萬(wàn)事萬(wàn)物,由于都有其個(gè)性化特征,如果按常規(guī)方式,窮舉是很難滿足所有業(yè)務(wù)需求。但如果我們能按其特征抽取,封裝組件能力,由流程引擎根據(jù)業(yè)務(wù)訴求,自由組合,則能滿足其最大靈活性,更像一個(gè)軟件高手所為。
本文的代碼示例已上傳到github
https://github.com/aalansehaiyang/project-example
歡迎關(guān)注微信公眾號(hào):互聯(lián)網(wǎng)全棧架構(gòu),收取更多有價(jià)值的信息。
