代码之家  ›  专栏  ›  技术社区  ›  Ondrej Slinták

复合模式中的多叶方法问题

  •  6
  • Ondrej Slinták  · 技术社区  · 15 年前

    在工作中,我们正在开发一个PHP应用程序,以后将其重新编程到Java中。使用Java的一些基本知识,我们试图设计一切容易重写的东西,没有任何头痛。当我们试图实现时,出现了一个有趣的问题 composite pattern 在leafs中有大量的方法。

    我们试图实现什么(不使用接口,这只是一个简单的例子):

    class Composite {
        ...
    }
    
    
    class LeafOne {
        public function Foo( );
    
        public function Moo( );
    }
    
    
    class LeafTwo {
        public function Bar( );
    
        public function Baz( );
    }
    
    
    $c = new Composite( Array( new LeafOne( ), new LeafTwo( ) ) );
    
    // will call method Foo in all classes in composite that contain this method
    $c->Foo( );
    
    // same with Bar
    $c->Bar( );
    

    这看起来很像经典的复合模式,但问题是我们将有相当多的叶类,并且每个叶类可能有~5个方法(其中很少有方法不同于其他方法)。我们的解决方案之一是使用 __call magic method 调用leafs中的方法。 不幸的是,我们不知道Java中是否有它的等价物。

    因此,实际问题是:是否有更好的解决方案,使用最终会被重新编码成Java的代码?或者你推荐其他的解决方案?也许这里有一些不同的更好的模式。

    如果有什么不清楚的地方,问一下,我会编辑这篇文章。

    编辑:

    实际问题是并非每个叶类都包含方法baz。如果我们在每个类中使用简单的foreach来调用baz,它将给出使用大量错误,因为有些类不包含此方法。经典的解决方案是将每个单独的叶类中的每个方法实现为复合类,每个方法都有不同的实现。但这会使我们的复合类变得庞大,并且使用大量的方法。

    所以通常的解决方案是这样的(复合类):

    class Composite implements Fooable, Bazable {
        ...
    
        public function Foo( ) {
            foreach( $this->classes as $class ) {
                $class->Foo( );
            }
        }
    
        public function Baz( ) {
            ...
        }
    }
    

    为了防止代码变得一团糟,我们考虑了如下问题:

    class Composite {
        ...
    
        public function __call( ) {
            // implementation
        }
    }
    

    但我们不确定它是否是一个好的解决方案,如果Java中也有类似的东西(在编辑之前已经问及了)。

    2 回复  |  直到 15 年前
        1
  •  2
  •   Adamski    15 年前

    在Java中,您可以考虑使用 visitor 将访问者对象传递到树中的每个节点的模式,该节点使 回拨 以确定应执行哪些行为。

    这避免了任何强制转换或显式检查每个节点的类型。

    /**
     * Visitor capable of visiting each node within a document.
     * The visitor contains a callback method for each node type
     * within the document.
     */
    public interface DocumentNodeVisitor {
      void visitWord(Word word);
      void visitImage(Image img);
    }
    
    /**
     * Base interface for each node in a document.
     */
    public interface DocumentNode {
      void applyVisitor(DocumentVisitor v);
    }
    
    /**
     * Conrete node implementation representing a word.
     */    
    public class Word implements DocumentNode {
      private final String s;
    
      public Word(String s) { this.s = s; }
    
      public String getValue() { return this.s; }
    
      public void applyVisitor(DocumentVisitor v) {
        // Make appropriate callback to visitor.
        v.visitWord(this);
      }
    }
    
    /**
     * Conrete node implementation representing an image.
     */        
    public class Image implements DocumentNode {
      public void applyVisitor(DocumentVisitor v) {
        // Make appropriate callback to visitor.
        v.visitImage(this);
      }
    }
    
    public class Paragraph implements DocumentNode {
      private final List<DocumentNode> children;
    
      public Paragraph() {
        this.children = new LinkedList<DocumentNode>();
      }
    
      public void addChild(DocumentNode child) {
        // Technically a Paragraph should not contain other Paragraphs but
        // we allow it for this simple example.
        this.children.add(child);
      }
    
      // Unlike leaf nodes a Paragraph doesn't callback to
      // the visitor but rather passes the visitor to each
      // child node.
      public void applyVisitor(DocumentVisitor v) {
        for (DocumentNode child : children) {
          child.applyVisitor(v);
        }
      }
    }    
    
    /**
     * Concrete DocumentVisitor responsible for spell-checking.
     */
    public class SpellChecker implements DocumentVisitor
      public void visitImage(Image i) {
        // Do nothing, as obviously we can't spellcheck an image.
      }
    
      public void visitWord(Word word) {
        if (!dictionary.contains(word.getValue()) {
          // TODO: Raise warning.
        }
      }
    }
    
        2
  •  2
  •   Gabriel Ščerbák    15 年前

    访客设计模式是一个很好的解决方案。但是您必须考虑结构中可能发生的更改,例如,新的leaf类将使您实现applyVisitor,并将visit*方法添加到您创建的其他每个访问者中。所以访问者真的能帮助你以不经常改变的结构为代价向结构化对象添加行为。如果结构经常改变,算法也不太多,那么您可以考虑为具有相同接口的对象使用不同的复合。如果你想这样做 肮脏的 正如您目前在PHP中所做的那样,查看Java反射API。好的解决方案是imho动态调用(如Ruby或Python)。你可以模拟这些,但那会很有用…所以我的答案是小心使用访客,或者考虑不同行为对象的不同复合材料。

    推荐文章