深入浅出设计模式读书笔记—Composite

一、composite能做什么?

1.composite定义了一个tree-like的part-whole的层级关系。

2.这个体系中的类都会实现统一的接口,这样客户端就可以通过一致的方式访问所有的对象。那些复合对象(包含子对象)增加或删除子对象时,客户端不用改变调用代码(客户端可以完全不用关心这些事情)。

3.结合iterater,调用方可以透明的从树的根开始遍历完这个体系中的所有对象,即客户仅需要在根上调用某个方法,这个体系中的所有对象的这个方法都会被调用。

 

二、composite是什么?

说了这么多composite的好处,那composite到底是什么呢?首先来看看它的定义:

将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性. 

 

   这里有几个很关键的角色:

   1.IComponent:我们的属性体系都要实现的接口。以保证能用同样的方式调用体系中的对象,而不用担心对象是CompositeComponent或LeafComponent。

   2.CompositeComponent:这类对象含有子对象,而子对象可以是LeafComponent,也可以是CompositeComponent(如此递归下去)。

     3.LeafComponent: 这类对象不包含子对象(想想递归,通常这类对象就是递归的终结条件)。

 

   问题:

     1.因为LeafComponent和 CompositeComponent实现了统一的接口,这有可能带来彼此会包含并不适合自己的方法,而当客户端调用了这些方法时该怎么办?

      这个问题让人很纠结。唯有具体情况具体分析了。常用的解决办法有:

   a.在实现不适合自己的方法时抛出未实现或者不支持异常。由此而来的问题是在使用这些方法时,不得不加try,catch代码(尽管这并不是business的一部分)。

     b.添加默认实现。比如对于LeafComponent实现Add时,不做任何事情就可以了。但是对于LeafComponent调用Add方法,成功执行了但没有任何效果总让人觉得不自在。

   2.IComponent抽象了LeafComponent和CompositeComponent的行为,不是违反单一职责原则吗?

      从某种层面说:是的。但是这也带来了使用方不用关心对象究竟是Leaf还是Composite的好处。而这也恰恰是该模式的一个主要关注点。

    当然我们也可以将Leaf和Composite的行为抽象到不同的接口当中,但在使用时就不得不面临要obj is ILeaf这样的代码了。

 

    总之,凡是都有两面性,模式也如此,没有放之四海皆准的标准。具体怎么抉择还得看‘国情’。 

 三、身边的composite

   对于asp.net开发人员来说,control或者page就是我们每天都会面对的composite。

  一个 WebForm就是一棵控件树。这棵控件树包含中的对象都直接或间接继承于Control。在页面的生命周期中,很多阶段都是调用了Page的相关方法后,framework会递归地调用Page的子控件的相应方法(如此递归下去,直至所有控件的相应方法调用完毕)。但是这个递归的过程并不是通过迭代器,而是在Control内部完成的。下面提供一个结合迭代器来遍历的简单实现:

public class CLSEnum : IEnumerator
    {
        
private ControlCollection cls = null;
        
private Stack<IEnumerator> enums = new Stack<IEnumerator>();

        
public CLSEnum( ControlCollection cls)
        {
            this.cls 
= cls;
            enums.Push(cls.GetEnumerator());
        }
        
#region IEnumerator Members

        
public object Current
        {
            
get
            {
                var iter 
= enums.Peek();
                var curObj 
= iter.Current as Control;
                
if( curObj != null)
                {
                    
if( curObj.HasControls())
                    {
                        enums.Push(curObj.Controls.GetEnumerator());
                    }
                }

                
return curObj;
            }
        }

        
public bool MoveNext()
        {
            
if( enums.Count == 0)
            {
                
return false;
            }
            
else
            {
                
while (enums.Count > 0)
                {
                    var iter 
= enums.Peek();
                    
if( iter.MoveNext())
                    {
                        
return true;
                    }
                    
else
                    {
                        enums.Pop();
                    }
                }

                
return false;
            }
        }

        
public void Reset()
        {
            enums.Clear();
            enums.Push(cls.GetEnumerator());
        }

        #endregion
    }

四、小结

  在遇到树形结构时都可以考虑使用composite模式。 具体的实现形式则因情况而异。

   

   

 


posted @ 2010-03-19 13:15 FrogTan 阅读(1166) 评论(6) 编辑 收藏

 回复 引用 查看   
#1楼 2010-03-19 13:52 allentranks      
建议IComponent加个bool的属性:IsLeaf
 回复 引用 查看   
#2楼[楼主] 2010-03-19 13:56 FrogTan      
@allentranks
我也这么想过。。

 回复 引用 查看   
#3楼 2010-03-19 16:28 dax.net      
我倒是觉得可以把与非叶子节点的操作独立出来。比如定义一个IComponentContainer,然后将Add,Remove方法移到这个接口中。再让CompositeComponent类同时实现IComposite和IComponentContainer接口。

 回复 引用 查看   
#4楼[楼主] 2010-03-19 16:49 FrogTan      
@dax.net
嗯 没错,但是使用的时候就必须去判断了,对调用者来说就不透明了。也就达不到‘使得客户端对单个对象和组合对象的使用具有一致性’的效果了。
使用的时候肯定是哪种方式负面影响小就用哪种了。

 回复 引用 查看   
#5楼 2010-03-19 17:19 allentranks      
引用FrogTan:
@allentranks
我也这么想过。。

然后咧???

 回复 引用 查看   
#6楼[楼主] 2010-03-19 17:28 FrogTan      
引用allentranks:
引用FrogTan:
@allentranks
我也这么想过。。

然后咧???

呵呵,感觉和分散到不同的接口效果差不多。
这样只能为守规矩的调用者提供帮助,对于不守规矩的还是可以,举个例子,调用不属于叶子节点的方法。

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1689592 T3j6fL73GE8=