基于lambda簡化設計模式

前言

雖說使用設計模式可以讓復雜的業務代碼變得清晰且易于維護,但是某些情況下,開發可能會遇到我為了簡單的業務邏輯去適配設計模式的情況,本文筆者就以四種常見的設計模式為例,演示如何基于lambda來簡化設計模式的實現。

策略模式

我們的項目中會涉及各種各樣的校驗,可能是校驗電話號碼、單雙數、字符串長度等,為此我們希望通過策略模式來封裝這些校驗規則。

第一步自然是通過接口來定義策略,編寫一個名為execute方法,讓用戶傳入字符串,返回校驗結果的布爾值:

/*** 定義策略模式的接口*/
public interface ValidationStrategy {/*** 校驗該字符串是否符合要求,若符合則返回true* @param str* @return*/boolean execute(String str);
}

然后將這個策略接口聚合到我們的校驗器中,后續我們就可以按照需求傳入對應的校驗策略即可:

/*** 校驗工具,將策略接口成員成員屬性,起到依賴抽象的作用*/
public class Validator {private ValidationStrategy strategy;public Validator() {}public Validator(ValidationStrategy strategy) {this.strategy = strategy;}public boolean validate(String str) {return strategy.execute(str);}
}

假如我們需要校驗這個字符串是否全為字符串小寫,那么我們就可以封裝這樣一個類:

/*** 判斷是否全為小寫*/
public class IsAllLowerCase implements ValidationStrategy {@Overridepublic boolean execute(String str) {return str.matches("[a-z]+");}
}

同理,如果我們需要判斷是否全為數字,則可以這樣寫:

/*** 判斷傳入字符是否全為數字*/
public class IsNumeric implements ValidationStrategy {@Overridepublic boolean execute(String str) {return str.matches("\\d+");}
}

使用時,我們只需按需傳入校驗規則即可:

public class Main {public static void main(String[] args) {//校驗是否全為數字Validator v1 = new Validator(new IsNumeric());System.out.println(v1.validate("1234"));//校驗是否全是小寫Validator v2 = new Validator(new IsAllLowerCase());System.out.println(v2.validate("dalhl"));}
}

輸出結果如下:

true
true

不知道讀者是否可以發現問題,規則的校驗往往只是一兩行代碼,為了適配規則校驗所用到的策略模式,開發者往往需要對此額外創建一個類,要知道字符校驗的規則是成百上千的,并且很多校驗規則很可能僅僅是某個業務才會用到的。

所以我們是否有辦法做到既能適配策略模式,又避免為了一段簡單的校驗代碼而去創建一個類呢?

查看我們校驗策略接口ValidationStrategy的定義,它要求傳入一個String返回一個boolean,由此我們想到了java8提供的函數時接口Function,其定義如下所示,可以根據泛型要求要指明泛型TRapply方法要求傳入一個T,這里可以直接理解為我們的String,然后返回一個R,同理代入我們的boolean:

@FunctionalInterface
public interface Function<T, R> {/*** Applies this function to the given argument.** @param t the function argument* @return the function result*/R apply(T t);.......}

按照java8lambda語法糖,Function<T, R>只有一個需要實現的方法R apply(T t),我們完全可以表面類的創建,取而代之的是這樣一段表達式:

t->R

查看我們ValidationStrategy的定義,它也是只有一個方法execute,我們完全可以將其視為Function<String, Boolean>,即可得表達式s->boolean

在這里插入圖片描述

由此我們得出下面這段代碼,可以看到根據接口的定義匹配java8對應的函數時接口,然后基于lambda表達式即可完成創建,這樣做法避免了類的生命,避免了簡單邏輯復雜化實現的問題:

public static void main(String[] args) {//校驗是否全為數字Validator v1 = new Validator((s) -> s.matches("\\d+"));System.out.println(v1.validate("1234"));//校驗是否全是小寫Validator v2 = new Validator(s -> s.matches("[a-z]+"));System.out.println(v2.validate("dalhl"));}

模板方法

銀行接待VIP顧客的核心流程為:

  1. 查詢顧客是否是VIP
  2. 招待顧客,為顧客辦理業務。

所有銀行的大體流程都是這樣,唯一的區別就是第2步,對此我們可以使用模板方法模式,創建一個抽象類,將第1步抽出來,而第2步按照不同銀行進行不同的實現:

public abstract class Banking {public void processCustomer(int id) {//查詢會員名String customer = getCustomerWithId(id);//招待會員makeCustomerHappy(customer);}private String getCustomerWithId(int id) {return RandomUtil.randomString(5);}protected abstract void makeCustomerHappy(String customer);
}

對應兩個銀行的實現代碼,先來看看BankingA 的招待邏輯:

public class BankingA extends Banking {@Overrideprotected void makeCustomerHappy(String customer) {System.out.println("請"+customer+"吃飯,并為其辦理業務");}
}

BankingB的招待邏輯:

public class BankingB extends Banking {@Overrideprotected void makeCustomerHappy(String customer) {System.out.println("請" + customer + "喝茶,并為其辦理業務");}
}

測試代碼如下:

public static void main(String[] args) {BankingA bankingA = new BankingA();bankingA.processCustomer(1);BankingB bankingB= new BankingB();bankingB.processCustomer(1);}

對應輸出結果:

6brkb吃飯,并為其辦理業務
請autjm喝茶,并為其辦理業務

還是一樣的問題,找到會員是一段無返回值的簡單輸出,為了適配模板方法,這一行代碼也還是要創建一個類,所以我們還是需要用lambda對其進行簡化。

查看抽象方法makeCustomerHappy的定義,它要求傳入一個傳入而返回一個void,查閱java8對應的函數式接口,我們找到了Consumer

@FunctionalInterface
public interface Consumer<T> {/*** Performs this operation on the given argument.** @param t the input argument*/void accept(T t);

于是我們得出公式s->Void

在這里插入圖片描述

對此我們將抽象類Banking 加以改造,將抽象方法makeCustomerHappy改為Consumer接口:

public abstract class Banking {public void processCustomer(int id, Consumer<String> makeCustomerHappy) {//查詢會員名String customer = getCustomerWithId(id);//招待會員makeCustomerHappy.accept(customer);}private String getCustomerWithId(int id) {return RandomUtil.randomString(5);}}

這樣一來,后續的調用即可用一段lambda實現:

public class Main {public static void main(String[] args) {Banking bankingA = new Banking();bankingA.processCustomer(1,customer-> System.out.println("請"+customer+"吃飯,并為其辦理業務"));Banking bankingB = new Banking();bankingB.processCustomer(1,customer-> System.out.println("請"+customer+"喝茶,并為其辦理業務"));}
}

觀察者模式

觀察者模式算是最經典也最好理解的設計模式,觀察者只需將自己注冊到感興趣的主題上,一旦有主題更新就會及時通知觀察者,觀察者按照自己的需要進行響應處理。

對此我們首先定義觀察者的接口:

/*** 觀察者*/
public interface Observer {void inform(String msg);
}

接下來就是主題:

public interface Subject {void registerObserver(Observer observer);void notifyObserver();
}

創建一個觀察者1以及觀察者2以及實現他們對自己感興趣主題時會做出的反饋輸出方法inform:

public class Observer1 implements Observer {@Overridepublic void inform(String msg) {System.out.println("觀察者1收到通知,內容為:" + msg);}
}
public class Observer2 implements Observer {@Overridepublic void inform(String msg) {System.out.println("觀察者2收到通知,內容為:" + msg);}
}

最后就是主題類的實現,我們將觀察者聚合,如果觀察者對SubJect1 感興趣,則通過registerObserver完成注冊,一旦主題要發布新消息就可以通過notifyObserver及時通知每一個訂閱者:

public class SubJect1 implements Subject {private String msg;public SubJect1(String msg) {this.msg = msg;}private List<Observer> observerList = new ArrayList<>();@Overridepublic void registerObserver(Observer observer) {observerList.add(observer);}@Overridepublic void notifyObserver() {observerList.forEach(o -> o.inform(msg));}
}

測試代碼和對應輸出結果如下所示:

public static void main(String[] args) {SubJect1 subJect1 = new SubJect1("請大家學習《基于lambda簡化設計模式》");//注冊訂閱者subJect1.registerObserver(new Observer1());subJect1.registerObserver(new Observer2());//主題發起通知subJect1.notifyObserver();}

輸出結果:

觀察者1收到通知,內容為:請大家學習《基于lambda簡化設計模式》
觀察者2收到通知,內容為:請大家學習《基于lambda簡化設計模式》

很明顯的Observer的inform是典型的Consumer接口,我們直接將其簡化:

public static void main(String[] args) {SubJect1 subJect1 = new SubJect1("請大家學習《基于lambda簡化設計模式》");//注冊訂閱者subJect1.registerObserver(s -> System.out.println("觀察者1收到消息" + s));subJect1.registerObserver(s -> System.out.println("觀察者2收到消息" + s));//主題發起通知subJect1.notifyObserver();}

責任鏈模式

我們希望字符串被對象1處理完成之后要轉交給對象2處理,并且我們后續可能還會交給更多的對象處理,通過對需求的梳理和抽象,這個功能完全可以通過責任鏈模式來實現。

首先聲明公共抽象類,可以看到考慮類的通用性筆者將這個類的入參設置為泛型,并且公共方法handle的步驟為:

  1. 調用自己的handWork處理輸入數據,handWork交給實現類自行編寫。
  2. successor不為空,則將處理結果交給下一個處理器處理,由此構成一條處理鏈。
public abstract class ProcessingObject<T> {/*** 下一個處理器*/private ProcessingObject<T> successor;public ProcessingObject<T> getSuccessor() {return successor;}public void setSuccessor(ProcessingObject<T> successor) {this.successor = successor;}public T handle(T input) {//先自己處理完,如果有后繼責任鏈,則交給后面的責任鏈處理,遞歸下去T t = handWork(input);if (successor != null) {return successor.handWork(t);}return t;}/*** 自己的處理邏輯** @param intput* @return*/abstract T handWork(T intput);
}

對應的我們基于這個抽象類實現兩個字符處理器,ProcessingStr1會將收到的中文逗號換位英文逗號:

public class ProcessingStr1 extends ProcessingObject<String> {@OverrideString handWork(String intput) {return intput.replace(",", ",");}
}

ProcessingStr2 會將中文句號替換為英文句號:

public class ProcessingStr2 extends ProcessingObject<String> {@OverrideString handWork(String intput) {return intput.replace("。", ".");}
}

測試代碼和輸出結果如下:

public static void main(String[] args) {ProcessingObject<String> p1 = new ProcessingStr1();ProcessingObject<String> p2 = new ProcessingStr2();p1.setSuccessor(p2);System.out.println(p1.handle("hello,world。"));}

可以看到所有的中文符號都被替換成英文符號了:

hello,world.

話不多說,不難看出上文這種傳入String返回String的方法,我們完全可以使用UnaryOperator函數式接口實現表達式。

UnaryOperator源碼可知,它繼承Function,我們只需傳入泛型T即可得到一個Function<T, T>,從而讓我們得到一個T->TFunction表達式:

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {/*** Returns a unary operator that always returns its input argument.** @param <T> the type of the input and output of the operator* @return a unary operator that always returns its input argument*/static <T> UnaryOperator<T> identity() {return t -> t;}
}

而責任連的方式也很簡單,因為UnaryOperator是Function的子類,這意味著我們可以使用FunctionandThen將所有的UnaryOperator完成銜接:

 UnaryOperator<String> p1 = i -> i.replace(",", ",");UnaryOperator<String> p2 = i -> i.replace("。", ".");p1.andThen(p2);System.out.println(p1.apply("hello,world。"));

小結

為了適配設計模式常會出現為了一段簡單的邏輯,而去編寫大量實現類的情況,所以我們建議,對于邏輯比較簡單且需要適配設計模式的功能,可以嘗試找到合適的函數式接口簡化功能的實現,避免大量類文件的聲明。

參考

Java 8 in Action

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/211327.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/211327.shtml
英文地址,請注明出處:http://en.pswp.cn/news/211327.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

WorkPlus高效助力企業溝通的專業級即時通訊軟件

在當今高度信息化和全球化競爭的世界&#xff0c;企業需要一個高效便捷的溝通工具來促進團隊協作、提高工作效率。在這樣的背景下&#xff0c;WorkPlus作為一款專業級的即時通訊軟件應運而生。讓我們一起深入了解WorkPlus&#xff0c;探討其在企業溝通中的領先優勢和卓越能力。…

平衡二叉樹

AVL簡稱平衡二叉樹&#xff0c;縮寫為BBST&#xff0c;由蘇聯數學家 Adelse-Velskil 和 Landis 在 1962 年提出。 二叉樹是動態查找的典范&#xff0c;但在極限情況下&#xff0c;二叉樹的查找效果等同于鏈表&#xff0c;而平衡二叉樹可以完美的達到 log ? 2 n \log_2 n log2…

ElementPlus table 中嵌套 input 輸入框

文章目錄 需求分析 需求 vue3 項目中 使用UI組件庫 ElementPlus 時&#xff0c;table 中嵌入 input輸入框 分析 <template><div class"p-10"><el-table :data"tableData" border><el-table-column prop"date" label&qu…

課堂練習4.1:段式內存管理

4-1 課堂練習4.1&#xff1a;段式內存管理 段式內存管理以段為單位分配內存空間&#xff0c;段內連續&#xff0c;段間可以不連續。段可以很大&#xff0c;比如數據段、代碼段、棧段等。本實訓分析 Linux 0.11 的段式內存管理技術。 第1關1 號進程 mynext 變量的邏輯地址與線性…

cache教程 3.HTTP服務器

上一節我們實現了單機版的緩存服務&#xff0c;但是我們的目標是分布式緩存。那么&#xff0c;我們就需要把緩存服務部署到多態機器節點上&#xff0c;對外提供訪問接口。客戶端就可以通過這些接口去實現緩存的增刪改查。 分布式緩存需要實現節點間通信&#xff0c;而通信方法…

【面試經典150 | 二叉樹】翻轉二叉樹

文章目錄 寫在前面Tag題目來源題目解讀解題思路方法一&#xff1a;遞歸方法二&#xff1a;迭代 寫在最后 寫在前面 本專欄專注于分析與講解【面試經典150】算法&#xff0c;兩到三天更新一篇文章&#xff0c;歡迎催更…… 專欄內容以分析題目為主&#xff0c;并附帶一些對于本題…

4-SpringMVC

文章目錄 項目源碼地址回顧-MVC什么是MVC&#xff1f;MVC各部分組成 回顧-ServletMaven創建Web項目1、創建Maven父工程pom&#xff0c;并導入依賴2、用Maven新建一個Web Module3、代碼&#xff1a;HelloServlet.java3、代碼-hello.jsp3、代碼-web.xml4、配置Tomcat5、瀏覽器測試…

github使用方法【附安裝包】

如果你是一枚Coder&#xff0c;但是你不知道Github&#xff0c;那么我覺的你就不是一個菜鳥級別的Coder&#xff0c;因為你壓根不是真正Coder&#xff0c;你只是一個Code搬運工。說明你根本不善于突破自己&#xff01;為什么這么說原因很簡單&#xff0c;很多優秀的代碼以及各種…

高級系統架構設計師之路

前言&#xff1a;系 統 架 構 設 計 師 (System Architecture Designer)是項目開發活動中的眾多角色之 一 &#xff0c;它可 以是 一個人或 一個小組&#xff0c;也可以是一個團隊。架構師 (Architect) 包含建筑師、設計師、創造 者、締造者等含義&#xff0c;可以說&#xff0…

邊緣計算系統設計與實踐:引領科技創新的新浪潮

文章目錄 一、邊緣計算的概念二、邊緣計算的設計原則三、邊緣計算的關鍵技術四、邊緣計算的實踐應用《邊緣計算系統設計與實踐》特色內容簡介作者簡介目錄前言/序言本書讀者對象獲取方式 隨著物聯網、大數據和人工智能等技術的快速發展&#xff0c;傳統的中心化計算模式已經無法…

基于ssm人力資源管理系統論文

摘 要 隨著企業員工人數的不斷增多&#xff0c;企業在人力資源管理方面負擔越來越重&#xff0c;因此&#xff0c;為提高企業人力資源管理效率&#xff0c;特開發了本人力資源管理系統。 本文重點闡述了人力資源管理系統的開發過程&#xff0c;以實際運用為開發背景&#xff0…

【大數據】Hudi 核心知識點詳解(一)

Hudi 核心知識點詳解&#xff08;一&#xff09; 1.數據湖與數據倉庫的區別 &#xff1f;1.1 數據倉庫1.2 數據湖1.3 兩者的區別 2.Hudi 基礎功能2.1 Hudi 簡介2.2 Hudi 功能2.3 Hudi 的特性2.4 Hudi 的架構2.5 湖倉一體架構 3.Hudi 數據管理3.1 Hudi 表數據結構3.1.1 .hoodie …

【C語言】位運算實現二進制數據處理及BCD碼轉換

文章目錄 1&#xff0e;編程實驗&#xff1a;按short和unsigned short類型分別對-12345進行左移2位和右移2位操作&#xff0c;并輸出結果。2&#xff0e;編程實驗&#xff1a;利用位運算實現BCD碼與十進制數之間的轉換&#xff0c;假設數據類型為unsigned char。3&#xff0e;編…

FPGA | Verilog基礎語法

這里寫自定義目錄標題 Case語句系統任務$dumpfile | 為所要創建的VCD文件指定文件名。$dumpvar | 指定需要記錄到VCD文件中的信號$fscanf$fread菜鳥教程連接 Case語句 case(case_expr)condition1 : true_statement1 ;condition2 : true_stat…

多線程(進階二:CAS)

目錄 一、CAS的簡單介紹 CAS邏輯&#xff08;用偽代碼來描述&#xff09; 二、CAS在多線程中簡單的使用 三、原子類自增的代碼分析 都看到這了&#xff0c;點個贊再走吧&#xff0c;謝謝謝謝謝 一、CAS的簡單介紹 CAS的全稱&#xff1a;“Compare And Swap”&#xff0c;字…

C語言——字符函數和字符串函數(一)

&#x1f4dd;前言&#xff1a; 這篇文章對我最近學習的有關字符串的函數做一個總結和整理&#xff0c;主要講解字符函數和字符串函數&#xff08;strlen&#xff0c;strcpy和strncpy&#xff0c;strcat和strncat&#xff09;的使用方法&#xff0c;使用場景和一些注意事項&…

python常見庫的匯總

python常見庫 一、爬蟲二、界面開發三、圖片處理四、視頻處理、視頻剪輯五、音頻處理六、數據處理七、數據庫八、網頁開發九、神經學習、AI開發十、打包十一、Excel處理十二、微信十三、控制鼠標鍵盤十四、手柄十五、控制外設十六、郵箱十七、短信 一、爬蟲 Requests&#xff…

Java入門項目--螞蟻愛購

簡介 這是一個靠譜的Java入門項目實戰&#xff0c;名字叫螞蟻愛購。 從零開發項目&#xff0c;視頻加文檔&#xff0c;十天就能學會開發JavaWeb項目&#xff0c;教程路線是&#xff1a;搭建環境> 安裝軟件> 創建項目> 添加依賴和配置> 通過表生成代碼> 編寫Ja…

解鎖MySQL的威力:針對常見問題的快速解決指南

數據庫和表的創建 創建數據庫&#xff1a; CREATE DATABASE IF NOT EXISTS MyDatabase; USE MyDatabase;案例&#xff1a; 想象您要開始一個博客項目。首先&#xff0c;您需要一個地方來存儲所有的文章和用戶信息。上述命令幫助您創建了這樣一個存儲空間&#xff0c;名為MyDa…

Tomcat使用https方式連接

Tomcat使用https方式連接 攏共分兩步&#xff0c;第一步&#xff1a;生成密鑰。第二步&#xff1a;修改配置。 第一步&#xff1a;生成密鑰。 keytool -genkey -v -alias tomcat -keyalg RSA -validity 365 -keystore /usr/tomcat-8.5/conf/tomcat.keystore第二步&#xff1…