Реализация шаблона посетителя в java - Как это выглядит? - PullRequest
2 голосов
/ 03 июня 2009

Alrite, я сразу перейду к коду:

public interface Visitor {

public void visitInventory(); 
public void visitMaxCount();
public void visitCountry();
public void visitSomethingElse();
public void complete();
//the idea of this visitor is that when a validator would visit it, it would validate data
//when a persister visits it, it would persist data, etc, etc.
// not sure if I making sense here...
}

public interface Visitable {
public void accept(Visitor visitor); 
}

вот базовая реализация:

public class StoreValidator implements Visitor {
private List <ValidationError> storeValidationErrors = new ArrayList<ValidationError>();

public void addError(ValidationError error) {
storeValidationErrors.add(error);
}

public List<ValidationError> getErrors() {
return storeValidationErrors;
}

public void visitInventory() {
// do nothing 
}

public void visitMaxCount() {
//do nothing
}
//... etc..  all empty implementations 

}

Вы поймете, почему я сделал пустую реализацию здесь ... Я бы сейчас написал валидатор .. который расширяет StoreValidator

public XYZValidator extends StoreValidator {

@Override 
public void visitInventory(Visitable visitable) { 
// do something with visitable .. cast it to expected type
// invoke a DAO, obtain results from DB
// if errors found, do addError(new ValidationError()); with msg.
}

@Override 
public void visitMaxCount(Visitable visitable) {
//do something with visitable.. 
}

// I wouldn't implement the rest coz they wouldn't make sense
// in XYZValidator.. so they are defined as empty in StoreValidator.

}

Теперь вот как будет выглядеть посетитель:

public Store implements Visitable {

public void accept(Visitor visitor) {
visitor.visitInventory();
visitor.visitMaxCount();
}
}

У меня мог бы быть код, который делает что-то подобное в списке объектов Store:

List<Store> stores; //assume this has a list of stores.
StoreValidator validator = new XYZValidator(); //or I would get it from a validatorfactory
for(Store store: stores) {
           store.accept(validator); // so even if you send a wrong validator, you are good.
}

Точно так же у вас будет ABCValidator, который будет обеспечивать реализацию для других методов (visitCountry / visitSomethinElse), и он будет выходить из StoreValidator. У меня был бы другой тип Object (не Store), определяющий метод accept.

Я вижу здесь проблему ... Скажем, мне нужен FileValidator, который отличается от StoreValidator, я ожидаю, что у него не будет ни одной из этих проверок, связанных с бизнесом, таких как visitInventory () и т. Д. Но, имея один интерфейс Visitor, я бы в конечном итоге объявил все виды методов в Интерфейс посетителя. Это верно? Это как ты это делаешь?

Я не знаю, правильно ли я понял схему или я что-то понимаю. Пожалуйста, поделитесь своими мыслями.

Ответы [ 4 ]

9 голосов
/ 03 июня 2009

Некоторое время назад я написал нечто подобное для моей магистерской диссертации. Этот код немного введите безопасный, чем ваш:

interface Visitable<T extends Visitor> {

   void acceptVisitor(T visitor);
}

interface Visitor {

    /**
     * Called before any other visiting method.
     */
    void startVisit();

    /**
     * Called at the end of the visit. 
     */
    void endVisit();
}

пример:

interface ConstantPoolVisitor extends Visitor {

    void visitUTF8(int index, String utf8);

    void visitClass(int index, int utf8Index);

    // ==cut==
}

class ConstantPool implements Visitable<ConstantPoolVisitor> {

    @Override
    public void acceptVisitor(ConstantPoolVisitor visitor) {
        visitor.startVisit();

        for (ConstanPoolEntry entry : entries) {
            entry.acceptVisitor(visitor);
        }

        visitor.endVisit();
    }

так что да, я думаю, что это определенно хороший и гибкий дизайн, если и только если ваши данные изменяются медленнее, чем ваше поведение. В моем примере данные являются байт-кодом Java, который является фиксированным (определенным спецификацией JVM). Когда «поведение доминирует» (я хочу записать, скомпилировать, преобразовать, изменить и т. Д. Мой байт-код), шаблон Visitor позволяет изменять / добавлять / удалять поведение, не касаясь классов данных. Просто добавьте еще одну реализацию Visitor.

Ради простоты предположим, что я должен добавить еще один метод посещения к своему интерфейсу посетителя: в конечном итоге я нарушу весь свой код.

В качестве альтернативы я бы рассмотрел шаблон стратегии для этого сценария . Стратегия + декоратор - хороший дизайн для валидации.

4 голосов
/ 03 июня 2009

Существует проблема с вашим кодом, как указано. У интерфейса, который вы предоставляете, есть такие методы, как

public void visitInventory(); 

но затем вы реализуете его в XYZValidator как

public void visitInventory(Visitable visitable)

Шаблон посетителя - это способ реализации многократной отправки на языках, которые не делают этого автоматически (например, Java). Одно из требований заключается в том, что у вас есть группа связанных классов (то есть набор подклассов с одним суперклассом). У вас нет этого здесь, поэтому шаблон посетителей не подходит. Задача, которую вы пытаетесь выполнить, однако, это хорошо, это просто не шаблон посетителя.

В Java вам следует подумать о шаблоне Visitor, если у вас есть код, подобный

public void count(Item item) {
  if (item instanceof SimpleItem) {
    // do something
  } else if (item instanceof ComplexItem {
    // do something else
  } else ...
}

особенно, если подклассы Item относительно фиксированы.

1 голос
/ 03 июня 2009

Вы, вероятно, не хотите отображать шаблон непосредственно на один интерфейс, который реализует все, что следует за этим шаблоном. Шаблоны НЕ являются интерфейсами, это общие планы реализации решения.

В вашем примере вы бы создали интерфейс StoreVisitor и интерфейс FileVisitor для различных бизнес-объектов, которые хотят использовать шаблон Visitor в соответствующих обстоятельствах.

Возможно, разные реализации Visitor имеют общие действия, поэтому у вас может быть суперинтерфейс, который определяет эти общие функции. Затем вы можете кодировать интерфейсы Visitable для использования либо конкретного интерфейса Visitable, либо его суперкласса, в зависимости от ситуации.

Например, интерфейсы FileVisitor и SQLTableVisitor могут быть подклассом интерфейса DataStoreVisitor. Тогда:

VisitableStore принимает StoreVisitor,

VisitableFile принимает Filevisitor, или

VisitableDataStore принимает DataStoreVistor (который может быть реализацией либо FileVisitor, либо SQLTableVisitor).

  • простите случайные примеры, я надеюсь, что это имеет смысл.
1 голос
/ 03 июня 2009

Я использую шаблон посетителя другим способом. У меня есть определенный интерфейс Visitor для типа объекта, и этот интерфейс объявляет только один метод - для посещения этого объекта .. как это:

public interface TreeNodeVisitor {
    void visit(TreeNode node);
}

объект TreeNode может принимать TreeNodeVisitor s, что означает, что он просто вызывает метод visit для узла и / или его дочерних элементов.

Конкретная реализация посетителя реализует метод посещения и сообщает, что будет делать посетитель .. например ContryVisitor, InventoryVisitor и т. Д.

Этот подход должен избежать ваших проблем ..

...