Шаблон посетителей для посещения суперклассов, интерфейсов и отмены приличных

Я работаю над вариантом стандартного шаблона посетителей со следующими тремя требованиями:

  • Для каждого узла все суперклассы узла должны быть посещены первыми
  • Для каждого класса все реализованные интерфейсы должны быть посещены в первую очередь.
  • Каждое посещение класса или интерфейса должно быть в состоянии отменить посещение подклассов / реализующих классов

Мой вопрос двоякий. 1) это известный метод или похожий шаблон под другим именем? 2) есть ли очевидные улучшения или проблемы с этим подходом?

Я подробно опишу свой подход на абстрактном примере.

Посещаемая иерархия классов является Collection из ConcreteItems, ConcreteItem реализует CrossCuttingConcern и расширяет AbstractItem учебный класс.

class Collection implements Visitable {
  public final List<ConcreteItem> concreteItems;

  public Collection(ConcreteItem ...concreteItems) {
    this.concreteItems = asList(concreteItems);
  }

  public Decent accept(Visitor visitor) {
    return visitor.visit(this);
  }
}

class AbstractItem implements Visitable {
  public Decent accept(Visitor visitor) {
    return visitor.visit(this);
  }
}

interface CrossCuttingConcern {
  default Decent acceptCrossCuttingConcern(Visitor visitor) {
    return visitor.visit(this);
  }
}

class ConcreteItem extends AbstractItem implements CrossCuttingConcern {
  public Decent accept(Visitor visitor) {
    // This will visit the abstract super type, interface, and concrete class in turn.
    // If any of those returns Decent.STOP, then the remaining ones are not visited.
    return Decent.allUntilStop(
        () -> super.accept(visitor),
        () -> this.acceptCrossCuttingConcern(visitor),
        () -> visitor.visit(this)
    );
  }
}

Теперь Visitor а также Visitable реализации модифицируются, чтобы вернуть тип с именем Decent (да, может быть, не лучшее название для этого). visit Метод возвращает STOP, если хочет, чтобы посетитель прекратил спуск по иерархии классов. т.е. если вы хотите посетить только AbstractItems Вы возвращаетесь Decent.STOP от visit(AbstractItem),

interface Visitor {
  Decent visit(Collection collection);
  Decent visit(AbstractItem abstractItem);
  Decent visit(CrossCuttingConcern interfaceItem);
  Decent visit(ConcreteItem concreteItem);
}

interface Visitable {
  Decent accept(Visitor visitor);
}

enum Decent {
  STOP,
  CONTINUE;

  public static Decent allUntilStop(Supplier<Decent> ...fns) {
    for (Supplier<Decent> fn : fns) {
      if (fn.get() == STOP) {
        return STOP;
      }
    }
    return CONTINUE;
  }

  public static BinaryOperator<Decent> product() {
    return (a, b) -> a == CONTINUE && b == CONTINUE ? CONTINUE : STOP;
  }
}

Теперь реализация адаптера посетителя по умолчанию возвращает Decent.CONTINUE и печатает отладочную информацию, использованную в примере ниже.

class VisitorAdapter implements Visitor {

  @Override
  public Decent visit(Collection collection) {
    System.out.println("visiting Collection: " + collection);
    // iterate over all concrete items and return STOP if one of the visit(items) does so
    return collection.concreteItems.stream()
        .map(a -> a.accept(this))
        .reduce(Decent.product())
        .orElse(CONTINUE); // return CONTINUE if collection contains zero items
  }

  @Override
  public Decent visit(AbstractItem abstractItem) {
    System.out.println("visiting AbstractItem: " + abstractItem);
    return CONT;
  }

  @Override
  public Decent visit(CrossCuttingConcern interfaceItem) {
    System.out.println("visiting CrossCuttingConcern: " + interfaceItem);
    return CONT;
  }

  @Override
  public Decent visit(ConcreteItem concreteItem) {
    System.out.println("visiting ConcreteItem: " + concreteItem);
    return CONT;
  }
}

Этот пример демонстрирует рабочие требования:

public static void main(String[] args) {
  Collection collection = new Collection(new ConcreteItem(), new ConcreteItem());

  System.out.println("Visit all");
  new VisitorAdapter().visit(collection);
  System.out.println("");

  System.out.println("Stop at AbstractItem")
  new VisitorAdapter() {
    public Decent visit(AbstractItem abstractItem) {
      super.visit(abstractItem);
      return STOP;
    }
  }.visit(collection);
  System.out.println("");

  System.out.println("Stop at CrossCuttingConcern");
  new VisitorAdapter() {
    public Decent visit(CrossCuttingConcern interfaceItem) {
      super.visit(interfaceItem);
      return STOP;
    }
  }.visit(collection);
  System.out.println("");
}

Обеспечение следующего вывода:

Visit all
visiting Collection: Collection@7f31245a
visiting AbstractItem: ConcreteItem@16b98e56
visiting CrossCuttingConcern: ConcreteItem@16b98e56
visiting ConcreteItem: ConcreteItem@16b98e56
visiting AbstractItem: ConcreteItem@7ef20235
visiting CrossCuttingConcern: ConcreteItem@7ef20235
visiting ConcreteItem: ConcreteItem@7ef20235

Stop at AbstractClass
visiting Collection: Collection@7f31245a
visiting AbstractItem: ConcreteItem@16b98e56
visiting AbstractItem: ConcreteItem@7ef20235

Stop at Interface
visiting Collection: Collection@7f31245a
visiting AbstractItem: ConcreteItem@16b98e56
visiting CrossCuttingConcern: ConcreteItem@16b98e56
visiting AbstractItem: ConcreteItem@7ef20235
visiting CrossCuttingConcern: ConcreteItem@7ef20235

Так; это выглядит знакомо, или есть более простой способ выполнить мои требования?

0 ответов

Другие вопросы по тегам