王朝网络
分享
 
 
 

J2ee程序中的面向对象设计(OO Design Recommendations for J2EE Applications)--摘自ExpertOneOnOneJ2eeDesignAndDev

王朝java/jsp·作者佚名  2006-12-30
宽屏版  字体: |||超大  

面向对象程序设计比任何具体技术都重要。但是在使用种种模式的时候,也要防止过度设计,增加不必要的复杂性、浪费时间和经费。

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() [1])) {

// 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,文档都应该是重构的对象。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有