• ZK官方MVC原理详解(一)


    作者:Henri Chen, Principal Engineer

    ZK的团队一直试图在zk中实现MVC方法最佳实践,在这篇文章中我们将会讨论zk中MVC编程,我们还详细的介绍在zk中提供三个工具类和三个帮助方法,将为开发者在程序中编写Model-View-Controller模式更方便、更简单。此外、还将最初将Java写在zscript中,提供了最佳途径来重构原型代码。

    我将使用一个简单的例子来说明这些工具类和帮助方法实力。

    假设:两个文本框:一个是姓、一个是名,当这两个文本框变化时候而另外一个全名要自动更新。

    第一种方式:

    composer1.zul

    <window title="composer1 example" border="normal" width="300px" apply="MyComposer1">
        <grid>
            <rows>
                <row>First Name: <textbox id="firstName" forward="onChange=onFirstName"/></row>
                <row>Last Name: <textbox id="lastName" forward="onChange=onLastName"/></row>
                <row>Full Name: <label id="fullName"/></row>
            </rows>
        </grid>
    </window>
    MyComposer1.java
    public class MyComposer1 implements Composer {
        private Textbox firstName;
        private Textbox lastName;
        private Label fullName;
        
        public void doAfterCompose(Component win) throws Exception {
            firstName = (Textbox) win.getFellow("firstName");
            lastName = (Textbox) win.getFellow("lastName");
            fullName = (Label) win.getFellow("fullName");
    
            //定义和监听事件登记 define and register event listeners
            win.addEventListener("onFirstName",
                new EventListener() {
                    public void onEvent(Event event) throws Exception {
                        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
                    }
                });
            win.addEventListener("onLastName",
                new EventListener() {
                    public void onEvent(Event event) throws Exception {
                        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
                    }
                });
        }
    }
    
    在这里composer1.zul定义了程序的页面视图部分,而MyComposer1.java在程序生命周期上window创建时候介入,主要负责负定义和注册监听事件。然而,不管怎么样!
    当我添加更多事件代码时候,我们会不停的添加addEventListener这样重复的代码。但是我们怎么去除这烦人的代码呢?
     org.zkoss.zk.ui.util.GenericComposer 辅助类帮助我们解决这个问题
     
    composer2.zul
    <window title="composer2 example" border="normal" width="300px" apply="MyComposer2">
        <grid>
            <rows>
                <row>First Name: <textbox id="firstName" forward="onChange=onFirstName"/></row>
                <row>Last Name: <textbox id="lastName" forward="onChange=onLastName"/></row>
                <row>Full Name: <label id="fullName"/></row>
            </rows>
        </grid>
    </window
     
    MyComposer2.java
    
    ...
    public class MyComposer2 extends GenericComposer {
        private Textbox firstName;
        private Textbox lastName;
        private Label fullName;
        
        public void doAfterCompose(Component win) throws Exception {
            super.doAfterCompose(win);
      
            //得到zk组件
            firstName = (Textbox) win.getFellow("firstName");
            lastName = (Textbox) win.getFellow("lastName");
            fullName = (Label) win.getFellow("fullName");
      
            //all addEventListener and new EventListener() codes are removed
        }
        
        public void onFirstName(Event event) { 
            fullName.setValue(firstName.getValue()+" "+lastName.getValue());
        }
        
        public void onLastName(Event event) {
            fullName.setValue(firstName.getValue()+" "+lastName.getValue());
        }
    }
    该MyComposer2类继承GenericComposer,写事件侦听器的代码直接写onXxx,对GenericComposer类doAfterCompose方法会为你做的繁琐的addEventListener
    按照惯例,这是一个典型的Design by Convention方法,与onXxx命名模式的所有方法将会自动被视为事件处理,而且自动帮你注册这些事件。

    对于一个典型的互动丰富的应用程序,事件处理中通常需要访问数据模型bean和操纵UI组件与最终用户交互。获得这样的组件引用和数据bean是在事件处理常见的做法。所以为什么在MyComposer2中大量使用getFellow()方法,另一方面,这些都是在一个框架可以发挥作用,为什么不把这些组件和这些数据beans绑定呢?

      因此,这里来了org.zkoss.zk.ui.util.GenericAutowireComposer 工具类扩展GenericComposer类并添加了auto-wire的特点。

    composer3.zul

    <window title="composer3 example" border="normal" width="300px" apply="MyComposer3">
        <grid>
            <rows>
                <row>First Name: <textbox id="firstName" forward="onChange=onFirstName"/></row>
                <row>Last Name: <textbox id="lastName" forward="onChange=onLastName"/></row>
                <row>Full Name: <label id="fullName"/></row>
            </rows>
        </grid>
    </window>

    MyComposer3.java

    public class MyComposer3 extends GenericAutowireComposer {
        private Textbox firstName; //auto-wired
        private Textbox lastName; //auto-wired
        private Label fullName; //auto-wired
    
        //all getFellow() codes are removed
    
        public void onFirstName(Event event) { 
            fullName.setValue(firstName.getValue()+" "+lastName.getValue());
        }
        
        public void onLastName(Event event) {
            fullName.setValue(firstName.getValue()+" "+lastName.getValue());
        }
    }
    
    在来看看MyComposer3中它继承了GenericAutowireComposer ,所有的getFellow方法都去除掉了。

    对GenericAutowireComposer类doAfterCompose方法为你自动注入适当的组件,

    事实上,如果在你的zul文件中你定义适当的变量解析器(variable resolver),例如Spring变量解析器(variable resolver),则GenericAutowireComposer也会为你的Spring bean注入

    以下是这的一些片断代码。讨论的ZK +spring请参考另一篇文章。

    spring-config.xml

    ...
    <bean id="taskDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    ...
    
    taskEditor.zul
    <?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
    <window id="taskWnd" title="Task Editor" border="normal" width="500px" apply="TaskEditorComposer">
        <grid>
            <rows>
                <row>Title: <textbox id="title"/></row>
                <row>Description: <textbox id="description"/></row>
            </rows>
        </grid>
        <button id="saveBtn" label="Save" forward="onClick=onSaveTask"/>
    </window>
    TaskEditorComposer.java
    public class TaskEditorComposer extends GenericAutowireComposer {
        private TransactionProxyFactoryBean taskDao; //Spring bean auto wired
        private Textbox title; //auto wired
        private Textbox description; //auto wired
        private Button saveBtn; //auto wired
        
        public void onSaveTask(Event event) { 
            Task currentTask = componentScope.get("task");
            currentTask.setTitle(title.getValue());
            currentTask.setDescription(description.getValue());        
            taskDao.update(currentTask);
        }
        ...
    }

    由于在zscript中我们可以直接使用隐式对象,同样的GenericAutowrieComposer 也支持隐式对象甚至zscript中的alert() 方法也支持。下面composer4就是个例子,当firstName变化时,它就会请求self(应用composer时候的隐式对象,这里也就window实例)
    改变他的标题并且弹出alert消息框,这也带来另外一个好处,为zscript代码重构到java代码上来提供更方便更轻松的途径。你可以复制和粘贴,并做一些轻微的修改,即使你已经用了一些这样用来隐式对象存在只在zscript。查看一下composer4-1.zul使用zul和zscript,在看看MyComposer4.java中的onFirstName() 和onLastName()方法有什么类似吗?

    composer4.zul
    
    <window title="composer4 example" border="normal" width="300px" apply="MyComposer4">
        <grid>
            <rows>
                <row>First Name: <textbox id="firstName" forward="onChange=onFirstName"/></row>
                <row>Last Name: <textbox id="lastName" forward="onChange=onLastName"/></row>
                <row>Full Name: <label id="fullName"/></row>
            </rows>
        </grid>
    </window>
    MyComposer4.java
    
    public class MyComposer4 extends GenericAutowireComposer {
        private Textbox firstName;
        private Textbox lastName;
        private Label fullName;
        
        public void onFirstName(Event event) { 
            fullName.setValue(firstName.getValue()+" "+lastName.getValue());
            ((Window)self).setTitle("First Name changed");
            alert("First Name changed to "+firstName.getValue());
        }
        
        public void onLastName(Event event) {
            fullName.setValue(firstName.getValue()+" "+lastName.getValue());
            ((Window)self).setTitle("Last Name changed");
            alert("Last Name changed to "+lastName.getValue());
        }
    }
    composer4-1.zul (quick prototyping)
    
    <window title="composer4-1 example" border="normal" width="300px">
        <attribute name="onFirstName"><![CDATA[
             fullName.setValue(firstName.getValue()+" "+lastName.getValue());
            ((Window)self).setTitle("First Name changed");
            alert("First Name changed to "+firstName.getValue());
        ]]></attribute>
        
        <attribute name="onLastName"><![CDATA[
             fullName.setValue(firstName.getValue()+" "+lastName.getValue());
            ((Window)self).setTitle("Last Name changed");
            alert("Last Name changed to "+lastName.getValue());
        ]]></attribute>
        
        <grid>
            <rows>
                <row>First Name: <textbox id="firstName" forward="onChange=onFirstName"/></row>
                <row>Last Name: <textbox id="lastName" forward="onChange=onLastName"/></row>
                <row>Full Name: <label id="fullName"/></row>
            </rows>
        </grid>
    </window>

    知道现在!在前面几个例子中zul文件中总是使用forword来分别触发window中firstName和lastName文本框中OnFirstName和OnLastName事件来转向他们的onChang事件,毫无疑问!这个MVC使得每个组件重复forward,我们还能不能消除forward的属性呢?

    org.zkoss.zk.ui.util.GenericForwardComposer 这个工具类满足这一需求,请注意,GenericForwardComposer继承GenericAutowireComposer类,因此它有它的父类(GenericAutowireComposer)和祖父类(GenericComposer)所有功能

    composer5.zul

    <window title="composer5 example" border="normal" width="300px" apply="MyComposer5">
        <grid>
            <rows>
                <row>First Name: <textbox id="firstName"/></row><!-- forward is removed -->
                <row>Last Name: <textbox id="lastName"/></row><!-- forward is removed -->
                <row>Full Name: <label id="fullName"/></row>
            </rows>
        </grid>
    </window>
    MyComposer5.java
    
    public class MyComposer5 extends GenericForwardComposer {
        private Textbox firstName;
        private Textbox lastName;
        private Label fullName;
        
        //onChange event from firstName component
        public void onChange$firstName(Event event) { 
            fullName.setValue(firstName.getValue()+" "+lastName.getValue());
        }
        
        //onChange event from lastName component
        public void onChange$lastName(Event event) {
            fullName.setValue(firstName.getValue()+" "+lastName.getValue());
        }
    }
    当我们写composer继承于GenericForwardComposer的时候,你必须按照一些命名规则来定义你的事件侦听器方法,
    这是另一种Design by Convention模式实现。例如MyComposer5.class,被命名onchang$firstName事件监听读成为“onChange event from the firstName component”于firstName组件中的onChang事件

    GenericForwardComposer会帮助你forward事件到应用组件,正如你可以看到,forword在compser5.zul毫无疑问地移除掉,这样我们可以完全分离了页面层zul和业务逻辑控制层。

     哎呀!写不完!分两页写!翻译的不好!希望大家拍砖!如果那里不对希望大家帮我纠正!我主要是为了自己不再翻英文看!烦死了!

  • 相关阅读:
    Java基础-算法
    Java基础-语法-下
    Java基础-语法-上
    数据清洗工作日志
    docker login 登陆总是不成功
    SpringCloud启动eureka server报错ArrayStoreException,TypeNotPresentExceptionProxy
    pve开机提示volume group ‘pve’ not found问题
    java生成pdf字体的坑
    TwelveMonkeys处理图片
    实验6 流类库与I/O
  • 原文地址:https://www.cnblogs.com/xiaopen/p/zkMVC_1.html
Copyright © 2020-2023  润新知