第二章 通過行爲參數化傳遞代碼

     首先,思考一個問題,比如說一個養豬廠的廠長要你幫忙設計一個程序,統計他豬場內白豬的 數量。你輕蔑的一笑,這很簡單啊,於是你迫不及待的開工了。

先模擬一些豬的數據:

var pigList = new ArrayList<Pig>();
pigList.add(new Pig("黑色", 120));
pigList.add(new Pig("白色", 300));
pigList.add(new Pig("黑色", 208));
pigList.add(new Pig("白色", 188));

篩選白色的豬:

public class PigFilter {

	public static List<Pig> filterWritePig(List<Pig> pigList){
		var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
		for(Pig pig : pigList) {
			if("白色".equals(pig.getColor())) {
				newPigList.add(pig);
			}
		}
		return newPigList;
	}
}

 這樣就大功告成了,簡直小菜一碟,但是,你纔剛得意沒兩天,那個該死的廠長又找到你了,說他現在還想統計一下黑豬的數量,你微微一笑,這也很簡單,於是,你將上面的代碼複製修改了一番再次大功告成!

public static List<Pig> filterBlackPig(List<Pig> pigList){
		var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
		for(Pig pig : pigList) {
			if("黑色".equals(pig.getColor())) {
				newPigList.add(pig);
			}
		}
		return newPigList;
	}

或許這次之後,你就會想到,一旦這廝以後又要統計什麼紅豬黃豬腫麼辦?總不能一直粘貼複製修改吧? 於是你想到了這個辦法,將顏色作爲參數:

public static List<Pig> filterPigByColor(List<Pig> pigList,String color){
		var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
		for(Pig pig : pigList) {
			if(color.equals(pig.getColor())) {
				newPigList.add(pig);
			}
		}
		return newPigList;
	}

事畢之後,你長出了一口氣,默默的點燃了一支菸,不禁被自己的聰明才智打動了 。但是沒過幾天,那個該死的廠長又來找到你,說他又要統計重量爲200斤以上的豬,於是你顫抖着雙手又複製粘貼修改了上面的代碼:

public static List<Pig> filterPigByWeight(List<Pig> pigList,int weight){
		var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
		for(Pig pig : pigList) {
			if(weight<pig.getWeight()) {
				newPigList.add(pig);
			}
		}
		return newPigList;
	}

然後,你痛定思痛,你會想到能不能再增加一個參數,用來篩選條件呢?於是。。。。

 

public static List<Pig> filterPig(List<Pig> pigList,int weight,String color,Boolean flag){
		var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
		for(Pig pig : pigList) {
			if((flag&&weight<pig.getWeight()) || (!flag&&color.equals(pig.getColor()))) {
				newPigList.add(pig);
			}
		}
		return newPigList;
	}

使用:List<Pig> pigs = PigFilter.filterPig(pigList,0,"黑色",true); 

你把改進的代碼交給了廠長,但是廠長抱怨你這個true啊,flase啊是什麼意思?並提出了新的需求,他要統計白豬裏200斤以上的,黑豬裏不足200斤的,豬的不同品種,是公的還是母的。。。。。。。你瞬間奔潰惹。你要添加更多的參數更多的複雜的判斷條件來完成需求 。

      於是,你經過了苦苦的思索,終於,你想到了一種方式。你建了一個接口,該接口是豬的篩選條件建模,他的實現是這樣的,比如一頭豬是黑色的嗎?是返回true不是返回false。這種返回一個boolen值得函數,我們有一個專業的裝B的叫法----謂詞。當然不是一定要使用這所謂的“謂詞”,接口你可以隨心所欲的使用任何返回值類型,謂詞只是這種返回布爾值的特殊叫法而已。

public interface PigPredicate {

	public Boolean test(Pig pig);
}

它的一個實現類,黑豬篩選:

public class PigBlackPredicate implements PigPredicate{

	@Override
	public Boolean test(Pig pig) {
		// TODO Auto-generated method stub
		return "黑色".equals(pig.getColor());
	}

}

然後呢,我們就可以利用這個條件進行黑豬篩選:

public static List<Pig> filterPig(List<Pig> pigList,PigPredicate p){
		var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
		for(Pig pig : pigList) {
			if(p.test(pig)) {
				newPigList.add(pig);
			}
		}
		return newPigList;
	}

我們就可以這麼食用了:

List<Pig> pigs = PigFilter.filterPig(pigList,new PigBlackPredicate());

這樣,你發現你的代碼靈活多了,針對不同的篩選條件,你只需要添加不同的實現類就可以了。像這種你的篩選方法filterPig的行爲取決於你PigPredicate傳遞的代碼的行爲,就叫做行爲參數化!

     但是目前的這種實現方式雖好,卻有兩個問題,第一是你要用到的重要代碼test方法只能包裹在一個對象中(即PigPredicate接口的實現類)進行傳遞 ,第二是你要定義大量的接口實現類,而這些實現類可能你只使用一次而已,爲只使用一次的類而如此大費周章,顯然是有些不值得的。

      針對第二個問題,有的童鞋會說,你可以使用匿名內部類啊!沒錯,下面我們就可以使用匿名內部類,來實現行爲參數化。

List<Pig> pigs = PigFilter.filterPig(pigList,new PigPredicate() {
			
			@Override
			public Boolean test(Pig pig) {
				// TODO Auto-generated method stub
				return 211 == pig.getWeight();
			}
		});

這樣,我們也可以成功篩選體重爲211斤的豬。但是這種匿名內部類還是很羅嗦,看着也很不爽。

那麼爲了改進這個問題,java8引進了lambda表達式,那麼上面的代碼就可以改進爲:

List<Pig> pigs2 = PigFilter.filterPig(pigList,(Pig pig)->211==pig.getWeight());

怎麼樣?是不是很炫酷?(參數—>返回結果)=傳遞的行爲代碼(本例種的test,也就是你定義的篩選條件接口的實現!)。

lambda表達式同樣也解決了上述的第一個問題,就是我們可以將我們的方法直接作爲參數傳遞,而無需用對象進行包裹,使我們的方法成爲了一等值!

     看到這裏,可能有的童鞋會有疑問,如果接口中有多個方法怎麼辦,能使用lambda表達式嗎?答案是否定的,並不是所有接口都可以使用Lambda表達式,只有函數式接口可以。按照Java8函數式接口的定義,其只能有一個抽象方法,否則就不是函數時接口,就無法用Lambda表達式。可以使用@FunctionalInterface標註函數式接口,在編譯時提前發現錯誤。

    java8 API中很多方法都可以用不同的行爲來參數化。比如list.sort排序,線程的創建(Thread t = new Thread(() -> System.out.println("Hello world"));),GUI事件處理等等。

本章小姐姐:

1.行爲參數化,就是一個方法接受多個不同的行爲作爲參數,並在內部使用它們,完成不同行爲的能力。

2.行爲參數化可讓代碼更好地適應不斷變化的要求,減輕未來的工作量。

3.傳遞代碼,就是將新行爲作爲參數傳遞給方法。但在Java 8之前這實現起來很囉嗦。爲接口聲明許多隻用一次的實體類而造成的囉嗦代碼,在Java 8之前可以用匿名類來減少。

4.Java API包含很多可以用不同行爲進行參數化的方法,包括排序、線程和GUI處理。

 

發佈了36 篇原創文章 · 獲贊 15 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章