Groovy探索之Builder 三

                             Groovy探索之Builder 三
 
 
因为我们知道builder模式是从使用 MarkupBuilder开始的,而 MarkupBuilder是生成xml文件的。很自然的,当我想做第一个自己的builder的时候,我就会做一个table的builder。用来生成如下的代码:
<tr>
        <td>Name</td>
        <td>Age</td>
        <td>Address</td>
</tr>
<tr>
        <td>tom</td>
        <td>33</td>
        <td>Shenzhen</td>
</tr>
<tr>
        <td>mike</td>
        <td>22</td>
        <td>Taibei</td>
</tr>
 
 
在看下面的文字之前,诸位大虾可以考虑一下自己试着实现上面的功能。
虽然我们在上一篇文字中已经详细的介绍了 BuilderSupport的各个方法的功能,但要实现上面的功能,却实实在在的有些难度。下面让我们一点一点的来解析它的实现。
要分析一个问题,我们总是首先从最简单的开始。所以我们首先看<td>标签的实现。对于“<td>Name</td>”这样的代码,我们可以想象,在创建的时候,肯定是如下的代码:
builder. td 'Name'
 
这样的代码我们熟悉,也就是方法名加上一个参数,调用的是 BuilderSupport的“ createNode(Object name, Object value)”方法。
而如果是下面的代码:
<td colspan=”4”>Name</td>
 
这样的代码,在创建的时候,肯定是这样写:
builder. td([rowspan: '4' ], 'Name' )
 
也就是说它会调用 BuilderSupport 的“ createNode(Object name, Map attributes, Object value) ”方法。
 
同理分析,可以得知“ <td></td> ”会调用 BuilderSupport 的“ createNode(Object name) ”方法。
 
使用同样的方法对“ <tr> ”标签进行分析,也会得到同样的结果。但“ tr ”标签的难点在于标签里要嵌入“ td ”标签。
比如,对于代码“builder. td 'Name' ”,我们可以如下实现:
    protected Object createNode(Object name, Object value){
       sb<< "<"
       sb<<name.toString()
       sb<< ">"
       sb<<value.toString()
       sb<< "</"
       sb<<name.toString()
       sb<< ">"
 
}
 
一次就可以把整个标签实现了。但对于“builder. tr ”,我们却不能实现成下面的样子:
protected Object createNode(Object name){
       sb<< "<"
       sb<<name.toString()
       sb<< ">"
      
       sb<< "</"
       sb<<name.toString()
       sb<< ">"
 
}
 
 
很明显,因为“tr”标签里要嵌入“td”标签的,如果写成上面的样子,那么生成的代码如下:
<tr></tr>
 
中间就没有办法嵌入“ td ”标签了。
分析到了这里,才找到了我们实现整个业务需求的难点所在。如何解决这个问题呢?答案就是 BuilderSupport 的“ nodeCompleted(Object parent, Object node) ”方法。
下面我们来看看整个类是如何实现的:
class HtmlTrBuilder extends BuilderSupport {
   
private StringBuffer sb = new StringBuffer();
 
这个 StringBuffer 对象用来存储“ tr ”和“ td ”实现的内容。
    def isTd = true ;
一个标记,证明正在调用的方法操作的是“ td ”标签,它是实现我们这个业务难点的关键标记。用处在下面会进一步说明。
    protected void setParent(Object parent, Object child){
       
    }
 
setParent ”方法对我们没有用处,我们不对它做任何操作。
    protected Object createNode(Object name){
      
       sb<< "<" <<name.toString()<< ">"
       if (name.toString().toLowerCase() == 'td' )
       {
           sb<< "</" <<name.toString()<< ">"
           isTd = true
       }
}
 
当方法名为“ td ”的时候,生成“ </td> ”来结束该标签,同时,置 isTd 为“ true ”。当然,如果方法名为“ tr ”的时候就不能结束了。
那么为什么要置 isTd 为“ true ”呢?我们先来看看“ nodeCompleted(Object parent, Object node) ”方法的实现:
    protected void nodeCompleted(Object parent, Object node) {
       if (! this .isTd)
       {
           this .sb<< '</tr>'
       }
       else
       {
           this .isTd = false ;
       }
}
 
看到这个方法,我们就明白了为什么要置 isTd 为“ true ”,因为如果 isTd 为“ false ”的话,在“ nodeCompleted ”方法里就要执行“ this .sb<< '</tr>' ”,也就是关闭了“ tr ”标签。
同样的道理,下面的这三个方法就不难理解了:
    protected Object createNode(Object name, Object value){
       sb<< "<"
       sb<<name.toString()
       sb<< ">"
       sb<<value.toString()
       if (name.toString().toLowerCase() == 'td' )
       {
           sb<< "</" <<name.toString()<< ">"
           isTd = true
       }
    }
 
    protected Object createNode(Object name, Map attributes){
      
           sb<< "<"
           sb<<name.toString()
           sb<< " "
           attr. each {
              it ->
                  sb<<it.key.toString()
                  sb<< "=/""
                  sb<<it.value.toString()
                  sb<< "/""
           }
           sb<< ">"
       if (name.toString().toLowerCase() == 'td' )
       {
           sb<< "</" <<name.toString()<< ">"
           isTd = true
       }
      
    }
   
    protected Object createNode(Object name, Map attributes, Object value){
      
           sb<< "<"
           sb<<name.toString()
           sb<< " "
           attr. each {
              it ->
                  sb<<it.key.toString()
                  sb<< "=/""
                  sb<<it.value.toString()
                  sb<< "/""
           }
           sb<< ">"
       sb. append (value.toString())
       if (name.toString().toLowerCase() == 'td' )
       {
           sb<< "</" <<name.toString()<< ">"
           isTd = true
       }
      
}
}
 
这样就实现了整个业务的要求。然后再对整个代码进行一下整理,把该提出来的代码提出来。得到如下的实现:
class HtmlTrBuilder extends BuilderSupport {
   
    private StringBuffer sb = new StringBuffer();
   
    def isTd = true ;
   
    def makeWithAttr = {
           name,attr ->
           sb<< "<"
           sb<<name.toString()
           sb<< " "
           attr. each {
              it ->
                  sb<<it.key.toString()
                  sb<< "=/""
                  sb<<it.value.toString()
                  sb<< "/""
           }
           sb<< ">"
    }
   
    def doTdEnd = {
           name ->
           if (name.toString().toLowerCase() == 'td' )
           {
              sb<< "</" <<name.toString()<< ">"
              isTd = true
           }
          
    }
   
    protected void setParent(Object parent, Object child){
       
    }
   
    protected Object createNode(Object name){
      
       sb<< "<" <<name.toString()<< ">"
       doTdEnd. call (name)
    }
   
    protected Object createNode(Object name, Object value){
       sb<< "<"
       sb<<name.toString()
       sb<< ">"
       sb<<value.toString()
       doTdEnd. call (name)
    }
 
    protected Object createNode(Object name, Map attributes){
      
       makeWithAttr. call (name,attributes)
       doTdEnd. call (name)
      
    }
   
    protected Object createNode(Object name, Map attributes, Object value){
      
       makeWithAttr. call (name,attributes)
       sb. append (value.toString())
       doTdEnd. call (name)
      
    }
   
    protected void nodeCompleted(Object parent, Object node) {
       if (! this .isTd)
       {
           this .sb<< '</tr>'
       }
       else
       {
           this .isTd = false ;
       }
    }
}
 
最后,我们来对上面的代码进行测试:
     HtmlTrBuilder htb = new HtmlTrBuilder()
     
     htb.tr
     {
        td([rowspan: '2' ], 'a' )
        td([colspan: '3' ], 'b' )
        td 'c'
        td 'd'
        td 'e'
        td([rowspan: '3' ], 'f' )
     }
     
     htb.tr
     {
        td([colspan: '2' ], 'g' )
        td 'h'
        td 'i'
        td 'j'
        td 'k'
     }
     
     htb.tr
     {
        td 'l'
        td 'm'
        td 'n'
        td 'o'
        td 'p'
        td 'q'
        td 'r'
     }
     
  println htb.sb.toString()
 
运行结果为:
<tr><td rowspan="2">a</td><td colspan="3">b</td><td>c</td><td>d</td><td>e</td><td rowspan="3">f</td></tr><tr><td colspan="2">g</td><td>h</td><td>i</td><td>j</td><td>k</td></tr><tr><td>l</td><td>m</td><td>n</td><td>o</td><td>p</td><td>q</td><td>r</td></tr>
 
 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值