从现在开始,我们看如何应用前面介绍的对象消息框架。我们不从helloworld、hellojava开始,我认为它们体现不了对象的本质。我们从hello小明开始。现在假设一个场景,小明下班回家,进屋后打开了灯,屋子亮了,小明开始上网,而这时正在睡觉的小明老婆醒了,老婆很生气的说“关灯,我在睡觉呢”。对于这个场景,如果用传统的编程方式,我确实不知道如何实现,如果是这样的:
new xiaoming =person()
xiaoming.comein()
new light=light()
light.on();
new house =house()
house.light()
xiaoming.getweb()
new wife=person()
wife.say()
从结果输出上确实实现了场景。但是有几个问题:
1、对象之间没体现逻辑关系。所谓的对象逻辑关系在设计者的头脑里,而不是在程序中对象中自身体现。这是最重要的问题。
2、程序固化。如果下次小明老婆没有睡觉,那就要改代码增加判断。在假设过几天小明买了条狗,灯亮后狗叫了,那么又要改代码源代码,增加狗的对象和行为,几天后狗送人了,又要改代码。
所以这种对象方法的调用固化、死板,更重要是不符合现实对象的关系逻辑。现在看看对象消息编程框架如何处理的。代码在tlobjdemo.person包下。首先要建立各个类。这里先不分析类的代码,后面会看到逻辑关系。
人类,用于建立小明和小明的老婆对象模块
public class Person extends MyModule {
boolean sleep;
public Person(String name) {
super(name);
}
public Person(String name, TLObjectFactory moduleFactory) {
super(name, moduleFactory);
}
@Override
protected void init() {
sleep = false;
//注册到广播接收者
putMsg("broadcast", createMsg().setAction("registReceiver")
.setParam("msgId", "house").setParam("receiver", this));
TLMsg msg1 = new TLMsg().setAction("house");
msgTable = new HashMap<>();
addMsg(msgTable, "house", msg1, -1);
}
@Override
protected TLMsg checkMsgAction(Object fromWho, TLMsg msg) {
switch (msg.getAction()) {
case "house":
house(fromWho, msg);
break;
case "sleep":
sleep(fromWho, msg);
break;
case "comein":
comein(fromWho, msg);
break;
case "web":
web(fromWho, msg);
break;
default:
}
return null;
}
private void comein(Object fromWho, TLMsg msg) {
System.out.println(name + "说:我回家了,开灯啦 ");
putMsg("light", new TLMsg("on"));
}
private void sleep(Object fromWho, TLMsg msg) {
sleep = true;
}
private void house(Object fromWho, TLMsg msg) {
String housestatus = (String) msg.getParam("status");
String response = "(" + name + " 进程id: " + Thread.currentThread().getName() + ")";
if (housestatus.equals("light")) {
if (sleep == true)
System.out.println(name + "喊:关灯,我在睡觉呢。 " + response);
else {
System.out.println(name + "说:屋子亮啦,回家的感觉真好。 上上网吧 " + response);
webclient("http://www.baidu.com");
}
}
}
private void webclient(String url) {
TLMsg msg = new TLMsg();
msg.setAction("get")
.setMsgId("webServer")
.setParam("url", url)
.setParam("resultFor", this)
.setParam("resultAction", "web");
putMsg("appCenter", msg);
String action = name + " 打开:" + url;
System.out.println(action);
}
private void web(Object fromWho, TLMsg msg) {
String response = (String) msg.getParam(WEBRESPONSE);
System.out.println(response);
}
}
在 Person类中,定义了进屋、睡觉、上网、屋子亮几个行为。
灯类:
public class Light extends MyModule {
public Light (String name ){
super(name);
}
public Light (String name,TLObjectFactory moduleFactory) {
super(name,moduleFactory);
}
@Override
protected TLMsg checkMsgAction(Object fromWho, TLMsg msg) {
switch (msg.getAction()){
case "on" :
on( fromWho ,msg) ;
break ;
case "off" :
off(fromWho ,msg);
break ;
default : System.out.println("no action");
}
return null;
} ;
private void on (Object fromWho, TLMsg msg) {
System.out.println("灯 打开 " );
putMsg("house",new TLMsg("light"));
}
private void off(Object fromWho, TLMsg msg ) {
System.out.println("灯关上 " );
putMsg("house",new TLMsg("dark"));
}
}
灯只有两个行为,打开、关上。
屋子类:
public class House extends MyModule {
public House (String name ){
super(name);
}
public House (String name,TLObjectFactory moduleFactory) {
super(name,moduleFactory);
}
@Override
protected TLMsg checkMsgAction(Object fromWho, TLMsg msg) {
switch (msg.getAction() ){
case "light" :
light( fromWho ,msg) ;break ;
case "dark" :
dark( fromWho ,msg) ;break ;
default :
}
return null;
}
private void light (Object fromWho, TLMsg msg) {
System.out.println("屋子亮了" );
toperson("light") ;
}
private void dark (Object fromWho, TLMsg msg) {
System.out.println("屋子黑了 " );
toperson("dark") ;
}
private void toperson(String status) {
TLMsg bmsg=createMsg().setDestination("broadcast").setAction("broadcast")
.setParam("msgId","house").setParam("status",status);
putMsg("broadcast",bmsg);
}
}
屋子只有两个行为 变亮 、变暗。
程序主模块appCenter:
public class MyAppCenter extends APPCenter {
public MyAppCenter(String name ){
super(name);
}
public MyAppCenter (String name,TLObjectFactory moduleFactory) {
super(name,moduleFactory);
}
@Override
protected TLMsg mycheckMsgAction(Object fromWho, TLMsg msg) {
return null;
}
}
前面介绍过,主模块appCenter主要负责程序的初始化,该案例中没有定义其他行为。
程序入口main:
public class Main {
public static void main (String[] args ) {
String configdir =System.getProperty("user.dir")+"\\config\\person\\";
Myfactory myfactory= Myfactory.getInstance(configdir,null);
TLMsg msg = new TLMsg()
.setAction(TLObjectFactory.FACTORY_GETMODULE)
.setParam(TLObjectFactory.FACTORY_MODULENAME, "appCenter");
MyAppCenter appCenter = (MyAppCenter) myfactory.putMsg(myfactory, msg)
.getParam(TLObjectFactory.FACTORY_MODULEINSTANCE);
}
}
入口main 首先模块工厂实例化,工厂启动appCenter模块,到此程序为止了,没有new person 、new house,没有小明进屋,很奇怪。现在我们看看运行结果,为方便看,我把日志输出代码屏蔽掉了。
很自然的实现了我们要的场景,怎么会这样呢?我们看看前面介绍的主配置文件 appConfig.xml,其中有下面的配置:
<msgTable>
<msgid value="setup" >
<msg action="getModule" destination="moduleFactory" moduleName="wife"/>
<msg action="getModule" destination="moduleFactory" moduleName="xiaoming"/>
</msgid>
</msgTable>
<initMsg>
<msg msgid="setup" />
</initMsg>
<initMsg>消息表设置了appCenter模块启动时处理的消息。这里有一条msgid="setup",这个消息没有设置action,只设置了消息id。当执行这消息时,对于设置了消息ID的消息,模块到消息路由表里面查找对应的消息。在消息路由表<msgTable>中,对应消息id="setup"的消息有两条消息,然后appCenter模块依次执行这两条消息。这两条消息的目的是模块工厂,action是"getModule",也就是传递消息给工厂分别创建模块 wife和小明。这样在appCenter模块初始化时自动创建了小明和小明老婆模块。
小明的进屋动作呢,看小明的配置xiaoming_config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<moduleConfig>
<params>
<name value="demo"></name>
<verison value="1"></verison>
</params>
<initMsg>
<msg action="comein" ></msg>
</initMsg>
</moduleConfig>
小明初始化后,自动的执行初始消息“comin”,完成进屋的行为。这个行为是小明自行完成的,而不是被其他模块调用,这就完全符合前面说的事物的本质----行为要由对象自己执行。小明进屋后干什么呢,来看comin行为,在person类中:
private void comein(Object fromWho, TLMsg msg) {
System.out.println(name + "说:我回家了,开灯啦 ");
putMsg("light", new TLMsg("on"));
}
小明进屋后说话,然后打开灯,所谓打开灯就是给灯发出打开的消息。看灯的on行为:
private void on (Object fromWho, TLMsg msg) {
System.out.println("灯 打开 " );
putMsg("house",new TLMsg("light"));
}
灯打开后,给屋子发出亮的消息。灯打开并不等于屋子就亮,小明老婆醒来是因为屋子亮,而不是灯打开。看屋子亮的行为:
private void light (Object fromWho, TLMsg msg) {
System.out.println("屋子亮了" );
toperson("light") ;
}
private void dark (Object fromWho, TLMsg msg) {
System.out.println("屋子黑了 " );
toperson("dark") ;
}
private void toperson(String status) {
TLMsg bmsg=createMsg().setDestination("broadcast").setAction("broadcast")
.setParam("msgId","house").setParam("status",status);
putMsg("broadcast",bmsg);
}
屋子亮后,发出亮的消息。但这里没有单独给小明的老婆发送消息,因为屋子亮消息是被屋内所有人接收的,屋子亮的消息是一种广博消息,因此通过一个消息广播类broadcast发出。那么小明、小明老婆是怎么接收这消息呢。看person代码:
@Override
protected void init() {
sleep = false;
//注册到广播接收者
putMsg("broadcast", createMsg().setAction("registReceiver")
.setParam("msgId", "house").setParam("receiver", this));
TLMsg msg1 = new TLMsg().setAction("house");
msgTable = new HashMap<>();
addMsg(msgTable, "house", msg1, -1);
}
在person对象初始化时,给广播模块注册了一条消息,告诉广播模块接收对于消息id为house的消息。同时在消息路由表中增加了house消息的处理方式。这样当house发出亮的广播消息时,小明和老婆都接收到了消息。person接收消息代码:
private void house(Object fromWho, TLMsg msg) {
String housestatus = (String) msg.getParam("status");
String response = "(" + name + " 进程id: " + Thread.currentThread().getName() + ")";
if (housestatus.equals("light")) {
if (sleep == true)
System.out.println(name + "喊:关灯,我在睡觉呢。 " + response);
else {
System.out.println(name + "说:屋子亮啦,回家的感觉真好。 上上网吧 " + response);
webclient("http://www.baidu.com");
}
}
}
如果人的状态是sleep,也就是小明的老婆,喊关灯。对于小明,状态不是sleep,他上网。小明的老婆的状态也是在配置文件中wife_config.xml初始化的:
<?xml version="1.0" encoding="UTF-8" ?>
<moduleConfig>
<params>
<name value="demo"></name>
<verison value="1"></verison>
</params>
<initMsg>
<msg action="sleep" ></msg>
</initMsg>
</moduleConfig>
从上面程序运行分析看出,对象的关系逻辑完全在事物本质的关系中体现,没有强行干预、控制,一切自行运转。如果小明的老婆没有睡觉,那么改变初始化消息就行。像前面假设的情况,如果这时候家里有条狗,灯亮了狗叫,我们不用更改现有任何代码,只要增加一个狗对象,注册到广播类里接收消息,在主程序配置中增加狗的工厂创建就行。
在person的代码中,为了方便,将上网行为写到house方法里面去了,如果抽出单独写一个行为,将该行为通过配置加在house信息后向消息执行表中,如下:
<afterMsgTable>
<action value="house" >
<msg action="web" />
</action>
</afterMsgTable>
通过这个配置,则可灵活通过配置当屋子亮后小明的行为,如果不是上网而是做家务,只需更改配置消息的action。再假设在小明进屋前想起老婆要他下班买的东西,如果买就进屋,如果没有就不进屋而去买东西。这样修改person对象增加一个think方法。但这个方法什么时候调用呢,放哪里呢,因为不是每次小明进屋都要想是否买东西,如果老婆出差去了,他就不用想,所以这个方法放到comein里面判断肯定不对,而且comein是已经进屋后的行为,think方法必须在comein之前判断。这时候我们就可以通过配置实现;
<beforeMsgTable>
<action value="comein" >
<msg action="think" ></msg>
</action>
</beforeMsgTable>
在消息前期执行表里面增加think消息,当执行comein行为的时候,先执行think行为,根据think行为返回的结果而决定是否执行comein,如果返回结果参数DONEXTMSG为false,则不执行。如果不需要这个行为了,从配置删除就可以。
在代码中,我们发现任何两个模块或对象的互动仅仅是通过一个方法putMsg,没有对象其他方法的调用,任何对象是一个黑盒。同时对于接收到的消息,如果没有对应的执行行为则丢弃,不会影响接收者。对象之间是通过名字标识彼此,方便理解、消息传送。
通过小明这个场景,可以看到对象消息编程的灵活,定制方便,这里的每个类基本都是独立的,可以随意装卸,例如如果把初始wife配置去掉,程序一样运行,就是小明回家开灯上网了。