本文的上一篇發表之后,承蒙各位網友關注,發表了很多評論,我感覺很多人對我寫得文章有誤解,大概是我表述不清楚的原因吧。這篇文章是對上一篇的補充,以一個示例闡述了解collection框架的重要性。
我在半年以前寫過一個函數printAll(Vector vector),具體代碼如下
import java.util.*;
public class UtilTool
{
public static void printAll ( Vector vector )
{
System.out.println( "the Collection is vector" );
System.out.println( vector.getClass().toString() );
Iterator iterator = vector.iterator();
while ( iterator.hasNext() )
{
System.out.println(iterator.next().toString());
}
}
public static void main( String[] arg )
{
Vector vector = new Vector();
vector.add( new Integer(1));
vector.add( new Integer(2));
vector.add( new Integer(3));
UtilTool.printAll(vector);
}
}
printAll這個函數設計的很不好——不夠通用,假如,還想打印HashSet類型的數據,你就必須重載printAll函數,代碼如下
public static void printAll ( HashSet hashSet )
{
System.out.println( "the Collection is hashSet" );
System.out.println( hashSet.getClass().toString() );
Iterator iterator = hashSet.iterator();
while ( iterator.hasNext() )
{
System.out.println(iterator.next().toString());
}
}
printAll函數的代碼重用率低。其實Vector和 HashSet都是Collection的實現,可以將printAll的參數類型改為Collection,而不必重載。代碼如下
public static void printAll ( Collection collection )
{
System.out.println( "the Collection is collection" );
System.out.println( collection.getClass().toString() );
Iterator iterator = collection.iterator();
while ( iterator.hasNext() )
{
System.out.println(iterator.next().toString());
}
}
這樣就可以刪除printAll(Vector vector)和printAll(HashSet hashSet)函數了。
在設計函數時,應優先使用接口,而不是類。當然必須了解Vector 是Collection的實現。
如果對Collection的繼承關系不清楚,很容易濫用重載,以下代碼是一個有問題的代碼(摘自Effective Java Programming Language Guide)
public class CollectionClassifier{
public static String classify(Set s){
return "Set";
}
public static String classify(List l){
return "List";
}
public static String classify(Collection c){
return "Unknow Collection";
}
public static void main( String[] args )
Collection[] tests = new Collection[]{
new HashSet(),
new ArrayList(),
new HashMap().values()
}
for(int i=0;i<tests.length;i++)
System.out.println(classify(test[i]));
}
}
程序輸出的是三次"Unknown Collection",而不是你期望的打印"Set","List"以及"Unknown Collection"。這個程序錯誤的根源是對Collection層次結構不熟悉,而濫用重載導致。
這篇文章仍然是對《我學習使用java的一點體會(上)》的補充。
我使用java開發一年多,使用的應該還算熟練,最近在閱讀《設計模式》和《Effective Java》時,又重新學了一下java的基本類庫,對編程思想有了新的認識。java的基本類庫是由專家設計的,理解基本類庫一方面可以增加自己的開發效率,另外一方面可以學學專家的設計思路。在java的基本類庫中,使用了很多的設計模式,在很多方面提供擴展機制,方便的支持設計模式。可以說java的基礎類庫,將面向對象設計的Open-Close principle (Software entities should be open for extension,but closed for modification)發揮到了極致。
在java的基礎類庫中,有些類設計的是為了給java開發者提供工具,直接讓開發者使用的,有些類是專門為繼承而設計的。對于第一種類型的類,使用集成開發工具很容易就能上手使用,而對于第二種類型的類,不主動去學它的API,很難掌握它的使用。我舉一個例子。java 2 提供了對Proxy模式的支持,在以下示例中,演示了如何使用代理模式(摘自《java與模式》)。主要體會java.lang.reflect.InvocationHandler的用法
package com.javapatterns.proxy.reflect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Method;
import java.util.Vector;
import java.util.List;
public class VectorProxy implements InvocationHandler
{
private Object proxyobj;
/** @link dependency */
/*#Proxy lnkProxy;*/
public VectorProxy(Object obj)
{
proxyobj = obj;
}
public static Object factory(Object obj)
{
Class cls = obj.getClass();
return Proxy.newProxyInstance( cls.getClassLoader(),
cls.getInterfaces(),
new VectorProxy(obj) );
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
System.out.println("before calling " + method);
if (args != null)
{
for (int i=0; i<args.length; i++)
{
System.out.println(args[i] + "");
}
}
Object o = method.invoke(proxyobj, args);
System.out.println("after calling " + method);
return o;
}
public static void main(String[] args)
{
List v = null;
v = (List) factory(new Vector(10));
v.add("New");
v.add("York");
}
}
現在應該說一說設計模式的學習了。設計模式是一個高度抽象的概念,每一種模式都是被抽象化了的解決某一問題的成功經驗,理解設計模式需要洞察力,因而學習設計模式前,最好有一定的經驗,不然很難體會設計模式的精髓。
寫設計模式方面的文章對我來說困難重重,首先由于經驗、水平的限制,對設計模式的理解還沒有達到那個高度,其次設計模式文章很多,從簡單介紹,到深入討論都有,很難再有新的視角討論。我今天就簡單介紹一下我讀過的兩本設計模式的書,從我對這兩本書的理解來介紹一下設計模式。
設計模式方面的書、資料很多,我讀過兩本,《設計模式》和《java與模式》,下面分別介紹。
《設計模式》就是常提及的Gof寫的那本,是使用模式化方法研究的開創性著作《Design Patterns Elements of Reusable Object-Oriented Software》的中譯本。無論如何這本書都稱得上是經典,值得收藏,可以隔一段時間拿出來品味一番。全書共6章,第一章引言,介紹了關于設計模式的基本概念,以及如何閱讀這本書。第二章介紹了一個應用了多個設計模式的實例。第三章討論了5個創建型模式,第四章討論了7個結構型模式,第五章討論了11個行為模式。第六章是結論。本書提及的23個設計模式,也是模式中的經典,在新創建的設計模式中,很大部分是這23個模式中的變體。但這本書晦澀難懂也同樣出名:
- 這本書介紹設計模式的對象描述語言,不是我們現在通用的UML,學習時,需要理解這種對象描述語言。
- 這本書示例實現語言是smalltalk和C++,學習這本書時要熟悉這兩種語言,而C++本身就是一個非常難的語言。
我認為這本書不適合初學設計模式的人,尤其對于java開發人員。
《java與模式》是對《設計模式》在java這方面做的簡單演繹。全書共55章,前兩章介紹一些基礎知識,包括UML的基本知識,接下來9章介紹面向對象的編程原則,剩余44章,討論了26個設計模式,18個主題。這18個主題是設計模式在應用中的一些示例,有些是作者的經驗,有些是廣泛應用的成功示例。
《java與模式》是針對java語言的,因而示例都是用java實現的。本書提供大量的簡單示例,簡單的示例易于理解,便于以后使用模式時模仿。而在主題部分討論的問題又很深刻,體現了作者對軟件設計的深刻理解,是作者對設計模式的一個演繹。雖然這門書相對簡單,我也是讀了3遍之后,才體會出這本書的味道。以下是我閱讀這本書的一點心得,我閱讀了三遍,就將每一遍我理解了什么說一說。
- 第一次閱讀:本書的前兩章是基礎的概念,自然要先了解;而面向對象的原則部分,需要一定的體會之后才能深刻理解,因而第一次讀時,能理解多少就理解多少,不必深究;對于設計模式的閱讀,至少要熟悉每個模式的uml圖以及這個模式的示例代碼;主題是第一次閱讀的重點,這些主題是應用設計模式的范例,應該主要理解。
- 第二次閱讀,要明白面向對象的原則說的是什么;重點研究每一個設計模式,理解適用范圍,理解優缺點,以及模式和模式的比較,在每個設計模式中如何體現面向對象的設計原則。
- 第三次閱讀,重點是體會面向對象的原則。
設計模式的學習不是一個簡單的過程,需要反復學習,不斷實踐。
前面的系列文章,只是我個人的體會,文章有些教條,僅希望能給還在java門外的人一點意見。學習、應用java的方向,我推薦看一下《談java的學習方向?》,這篇文章寫的不錯(見http://www.csdn.net/develop/Read_Article.asp?Id=21393),我學習、應用java的經歷和這篇文章介紹的大同小異,所推薦的書籍和資料我也大部分都讀過了(差別是我沒有讀Oreilly公司的書,java 入門我讀的是《java編程思想》和《the Complete Reference Java 2》兩本java的經典教材,另外我也沒有仔細研究過Specification)。如果將自己定位于j2ee方向,那么作者推薦的資料無疑相當不錯,同時也比較全面。但我覺得也有一點缺憾:應該在某一個階段學習一下設計模式和重構,畢竟設計模式和重構是面向對象開發的兩本經典,而java是一個純粹的面向對象的語言,在這里我只想再推薦幾本書。
- 《java編程思想》是一本經典的java入門教程,在介紹語言的同時,也介紹了面向對象編程的一些思想。這本書是我學習java看的第一本書,我直到現在還經常翻開這本書,從中仍能找到一些以前沒有深刻理解的內容,值得去品味。
- 《the Complete Reference Java 2》既是一本java的入門書籍,又是java的參考書籍,現在coding時,我仍然要經常參考這本書。
- 《java與模式》是向java開發人員介紹設計模式的書,在閱讀這本書時,讓我去回味以前做過得項目,體會以前做過的項目設計的成功與失敗的地方,同時也促使我重新去閱讀《java編程思想》和《the Complete Reference Java 2》兩本書,閱讀3遍之后,加深了對oop的理解。
- 《重構——改善既有代碼的設計》,曾經有一個讓我非常佩服的項目經理,指點我說一個項目結束后,將這個項目的源代碼,重新閱讀、清理、總結一下,是提高編程水平的一個手段,而我在以后的工作中,經常清理自己以及別人的一些垃圾代碼,確實對編程水平的提高有很大的好處。而《重構》這本書,講解了70多種清理、重構代碼的方法,依照重構的方法去做,既能提高代碼質量,又能提高編程水平,也是體會設計模式的一種手段。
到此,我對java的體會系列文章就結束了。感謝各位網友對我寫文章的關注。