统一对象消息编程(11)—对象消息编程框架应用2

   从现在开始,我们看如何应用前面介绍的对象消息框架。我们不从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、程序固化。如果下次小明老婆没有睡觉,那就要改代码增加判断。在假设过几天小明买了条狗,灯亮后狗叫了,那么又要改代码源代码,增加狗的对象和行为,几天后狗送人了,又要改代码。

所以这种对象方法的调用固化、死板,更重要是不符合现实对象的关系逻辑。现在看看对象消息编程框架如何处理的。首先要建立各个类。

人类,用于建立小明和小明的老婆对象模块

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配置去掉,程序一样运行,就是小明回家开灯上网了。

猜你喜欢

转载自blog.csdn.net/tianlong117/article/details/81482615