На работе мы разрабатываем приложение 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( );
Это походит в значительной степени на классический Составной шаблон, но проблема состоит в том, что у нас будет вполне много листовых классов, и у каждого из них могло бы быть ~5 методов (которых немногие могли бы отличаться, чем другие). Одно из наших решений, которое, кажется, лучший до сих пор и могло бы на самом деле работать, использует __, называют волшебный метод для вызова методов в листах. К сожалению, мы не знаем, существует ли эквивалент его в 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 (как спросили перед редактированием).
В 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.
}
}
}
Шаблон оформления посетителей - неплохое решение. Но вы должны учитывать возможные изменения в структуре, например Новый класс Leaf заставит вас реализовать applyVisitor и добавить метод visit * для каждого созданного вами посетителя. Таким образом, Visitor действительно помогает вам добавить поведение к структурированным объектам за счет того, что структура не меняется слишком часто. Если структура меняется часто, а алгоритмы не так сильно, вы можете рассмотреть возможность использования разных композитов для объектов с одинаковыми интерфейсами. Если вы хотите сделать это грязным способом, как вы это делаете сейчас в PHP, посмотрите API отражения Java. Хорошим решением будут динамические вызовы imho (как в Ruby или Python). Вы можете смоделировать их, но это потребует много работы ... Итак, мой ответ - осторожно используйте Visitor или рассматривайте разные Composites для объектов с разным поведением.