本文的上一篇发表之后,承蒙各位网友关注,发表了很多评论,我感觉很多人对我写得文章有误解,大概是我表述不清楚的原因吧。这篇文章是对上一篇的补充,以一个示例阐述了解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的体会系列文章就结束了。感谢各位网友对我写文章的关注。