我们也在非编程领域内耕耘,不过现在绝大部分的所谓非编程产品都只能解决一些基本简单的数据管理应用,所谓CRUD操作;应对复杂应用就远没这么简单;非编程是可能的,但要实现复杂应用远没那么简单;可以看看我们在这方面的努力;www.extraction.cn
试用站点 www.extract.com.cn:888 帐号sysmanager 密码123456

我想稍微完整述说一下我对这样的非MDA代码生成工具(一些也是MDA的问题)认识,有失偏颇权当参考。

要分析这些代码生成工具,就象cats_tiger 分析思路一样,首先要了解我们通用的一些需求:我就暂时以cats_tiger的几个需求为例子,提出我的看法,例如:
>1.报表格式不确定---我通常是给他们培训Excel^_^
使用报表组件,这些组件每天不断出现在本站广告里。这是因为我们将报表从业务逻辑松耦合解放出来的结果。

>2.字段不确定,解决方法同上。
代码生成工具通常引以为豪的功能,其实看似字段修改这样技术问题,其实这涉及业务需求的表达,字段/对象通常是业务需求的落实转换,这需要建模专家和专业人员实现,代码生成工具通常把这个最复杂的事情推给客户,这是严重不对的,使用MDA工具时,字段修改被纳入域建模范围,我域建模是用UML等专业需求分析工具分解出来的,因此操作者只能是建模专家或系统分析师。

>3.查询条件任意组合,这个到可以做,但是查询性能难以保障。
呵呵,这个特点说到Jdon框架的优点,即将到来的jdon框架1.4版本除了对以前的查询优化外,更加增加细粒度的优化,不同查询条件得出的结果中记录总是会有可能重复的,因为用户操作是依据我们的系统提供的步骤逐渐深入操作的,所以后面的操作的数据总会有一部分和前一个步骤有重复(全文检索除外)。
关于这点,我想说的是:通用查询这样的性能优化我们也可以从具体应用中分离出来了,性能可以通过可以重用的框架得到保证了。

从以上几个例子我的分析思路不难看出,我是将一个软件的产生划分为几个步骤:需求分析Use case;域建模;依赖快速开发框架完成模型的CRUD基础功能;反馈给用户确认;根据用户需求修改模型或界面或数据库或逻辑(因为有Spring/jdon这样框架形成松耦合,修改一处不会影响太多无关部分,具有好的可维护和可拓展),再次给用户确认;进入下一个循环。

大家能看出,这个过程是一个敏捷XP软件工程,这一过程是经过我的实践,被证明可以随需而变的。

最后我再次比喻,软件生产也是一个产品生成过程,只要是产品就要有质量要求,如果质量单纯靠人工保证是不行的,必须靠自动化的生产设备,而开发框架和组件或构件库则就是可以保证质量的生产设备;当然只有生产设备也是不行的,必须有良好的产品设计,必须符合客户要求,符合市场需求,那么产品设计就是域建模设计,域建模专家就象工厂的产品设计师或工艺师一样。

那么除了生产设备和产品图纸是不是就可以生长出高质量软件?不是,必须有不同级别软件人员,以及对他们的管理,也即软件工程管理,软件工程管理是和生产设备是互补的,如果生产设备自动化程度高,对人员要求低,管理就相对容易一些。

所以,目前对于软件生产这样一个产业,最重要的是摆脱依赖人工的局面,形成生产设备高度自动化。

而框架或构架库的大力发展,和大家对软件松耦合的重视认识有关,只有进行粒度细化,才能实现分工协作,才能实现大生产。

当然,另外一个极端是必须指出的,就是过分重视生产设备自动化,这就是代码生产工具的产生原因,在目前,第一个阶段:松耦合还没有完成,就想进入共产主义,显然是不客观,是盲目的,是不科学的。
因为有代码生产工具的失败,也会使人怀疑生长设备的大力发展,这又范了另外一个极端,而且确实造成不好的影响。

总结如下:
开发框架和代码生成工具区别:
1.开发框架是基于软件最大追求:松耦合基础上诞生,真正软件质量;而代码生成工具则可能生成好或坏的代码。

2.软件产品生产=产品设计图(域建模)+自动化生产设备(开发框架或组件库)+不同级别的开发人员+软件工程管理
开发框架需要人员参与编程;而代码生成工具夸大生产设备作用,属于共产主义阶段。

量化软件的度是人者见人,智者见智,不能一概而论。

×××××××××××××××××××××××××××

怎样解决GTPlugin 中过多的IF语句

看过GTPlugin 源码的人一定知道在PacketParser.java 中有很多的IF 语句,如下:
public class PacketParser {
private IHandler handler;

public void parser(Packet packet, IGTPlugin dataCallback, GTPluginFacade facade){
...

if (originPacket.startsWith("/google")) {
targetPacket = originPacket.substring(7,originPacket.length());
handler = new GoogleHandler();
}
else if (originPacket.startsWith("/weather")){
targetPacket = originPacket.substring(8,originPacket.length());
handler = new WeatherHandler();
}
else {
targetPacket = originPacket;
handler = new DefaultHandler();
}
...
}

这里IF 语句在我看来已经严重的过多,当新的 Command(Handler) 出现,这里就会多出一个IF语句,如何解决? 你随便的Google 一下,大家都会告诉你用State 设计模式来解决,下面我们先在看看不用State 的解决方法:
1: 在PacketParser 中我主要是解析 Packet 中的文本,所以才造成了过多的IF ,第一个解决方法就是将Packet 的解析交给 Handler 来解决,这样就不会造成If 语句集中在一个地方,只是将IF分散到各个Handler 中去了,所以他也并没有解决我实质的IF 问题, 如下:

public class WeatherHandler implements IHandler {

//原来的方法原形
// public void process(String packet, IGTPlugin dataCallback, GTPluginFacade facade)

//更改后的方法
public void process(Packet packet, IGTPlugin dataCallback, GTPluginFacade facade) {
String origin = null;
if (origin = packet.getBody().toLowerCase().startsWith("/weather")){

dataCallback.sendMessage(facade.getWeatherModule().search( origin ));
}

}

}

再来说说第二种方法
2:就是通过 Java 反射 API 来解决 IF 过多的问题, 如下:

protected void runCommand(String command) {
try {
final Method m = this.getClass().getMethod("onCommand" + command);
m.invoke(this);
} catch (NoSuchMethodException ex) {
onUnknownCommand(command);
} catch (IllegalAccessException ex) {
onUnknownCommand(command);
} catch (InvocationTargetException ex) {
onUnknownCommand(command);
}
}

这样就可以解决IF 过多的问题, 但不用State 设计模式来解决的, 所以我们接下来就看看State 的解决方法。

3: State 设计模式解决 IF 过多的问题 ,先来和大家一起看看State 模式的定义

State模式的定义: 不同的状态,不同的行为;或者说,每个状态有着相应的行为.

何时使用?
State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If elseif else 进行状态切换, 如果针对状态的这样判断切换反复出现,我们就要联想到是否可以采取State模式了.

不只是根据状态,也有根据属性.如果某个对象的属性不同,对象的行为就不一样,这点在数据库系统中出现频率比较高,我们经常会在一个数据表的尾部,加上property属性含义的字段,用以标识记录中一些特殊性质的记录,这种属性的改变(切换)又是随时可能发生的,就有可能要使用State.


是否使用?
在实际使用,类似开关一样的状态切换是很多的,但有时并不是那么明显,取决于你的经验和对系统的理解深度.

OK, 定义也看了,其实在我看来State 在我这里也并不管用,首先是packet 中的命令文本并没有和状态有怎么密切的关联,其次如果我要用state来实现PacketParser.java,那么我将会写出很多的状态子类,其实这一点我有点多虑,其实就是将IHandler 中增加一个状态,只是我想这样的更改对我来说是不是值得,且changeState在我看来也不一定就很好首先他采用 switch 语句来替代所谓的IF ,也不一定就比IF强多少,就是不用Switch 语句,那么也要通过包装到Handler 中来实现,所以综上所述,我的观点是State 并不是什么万能灵药,别人通常所说的多IF 就用State设计模式来解决,这句话在我看来多少有些不负责任,下面给出别人写的State 模式实现,简单阅一遍,也让您有个影响。

===============================

首先看接口类,State.java

public interface State {
public void handle();
}

以定义接口以封装与Conext(代码稍候陈述!)的一个特定状态相关的行为。
然后看,接口的实现类。第一个,ConcreteState1.java


public class ConcreteState1 implements State { public void handle() { System.out.println("ConcreteState1.handle() executing"); }}
第二个,ConcreteState2.java
public class ConcreteState2 implements State { public void handle() { System.out.println("ConcreteState2.handle() executing"); }}
这两个类实现了State接口。
然后再看,Context.java是如何将上述三个.java文件联系起来。


public class Context { public static final int STATE_ONE = 0; public static final int STATE_TWO = 1; //大家注意,这句话很关键,该模式做手脚的地方! private State currentState = new ConcreteState1(); public void request() { currentState.handle(); } public void changeState(int state) { switch (state) { case STATE_ONE: currentState = new ConcreteState1();//关键点 break; case STATE_TWO: currentState = new ConcreteState2();//关键点 break; } }}
这样,写好4个.java文件后,其实您已经实现了State设计模式,很有趣,对吧?就是这样简单。
再看看如何使用该设计模式了。写一个Client.java看看。

public class Client {
public static void main(String[] args) {
// 构造Context
Context ctx = new Context();
// 唤起Context.request()
ctx.request();
// 改变ctx的状态?为什么改变了呢?大家想想看。
ctx.changeState(Context.STATE_TWO);
// 再次唤起Context.request(),结果大不一样。
ctx.request();
}
}



哈哈,zhuam兄举例那个State模式是伪模式。

我们必须理解状态模式是关注了什么、释放了什么,使用状态模式前提是:必须有事件促使状态变化。

试验想想看:事件是由用户触发的,如果发出的事件类型很多,这样状态就有各种变化,但是这些代码都散落在系统各个类中,造成状态切换难以控制,所以必须使用状态模式进行封装,输入信号是事件;输出是状态结果,状态模式状态机就是一个黑盒子。

状态模式举例可见这个网址:
http://www.jdon.com/jive/thread.jsp?forum=91&thread=10981


另外,在使用状态模式时,使用switch等都是伪模式的标记,状态切换是采取偷偷更换同一接口的子类来进行的,这是设计模式的通用规则,上面状态模式的切换代码如下:

currentState = currentState.next(e);
这个就类似 i = i + 1
这样自增一样,这是巧妙的,而不是switch那种伪模式。

我曾经看到电子出版的一本书中,搞电信老手写的OO,它的状态模式中竟然都是if else,这些都是伪模式,没有掌握模式真谛,更担心的是写出来误导初学者啊。

另外,碰到if else很多情况,不一定都使用状态模式,因为使用状态模式你必须有事件,你这个案例没有事件触发,不属于事件机制。

使用状态模式以外设计模式都可以消灭if else,你这个案例适合使用职责链模式来替代if else,你是对来自一个请求对象进行解析,打开看看里面是否有google,如果没有,传到下一个Handler,打开看看有没有weather,所以,你有GoogleHandler/WeatherHandler等几个组成职责链。


banq 兄 说的没错,此处不适合状态模式,switch ,if else 来做状态的切换说做伪码我认为不过分,看起来责任链模式就是为这种程序而诞生的,但责任链模式也没有摆脱过多的if else 命运, 所以我也不怎么感冒,一句话,模式的应用是要看地方的,想消除这样过多的if ,else 我当前还没有找到好的方法,除去 用java反射,不知banq 兄有什么高见啊。

见banq 兄自己的文章
设计模式之Chain of Responsibility(职责链):

http://www.jdon.com/designpatterns/cor.htm

Source Code 见下:


/**
* 这里其实就是 Handler 的 Factory 模式的实现, 根据
* 不同的 Command , Factory 出不同的 Handler ,
* 同时在这里也完成对 Interceptor 的调用, 来达到对Packet
* 的拦截操作
*
* @author zhuam {mail: zhuaming at gmail.com}
*
*/

public class PacketParser {

private IHandler handler;

public void parser(Message packet, IGTPlugin dataCallback, GTPluginFacade facade){

String origiPacket =packet.getBody().toLowerCase();
String targetPacket="";

InterceptorManager.getInstance().invokeInterceptors(packet,true,false);

if (origiPacket.startsWith("/google")) {
targetPacket = origiPacket.substring(7,origiPacket.length());
handler = new GoogleHandler();
}
else if (origiPacket.startsWith("/weather")){
targetPacket = origiPacket.substring(8,origiPacket.length());
handler = new WeatherHandler();
}
else if (origiPacket.startsWith("/help")){
targetPacket = origiPacket.substring(5,origiPacket.length());
handler = new HelpHandler();
}
else if (origiPacket.startsWith("/closegroupchat")){
targetPacket = packet.getFrom();
handler = new CloseGroupChatHandler();
}
else if (origiPacket.startsWith("/opengroupchat")){
targetPacket = packet.getFrom();
handler = new OpenGroupChatHandler();
}
else {
targetPacket = origiPacket;
handler = new DefaultHandler();
}

handler.process(targetPacket, dataCallback, facade);

InterceptorManager.getInstance().invokeInterceptors(packet,true,true);

}

}

这里其实涉及动态代理模式、职责链模式和命令模式以及AOP等设计概念的区别和应用差异,这在我举办的模式框架培训班我都有详细案例分析。

这里我简单分析一下:
>根据不同的 Command , Factory 出不同的 Handler ,
如果客户端发出的Command你是可以控制的,也就是说:google或weather对应哪一个Handler客户端和服务端都是知晓的,那么无疑使用command模式性能最好。

职责链性能最差,是最后无奈情况下选择的方式。

动态代理也就是使用Java反射,其实是命令模式和代理模式的一种结合,它的使用和职责链一样是耗费性能的,除非你有优化措施。

而且动态代理粒度很细,命令模式是假定方法固定,关注在不同类上;而动态代理则是假定方法是变化的,但是类是同一个接口;
因此这两者好像是横向和纵向关注区别。

你这个案例因为需求不是很充分,暂时不能在动态代理和命令模式之间选择。

Hey banq !

我列举上面的例子也就是想说不能一点概面,最起码用state是不能解决我上面的例子, 所以用什么东西,还是要看实际的情况了, 一句话说死所有的东东,那就不一定了,模式能解决一定的问题,但不是所有。

谢了,banq 兄!


-zhuam

呵呵,模式其实是一种设计师语言,具化下来就是UML图,设计师之间交流是不好用代码,就象我们之前的讨论实际是设计师之间讨论,有了这样基于模式语言的讨论,才迫使我们对需求了解更多,软件更加精确反映系统本质。

模式不能解决问题,实际是我们对模式场景研究不够,所以才有dmuyy关于在模式基础上的模式语言讨论:
http://www.jdon.com/jive/article.jsp?forum=91&thread=24018

如果大家都接受模式表达设计的思想,走上这个基本平台,软件质量飞跃就指日可待。

有一种观点:认为模式不是银弹,实际上这两者是风马牛不相及的东西,我们不是用模式解决所有问题,而是用模式思维来解决所有问题。

我增加两个字:模式思维。

真希望越来越多人能明白这个的意思,让dmuyy想法早日在中国实现。

>另外,我们更关心的是:生成后的代码是什么架构?是一种高质量的松耦合架构吗?
没有这么复杂,它是一个“应用系统生产器”,经它生产的系统可以直接投入使用,可以卖钱的。其实不新鲜,很久以前不就有MIS生成器码?这个系统用在中小型项目中,或制作原型是非常方便的。
>图用大一统概念或追求一个神奇的工具将软件开发揉吧揉吧,然后嘴里一吹仙气,一个好的软件系统就从这个软件开发工具中出来了.
哈哈,就是这个样子的工具。他们公司的Sales如此说的。但是我认为它只能在中小型MIS类软件中应用。

zhuam 举的这个例子,最好用command模式,消息服务器用的比较多,这个我前年做了一个,一直在用,很稳定。以前有一哥们,也是选择的命令模式。

需要一个指令对照数据表,集合观察者模式,还可以做到消息的实时通讯。