复合图案中多叶方法问题 [英] Multiple leaf methods problem in composite pattern

查看:107
本文介绍了复合图案中多叶方法问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在工作中,我们正在开发一种PHP应用程序,稍后将其重新编程为Java。有了Java的一些基础知识,我们正在努力设计一切易于重写的东西,而不会有任何困扰。当我们尝试在叶片中实施大量方法的复合模式时,出现了有趣的问题。 / p>

我们正在努力实现(不使用界面,这只是一个简单的例子):

  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()));

//将在包含此方法的组合中的所有类中调用方法Foo
$ c-> Foo();

//与Bar
$ c-> Bar();

这似乎是经典的复合模式,但问题是我们将有相当多的叶子类,他们每个人都可能有5种方法(其中很少有别的方法)。我们的解决方案之一,似乎是迄今为止最好的解决方案,可能实际上是使用 __调用魔法方法来调用叶子中的方法。
不幸的是,我们不知道Java中是否有这样的一个。



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



如果有一些不清楚的话,只要问,我会编辑这篇文章。



编辑:



实际的问题是,并不是每个叶子类都包含例如方法Baz。如果我们使用简单的foreach在每个类中调用Baz,那么它会给出一些错误,因为有些类不包含这个方法。经典解决方案是将每个单独的叶子类的每个方法都实现为Composite类,每个方法都有不同的实现。但是,这将使我们的复合类成为我们使用的方法的数量庞大和混乱。



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

  class Composite implements Fooable,Bazable {
...

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

公共功能Baz(){
...
}
}

为了防止我们的代码变得真实混乱,我们正在考虑如下:

  class Composite {
...

public function __call(){
// implementation
}
}

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

解决方案

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



这样可以避免任何转换或显式检查每个节点的类型。

  / ** 
*访问者能够访问文档中的每个节点。
*访问者在文档中包含每个节点类型
*的回调方法。
* /
public interface DocumentNodeVisitor {
void visitWord(Word word);
void visitImage(Image img);
}

/ **
*文档中每个节点的基本界面。
* /
public interface DocumentNode {
void applyVisitor(DocumentVisitor v);
}

/ **
*代表一个单词的混合节点实现。
* /
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){
//对访问者进行适当的回调。
v.visitWord(this);
}
}

/ **
*表示图像的混合节点实现。
* /
public class Image implements DocumentNode {
public void applyVisitor(DocumentVisitor v){
//对访问者进行适当的回调。
v.visitImage(this);
}
}

public class Paragraph implements DocumentNode {
private final List< DocumentNode>儿童;

public Paragraph(){
this.children = new LinkedList< DocumentNode>();
}

public void addChild(DocumentNode child){
//技术上一个段落不应该包含其他段落,但
//我们允许这个简单的例子。
this.children.add(child);
}

//与叶节点不同,段落不回调
//访问者,而是将访问者传递给每个
//子节点。
public void applyVisitor(DocumentVisitor v){
for(DocumentNode child:children){
child.applyVisitor(v);
}
}
}

/ **
*具体的DocumentVisitor负责拼写检查。
* /
public class SpellChecker实现DocumentVisitor
public void visitImage(Image i){
//显示我们无法拼写检查图像。
}

public void visitWord(Word word){
if(!dictionary.contains(word.getValue()){
// TODO:提升警告。
}
}
}


At work, we are developing an PHP application that would be later re-programmed into Java. With some basic knowledge of Java, we are trying to design everything to be easily re-written, without any headaches. Interesting problem came out when we tried to implement composite pattern with huge number of methods in leafs.

What are we trying to achieve (not using interfaces, it's just a quick example):

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( );

It seems like pretty much classic Composite pattern, but problem is that we will have quite many leaf classes and each of them might have ~5 methods (of which few might be different than others). One of our solutions, which seems to be the best one so far and might actually work, is using __call magic method to call methods in leafs. Unfortunately, we don't know if there is an equivalent of it in Java.

So the actual question is: Is there a better solution for this, using code that would be eventually easily re-coded into Java? Or do you recommend any other solution? Perhaps there's some different, better pattern I could use here.

In case there's something unclear, just ask and I'll edit this post.

Edit:

Actual problem is that not every leaf class contains, for example, method Baz. If we used simple foreach to call Baz in every class, it'd give use bunch of errors, as there are certain classes that don't contain this method. Classic solution would be to have every single method from every single leaf class implemented into Composite class, each with different implementation. But this would make our composite class huge and messy with amount of methods we use.

So usual solution would look like this (Composite class):

class Composite implements Fooable, Bazable {
    ...

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

    public function Baz( ) {
        ...
    }
}

To prevent our code to become real mess, we were thinking about something like:

class Composite {
    ...

    public function __call( ) {
        // implementation
    }
}

But we aren't really sure if it's a good solution and if there's something similar also in Java (as asked already before edit).

解决方案

Within Java you could consider using the visitor pattern whereby you pass a visitor object to each node in the tree and the node makes a callback to the visitor class to determine which behaviour should be performed.

This avoids any casting or explicitly checking the type of each node.

/**
 * 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.
    }
  }
}

这篇关于复合图案中多叶方法问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆