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

Ond*_*ták 6 php java design-patterns

在工作中,我们正在开发一个PHP应用程序,稍后将其重新编程为Java.有了Java的一些基本知识,我们正在尝试设计一些易于重写的内容,而不会有任何麻烦.当我们尝试在叶子中实现具有大量方法的复合模式时,出现了有趣的问题.

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

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( );
Run Code Online (Sandbox Code Playgroud)

它似乎是非常经典的复合模式,但问题是我们将拥有相当多的叶类,并且它们中的每一个可能有~5种方法(其中很少可能与其他方法不同).我们的解决方案之一似乎是迄今为止最好的解决方案,可能实际上正在使用__call魔术方法来调用叶子中的方法.不幸的是,我们不知道Java中是否有相同的东西.

所以实际的问题是:有没有更好的解决方案,使用最终可以轻松重新编码为Java的代码?或者您推荐任何其他解决方案?也许我可以在这里使用一些不同的,更好的模式.

如果有不清楚的地方,请询问,我将编辑这篇文章.

编辑:

实际问题是并非每个叶类都包含例如方法Baz.如果我们使用简单的foreach在每个类中调用Baz,它会使用一堆错误,因为某些类不包含此方法.经典的解决方案是将每个叶子类中的每一个方法都实现为Composite类,每个方法都有不同的实现.但这会使我们的复合类庞大而且混乱我们使用的方法量.

所以通常的解决方案看起来像这样(Composite类):

class Composite implements Fooable, Bazable {
    ...

    public function Foo( ) {
        foreach( $this->classes as $class ) {
            $class->Foo( );
        }
    }

    public function Baz( ) {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

为了防止我们的代码变得混乱,我们考虑的事情如下:

class Composite {
    ...

    public function __call( ) {
        // implementation
    }
}
Run Code Online (Sandbox Code Playgroud)

但我们并不确定它是否是一个很好的解决方案,如果在Java中也有类似的东西(在编辑之前已经提到过).

Ada*_*ski 2

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

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

/**
 * 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.
    }
  }
}
Run Code Online (Sandbox Code Playgroud)