代码之家  ›  专栏  ›  技术社区  ›  Decent Dabbler

在聚合对象上实现访问者模式

  •  2
  • Decent Dabbler  · 技术社区  · 14 年前

    我正在努力将访问者模式应用于一些具有标量成员的对象,同时聚合成员(集合)。

    这些是我拥有的对象:

    Artist
     - id
     - name
     - .. more scalar values ..
     - worksOfArt <-- this is a collection as WorkOfArt instances
    
    
    WorkOfArt
     - id
     - name
     - .. more scalar values ..
     - artists <-- this is a collection of Artist instances
    

    正如您所看到的,结构也将是递归的,但这稍后会引起我的关注。;-)

    我的问题是:实现访问者模式的最佳方法是什么,它允许我访问对象,并且只访问对象的可访问子对象(集合)。

    我想创建这样的界面:

    VisitableAggregateInterface
    {
        public function getVisitableChildren(); // this would return only visitable children
    }
    

    然后让艺术家和Workofart都扩展这样一个抽象类:

    VisitableAggregateAbstract implements VisitableAggregateInterface
    {
        public function accept( Visitor $visitor )
        {
            $visitor->visit( $this );
            foreach( $this->getVisitableChildren() as $visitableChild )
            {
               $visitableChild->accept( $visitor );
            }
        }
    
        /*
            VisitableAggregateInterface::getVisitableChildren()
            will be implemented by Artist and WorkOfArt and will only
            return visitable children (like collections), and not scalar values.
        */
    }
    

    最终的目标是最终得到一个具体的访问者,该访问者将写出一个类似于以下内容的XML文件:

    <?xml version="1.0" encoding="utf-8" standalone="yes"?>
    <artgallery>
        <artists>
            <artist>
                <id>1</id>
                <name></name>
                <worksOfArt>
                    <workOfArt refid="11"/>
                    <workOfArt refid="12"/>
                </worksOfArt>
            <artist>
        <artists>
        <worksOfArt>
            <workOfArt>
                <id>11</id>
                <artists>
                    <artist refid="1"/>
                </artists>
                <name></name>
                <info><![CDATA[some info]]></info>
            </workOfArt>
            <workOfArt>
                <id>12</id>
                <artists>
                    <artist refid="1"/>
                </artists>
                <name></name>
                <info><![CDATA[some info]]></info>
            </workOfArt>
        </worksOfArt>
    </artgallery>
    

    请建议:我走的方向对吗?因为 getVisitableChildren() 我觉得界面有点奇怪。或许我应该完全摒弃访客模式,采取不同的方式吗?

    谢谢。

    2 回复  |  直到 14 年前
        1
  •  0
  •   Ian    14 年前

    你实际上并没有说明艺术家和艺术品之间的关系。我怀疑,如果这两者都意味着艺术家完成了任务,那么您就没有递归数据结构,因此您不需要访问者模式。

    我只是“蛮力”而已。类似这样的东西(未经测试)

    echo "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>
    <artgallery>
        <artists>\n";
    Foreach (Artists as Artist) {
       echo "        <artist>
                <id>1</id>
                <name></name>
                <worksOfArt>\n";
       ForEach (Artist.worksOfArt as Work) {
          $refid = ????;
          echo "<workOfArt refid=\"$refid"/>\n";
       }
       echo "        </worksOfArt>
            </artist>\n";
    }
    echo " </artists>
        <worksOfArt>";
    Foreach (WorkOfArt as work) {
       ForEach (work.Artists as Artist) {
    
       }
    }
    

    然后把echo语句的其余部分放到上面

    顺便说一句-你的问题有错别字。添加了结束和需要。

        2
  •  1
  •   Dan Kendall    14 年前

    我见过 Visitor 使用对要访问的类型具有特定知识的“遍历器”类实现。在这种情况下,它会“知道”访问 WorksOfArt 在里面 Artists 但不是 艺术家 在里面 工匠 . 您可以为其他行为定义其他遍历器。

    这是我发现的一些伪代码…(实际上是vb,但不要告诉任何人:)

    Visitable:

    Public Interface IVisitable
        Sub accept(ByVal visitor As IVisitor)
    End Interface
    

    参观者:

    Public Interface IVisitor
        Sub visit(ByVal visitable As IVisitable)
    End Interface
    

    Traverser:

    Public Class PaymentListExportTraverser
        Private payments As PaymentList
        Private visitor As IVisitor
    
        Public Sub New(ByVal paymentList As PaymentList, ByVal exportVisitor As IVisitor)
            payments = paymentList
            visitor = exportVisitor
        End Sub
    
        Public Sub traverse()
            For Each p As Payment In payments
                p.accept(visitor)
            Next
        End Sub
    End Class