亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

Lambda 表達式的設計原則

Java 編程最基本的原則就是要追求高內聚和低耦合的解決方案和代碼模塊設計,這里我們主要討論在 Lambda 表達式的環境下來設計我們程序時的兩點原則:單一原則開放閉合原則。

1. 單一原則

程序中的類或者方法只有一個改變的理由

當需要修改某個類的時候原因有且只有一個。換句話說就是讓一個類只做一種類型責任,當這個類需要承當其他類型的責任的時候,就需要分解這個類。 類被修改的幾率很大,因此應該專注于單一的功能。如果你把多個功能放在同一個類中,功能之間就形成了關聯,改變其中一個功能,有可能中止另一個功能,這時就需要新一輪的測試來避免可能出現的問題,非常耗時耗力。

我們來看一個質數統計的例子:

//這是一個違反單一原則的例子  
public SRPDemo{  
	public static long countPrimes(int maxNum){  
		long total = 0;  
		for(int i = 1; i<maxNum;i++){  
			boolean isPrime = true;  
			for( int j=2; j<i;j++){  
				if(i%j ==0){  
					isPrime = false;  
				}  
			}  
			if(isPrime){  
				total = total +1;  
			}  
		}  
		return total;  
	}  
	?  
	public static void main(String ...s){  
		System.out.println(countPrimes(100));  
	}  
}
輸出結果:

26

上面的例子違反了單一原則,一個方法包含了兩重職責:

  • 計數;
  • 判斷是否為一個質數。

我們把上面的代碼重構,將兩種職責拆分到兩個方法中:

//符合單一原則的例子  
public SRPDemo{  
	//計數  
	public static long countPrimes(int maxNum){  
		long total = 0;  
		for(int i= 1;i<maxNum;i++){  
			if(isPrime(i))
				total = total+1;  
		}  
			return total;  
	}  
	//判斷是否為一個質數  
	public static boolean isPrime(int num){  
		for(int i = 2;i<num; i++){  
			if(num%i ==0){  
				return false;  
			}  
		}  
		return true;  
	}  
?  
	public static void main(String ...s){  
		System.out.println(countPrimes(100));  
	}  
}

我們現在使用集合流來重構上面代碼:

public SRPDemo{  
	public static long countPrimes(int maxNum){  
		return IntStream.range(1,maxNum).filter(MultipleInterface::isPrime).count();  
	}  
	public static boolean isPrime(int num){  
		return IntStream.range(2,num).allMatch(x -> num%x != 0);  
	}  
	?  
	public static void main(String ...s){  
		System.out.println(countPrimes(100));  
	}  
}

可見,我們使用集合流在一定程度上可以輕松地幫我們實現單一原則。

2. 開放閉合原則

軟件應該是擴展開放,修改閉合

  • 通過增加代碼來擴展功能,而不是修改已經存在的代碼;
  • 若客戶模塊和服務模塊遵循同一個接口來設計,則客戶模塊可以不關心服務模塊的類型,服務模塊可以方便擴展服務(代碼);
  • 開放閉合原則支持替換的服務,而不用修改客戶模塊。

我們來看一個發送消息的例子,假設我們現在有一個消息通知模塊用來發送郵件和短信:

//這是一個違反開放閉合原則的例子  
public class OCPDemo{  
	//發送郵件  
	public boolean sendByEmail(String addr, String title, String content) {  
		System.out.println("Send Email");  
		return true;  
	}  
	//發送短信  
	public boolean sendBySMS(String addr, String content) {  
		System.out.println("Send sms");  
		return true;  
	}  
}  

想必很多人都會這么寫,這么寫有一個問題,如果哪一天需求變更要求增加微信消息通知,這個時候不僅需要增加一個 sendWechat的方法,還需要在調用它的地方進行修改,所以違反了 OCP 原則?,F在我們來做一下修改:

//一個滿足開放閉合原則的例子  
public class OCPDemo{  
	@Data  
	public static class Message{  
		private String addr;  
		private String title;  
		private String content;  
		private int type;  
	}  
	public boolean send(Message message){  
		switch (message.getType()){  
			case 0: {  
				System.out.println("Send Email");  
				return true;  
			}  
			case 1:{  
				System.out.println("Send sms");  
				return true;  
			}  
			case 2:{  
				System.out.println("Send QQ");  
				return true;  
			}  
			default:return false;  
		}  
	}  
}

我們創建了一個 Message 對象來描述發送消息的所有信息,并增加了一個 type 字段用來區分發送渠道。在遇到類似的情況窩子需要在 send 方法中增加一個對應 渠道類型 type 的處理邏輯就可以了,對存量代碼無需求改。滿足了 OCP 原則。

現在我們再來看下使用函數式接口怎么來優化我們的程序:

@Data  
public class OCPDemo{  
	@Data  
	public static class Message{  
		private String addr;  
		private String title;  
		private String content;  
	}  
?  
	private Message message;  
	?  
	public boolean send(Function<Message , Boolean>  function){  
		return function.apply(message);  
	}  
?  
	public static void main(String ...s){  
		Message message = new Message();  
		message.setTitle("this is a qq msg");  
		OCPDemo demo = new OCPDemo();  
		demo.setMessage(message);  
		demo.send((msg)->{  
			System.out.println("send qq:\t"+msg.getTitle());  
			return true;  
		});  
	}  
}  
輸出:

send qq:  this is a qq msg

此時,我們運用函數接口 Function 來處理 Message,省去了消息類型的判斷,僅當調用的時候決定使用哪種渠道發送,當然我們可以把發送邏輯都寫在一個工具類里面,利用 Lambda 引用來調用。

3. 小結

圖片描述

本節主要討論的是我們如何在我們的程序設計中來使用 Lambda 表達式時所涉及的兩條原則 —— 單一原則開放閉合原則

這里關注的是程序整體,而不是具體的某一個方法。其前提是對于 Lambda 表達式的深度理解和熟練運用,為了說明問題,例子大多相對簡單,想了解更詳細的設計原則還是需要閱讀相關的專著(比如 S.O.L.I.D 原則),并在日常的編碼過程中不斷實踐和思考。