Проблема многолистовых методов в составном паттерне - PullRequest
6 голосов
/ 23 апреля 2010

На работе мы разрабатываем приложение 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( );

Кажется, что это довольно классический шаблон Composite, но проблема в том, что у нас будет довольно много листовых классов, и у каждого из них может быть ~ 5 методов (из которых немногие могут отличаться от других). Одно из наших решений, которое на данный момент является лучшим и может реально работать, - это использование __ call magic method для вызова методов в листьях. К сожалению, мы не знаем, есть ли эквивалент в Java.

Таким образом, фактический вопрос: есть ли лучшее решение для этого, используя код, который в конечном итоге будет легко перекодирован в Java? Или вы рекомендуете другое решение? Возможно, есть какой-то другой, лучший образец, который я мог бы использовать здесь.

В случае, если что-то неясно, просто спросите, и я отредактирую этот пост.

Edit:

Фактическая проблема заключается в том, что не каждый листовой класс содержит, например, метод Baz. Если бы мы использовали простой foreach для вызова Baz в каждом классе, это дало бы кучу ошибок, поскольку есть определенные классы, которые не содержат этот метод. Классическим решением было бы внедрить каждый отдельный метод из каждого листового класса в класс Composite, каждый с разной реализацией. Но это сделало бы наш составной класс огромным и запутанным с количеством используемых нами методов.

Итак, обычное решение будет выглядеть так (класс Composite):

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 ]

2 голосов
/ 23 апреля 2010

Шаблон дизайна посетителя является довольно хорошим решением.Но вы должны учитывать возможные изменения в структуре, например, новый класс Leaf заставит вас реализовать applyVisitor и добавить метод visit * ко всем другим созданным вами посетителям.Таким образом, Visitor действительно помогает вам добавлять поведение к структурированным объектам по цене такой структуры, которая не меняется слишком часто.Если структура часто меняется, а алгоритмы не так уж и велики, вы можете рассмотреть возможность использования разных композитов для объектов с одинаковыми интерфейсами.Если вы хотите сделать это пакостным способом, как в настоящее время в PHP, посмотрите API отражения Java.Хорошим решением были бы динамические вызовы imho (как в Ruby или Python).Вы можете смоделировать их, но это было бы много работы ... Так что мой ответ - использовать Visitor с осторожностью или рассмотреть различные композиты для объектов с различным поведением.

2 голосов
/ 23 апреля 2010

В 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.
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...