程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> J2ee程序中的面向對象設計

J2ee程序中的面向對象設計

編輯:關於JAVA

面向對象程序設計比任何具體技術都重要。但是在使用種種模式的時候,也要防止過度設計,增加不必要的復雜性、浪費時間和經費。

1、使用接口降低程序的耦合性(AchIEving Loose Coupling with Interfaces)

雖然面向接口編程回比直接面向具體類編程增加一點點的復雜性,但是帶來的好處卻是巨大的。

1)、可以改變實現而不改變調用的代碼。這使我們可以重新實現程序的一部分,而不用去修改其它部分。

2)、可以自由的實現接口,並促進重用。

3)、必要的時候可以寫出一個簡單的測試實現,或者Stub實現。

2、盡量使用結合,而不是繼承(Prefer Object Composition to Concrete Inheritance)

結合提供了更大的靈活性,它可以在運行時改變程序的行為。Strategy和State模式都是基於這一理論的。

3、模版模式(The Template Method Design Pattern)

模版模式是直接類繼承的一種正確使用。但你知道一些算法的步驟,但是不清楚他們的具體操作就可以使用模版模式,將具體的操作留到以後實現。依賴倒轉 (IOC/Inversion of Control)就是模版模式的一種使用,它通過模版模式,讓框架代碼調用用戶自己的代碼,而不是正常的那種用戶代碼去掉用框架的代碼。模版模式特別適用 於框架設計。

4、策略模式(The Strategy Design Pattern)

策略模式比模版模式稍微復雜了一點,但是提供了更大的靈活性。但存在以下的情況時就應該使用策略模式:

1)、當算法的所有的步驟都可變,而不是僅僅有幾個的時候

2)、當實現具體步驟的類有一個特定的繼承層次的時候

3)、當實現具體步驟的類需要和其他類相關聯的時候

4)、當這些步驟需要在運行時改變的時候

5)、當算法特定步驟的實現會持續增加的時候

5、使用回調增加擴展性(Using Callbacks to AchIEve Extensibility)

回調是策略模式的一種特殊使用,他可以實現特殊的操作,而將錯誤處理、Logging放進框架中。如:

public interface RowCallbackHandler {void processRow(ResultSet rs) throws SQLException;}

public void query(String sql, RowCallbackHandler callbackHandler)

throws JdbcSqlException {

try {

.....

while (rs.next()) {

callbackHandler.processRow(rs);

}

....

} catch (SQLException ex) {

....

} finally {

....

}

}

回調的優點:

1)、可以集中進行錯誤處理和資源的獲取、釋放。這樣可以讓你實現更靈活、更強大的錯誤處理,還不增加工作量。

2)、於底層實現細節無關。

3)、一個工作流程,可以通過不同的實現,完成不同的工作。充分實現了代碼的重用。

回調的缺點:

1)、這種模式,不能讓你直接看到被調用的代碼。這可能讓代碼變得不容易理解和維護。

2)、必須創建一個回調的接受接口或類。

3)、不知道書上再寫什麼。

這個模式特別適用於回調接口非常簡單的情況,最好回調接口只有一個方法,這樣可以通過inner類的形式實現。

6、觀察者模式(The Observer Desing Pattern)

只有當系統中出現了需要了解工作流程的松散監聽者(Loosely coupled Listener)時,才需要使用這種模式。如果我們過度的使用了他,就會導致業務被事件(event)發布的代碼所淹沒。只有重要的工作流程才有可能需 要實現他。監聽者(Listener)必須能夠快速的返回,並且線程安全。不能及時放回的監聽者可能阻塞這個程序。

七、完善方法的參數(Consider Consolidating Method Parameters)

有些時候,我們需要將多個方法參數壓縮到一個對象中,這樣可以簡化代碼。最大的好處就是可以改變方法的參數,但是不用改變調用的代碼,並且能過簡單的實現方法的默認參數。缺點就是可能參數大量的小對象,這些對象將消耗堆的空間。

八、異常捕捉(Exception Handling – Checked or Unchecked Exceptions)

Java將異常分為:Checked exceptions,Unchecked(Runtime) exceptoins。Checked exceptions繼承與java.lang.Exception,不許聲明和捕捉;Runtime exceptions繼承與Java.lang.RuntimeException,不用生命,可以不捕捉。(C++,C#中的異常都相當於 Unchecked Exceptions)

雖然傳統的觀點認為盡量使用Checked Exception,不用Runtime Exception。但是作者不怎麼認為,理由如下:

1)、太多代碼

2)、難於閱讀

3)、無休止的包裝Exception

4)、易變得方法聲明

5)、導致不必要的關聯底層實現。

作者相信,當一個函數返回兩個可選返回值得一個的時候,就需要去判斷,最好編譯器能過強制判斷。那就是使用Checked Exception,其他的情況,作者認為Checked Exception被過分強調了。

注:Checked異常要比使用return codes強,它能夠強迫用戶去捕捉異常。但是Checked Exception不適合用於表示致命的錯誤,和調用者不需要去捕捉的異常。J2EE容器有責任去捕捉並記錄Runtime Exception。

每當需要決定使用哪一種異常的時候,都需要問自己下面的幾個問題:

1)、所用調用者都需要處理這個錯誤嗎?是否異常是作為方法的另一種返回值。

例子:processInvoice()

回答:使用Checked Exception,讓編譯器幫組我們做檢查。

2)、只有少數調用者需要處理這個錯誤嗎?

例子:JDO exceptinos

回答:使用Runtime Exception。讓用戶可以捕捉,也可以不捕捉。

3)、是非常嚴重的異常嗎?是不能恢復的異常嗎?

例子:因連接不上數據庫,導致的業務異常

回答:使用Runtime Exception。調用者除了通知用戶,不能做任何其他有用的事情。

4)、還不清楚?

回答:使用Runtime Exception。讓調用者自己決定是否捕捉。

注:使用Runtime Exception時,最好也聲明異常。

良好的異常捕捉實踐:保存原始異常,重寫getMessage()和printStackTrace()方法。

讓異常包含更多信息:

1)、如果程序需要針對不同的異常作出相應的反應,就需要定義一系列實現統一個接口的異常,在每個類中作特殊的處理。

2)、針對用戶的異常,最好定義一個getErrorCode()的方法,將實際的異常消息放在propertIEs中,這樣方便以後的處理。

3)、異常信息必須盡可能的詳細![WebApplicationContext failed to load config]就不是一個很好的消息,這個消息應該寫成[WebApplicationContext failed to load config from file /WEB-INF/applicationContext.XML': cannot instantiate class ‘com.foo.bar.Magic’ attempting to load bean element with name ‘too’ – check that this class has a public no arg constructor]。難度好像的確很大。

九、使用反射(Using Reflection)

反射可以讓程序在運行時加載、實例化、操作類。反射還可以強化很多設計模式,比如工廠模式(Factory),就沒有必要將類名寫在程序中,可以直接配置到文件中。

反射和Swithches(Reflection and Switches)

public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException {

if (e.getPropertyName() .equals ("email")) {

String email = (String) e.getNewValue();

validateEmail (email, e);

}

...

} else if (e.getPropertyName() .equals ("age")) {

int age = ((Integer) e.getNewValue()).intValue();

validateAge(age, e);

}

...

}

是一個正常的屬性檢查的代碼,但是如果我們需要增加或者刪除一個屬性,我們就需要修改這些判斷,這意味著我們需要重新測試。但是如果我們使用反射,就可以非常優雅的解決怎麼問題

public AbstractVetoableChangeListener() throws SecurityException {

Method[] methods = getClass() .getMethods();

for (int i = 0; i < methods.length; i++) {

if (methods[i] .getName() .startsWith(VALIDATE_METHOD_PREFIX) &&

methods[i] .getParameterTypes() .length == 2 &&

PropertyChangeEvent.class.isAssignableFrom(methods[i].

getParameterTypes() )) {

// We've found a potential validator

Class[] exceptions = methods[i] .getExceptionTypes();

// We don't care about the return type, but we must ensure that

// the method throws only one checked exception, PropertyVetoException

if (exceptions.length == 1 &&

PropertyVetoException.class.isAssignableFrom(exceptions[0])) {

// We have a valid validator method

// Ensure it's Accessible (for example, it might be a method on an

// inner class)

methods[i].setAccessible(true);

String propertyName = Introspector.decapitalize(methods[i].getName().

substring(VALIDATE_METHOD_PREFIX.length()));

validationMethodHash.put(propertyName, methods[i]);

System.out.println(methods[i] + " is validator for property " +

propertyName);

}

}

}

}

public final void vetoableChange(PropertyChangeEvent e)

throws PropertyVetoException {

Method m = (Method) validationMethodHash.get(e.getPropertyName());

if (m != null) {

try {

Object val = e.getNewValue();

m.invoke(this, new Object[] { val, e });

} catch (IllegalAccessException ex) {

System.out.println("WARNING: can't validate. " +

"Validation method "' + m + "' isn't Accessible");

} catch (InvocationTargetException ex) {

// We don't need to catch runtime exceptions

if (ex.getTargetException() instanceof RuntimeException)

throw (RuntimeException) ex.getTargetException();

// Must be a PropertyVetoException if it's a checked exception

PropertyVetoException pex = (PropertyVetoException)

ex.getTargetException();

throw pex;

}

}

}

雖然,使用反射的代碼要比正常的代碼復雜了一點,但是他只需要測試一遍,就可以應對可能的修改,這樣才能說是框架性的代碼!反射是Java的核心API,所以必須掌握。

反射和工廠模式(Reflection and the Factory Design Pattern)

public Object getObject(String classname, Class requiredType)

throws FactoryException {

try {

Class clazz = Class.forName(classname);

Object o = clazz.newInstance();

if (! requiredType.isAssignableFrom(clazz))

throw new FactoryException("Class "' + classname +

"' not of required type " + requiredType);

// Configure the object...

return o;

} catch (ClassNotFoundException ex) {

throw new FactoryException("Couldn't load class "' + classname + ""', ex);

} catch (IllegalAccessException ex) {

throw new FactoryException("Couldn't construct class "' + classname + "': is the no arg constructor public?", ex);

} catch (InstantiationException ex) {

throw new FactoryException("Couldn't construct class "' + classname +

"': does it have a no arg constructor", ex);

}

}

使用: MyInterface mo = (MyInterface)

beanFactory.getObject("com.mycompany.mypackage.MyImplementation",

MyInterface.class);

使用反射時,一般都會導致我們不能確定代碼是否能夠正常被執行,這是就要求我們必須提供更詳細的錯誤信息,以方便以後的處理。

動態代理(Java 1.3 Dynamic ProxIEs)

第11章(Infrastructure and Application Implementation)會詳細介紹,是一個和AOP(ASPect OrIEnted Programming )相關的東西。

十、使用JavaBean取得靈活性(Using JavaBeans to AchIEve Flexibility)

使用JavaBeans可以讓對象很容易的使用代碼之外的配置。不過最好能過使用下面的這些類:

PropertyEditor

PropertyChangeListener

VetoableChangeListener

Introspector

十一、通過程序級的注冊避免單例模式(Avoid a Proliferation of Singletons by Using an Application Registry)

傳統的單例模式有很多的缺點,比如單例類將被硬編碼於代碼中,單例類必須自己配制,復雜的程序可能需要很多單例類,單例類不支持接口,單例類不能被繼承,不能被及時的更新狀態。所有的這一些都大大局限了單例類的使用。

不過通過程序上下文(application context)可以很好的解決這些問題,首先它可以是一個正常的Java類,這就避免了所有上面的問題。他只要在程序初始化時,取得然後在程序的其他地方直接使用。甚至還可以直接配置到JNDI中。

我覺得最大的優點就是他的可以動態配置性,這要比將他硬性寫在代碼中好太多。

十二、重構(Refactoring)

重構不應該僅僅局限於代碼的重構,錯誤消息,log,文檔都應該是重構的對象。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved