Отбрасывание шаблона посетителя шаблонов проектирования Java

Шаблоны проектирования

Оригинальный адрес блога:Отбрасывание шаблона посетителя шаблонов проектирования Java

шаблон посетителя

Represent an operation to be performed on the elements of an object structure. Visitor lets a new operation be defined without changing the classes of the elements on which it operates.

Шаблон посетителя предназначен для инкапсуляции некоторых операций, применяемых к элементам структуры данных. Как только эти операции необходимо изменить, структуры данных, которые принимают операции, могут оставаться неизменными.

намерениеСтруктура данных в основном отделяется от операции данных.

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

когда использоватьВам нужно выполнять множество различных и несвязанных операций над объектами в структуре объектов, и вам нужно избегать того, чтобы эти операции «загрязняли» классы этих объектов, используя шаблон посетителя для их инкапсуляции в классы.

Основные роли шаблона посетителя

Посетитель (абстрактный посетитель): абстрактный посетитель объявляет операцию посещения для каждого класса конкретных элементов ConcreteElement в структуре объекта. Из имени или типа параметра этой операции можно четко определить тип конкретного элемента, к которому необходимо получить доступ. посетитель должен реализовать эти методы Action, которые определяют операции доступа к этим элементам.

Конкретика (в частности, посетитель): конкретный посетитель достигает каждой операции, объявленной абстрактными посетителями, каждые структуры объектов, которые действуют для доступа к одному типу элемента.

Элемент (абстрактный элемент): абстрактный элемент обычно представляет собой абстрактный класс или интерфейс, который определяет метод accept(), который обычно принимает абстрактного посетителя в качестве параметра.

ConcreteElement (конкретный элемент): конкретный элемент реализует метод accept(), а метод доступа посетителя вызывается в методе accept() для завершения операции над элементом.

ObjectStructure: структура объекта представляет собой набор элементов, в котором хранятся объекты элементов и предоставляются методы для обхода его внутренних элементов. Он может быть реализован в сочетании с шаблоном композиции или может быть простым объектом коллекции, таким как объект List или объект Set.

используемые сцены:1. Класс, соответствующий объекту в структуре объекта, меняется редко, но часто возникает необходимость определить новые операции над этой структурой объекта. 2. Над объектами в структуре объектов необходимо выполнять множество различных и несвязанных операций, и необходимо избегать того, чтобы эти операции «загрязняли» классы этих объектов, и не желать модифицировать эти классы при добавлении новых операций.

Диаграмма UML, связанная с шаблоном посетителя

Диаграммы классов UML и диаграммы последовательности

img

Как видно на диаграмме классов, ElementA реализует метод accept(visitor) интерфейса Element, а через visitor.visitElementA(this) тот же класс visitor1 связывается с ElementA посредством реализации метода visitElementA(ElementA a). То же самое верно и для ElementB.

На диаграмме последовательности в правом верхнем углу объект Client имеет набор структур данных Element, а метод accept(visitor) вызывается для каждого элемента Element через цикл. Например, ElementA сначала вызывает accept(visitor), что на самом деле вызывает visitElementA(A) посетителя1, то же условие для ElementB.

Более четкая диаграмма классов выглядит следующим образом

img

Пример галантереи

Перейти к соответствующему исходному коду

//抽象元素
public interface Element {

    void accept(ElementVisitor visitor);
}

//具体元素-车轮
@Data
@AllArgsConstructor
public class Wheel implements Element {

    private String name;

    @Override
    public void accept(ElementVisitor visitor) {
        visitor.visit(this);
    }
}

//具体元素-车身
public class Body implements Element {

    @Override
    public void accept(ElementVisitor visitor) {
        visitor.visit(this);
    }
}

//具体元素-引擎
public class Engine implements Element {

    @Override
    public void accept(ElementVisitor visitor) {
        visitor.visit(this);
    }
}

//具体元素-整车
public class Car implements Element {

    public void accept(final ElementVisitor visitor) {
        visitor.visit(this);
    }

}

//抽象访问者
public interface ElementVisitor {

    void visit(Body body);

    void visit(Engine engine);

    void visit(Wheel wheel);

    void visit(Car car);
}

//具体的一个访问者,纯打印
@Slf4j
public class DoElementVisitor implements ElementVisitor {

    @Override
    public void visit(Body body) {
        log.info("Moving my body");
    }

    @Override
    public void visit(Engine engine) {
        log.info("Starting my engine");
    }

    @Override
    public void visit(Wheel wheel) {
        log.info("Kicking my " + wheel.getName() + " wheel");
    }

    @Override
    public void visit(Car car) {
        log.info("Starting my car");
    }
}

//单独还定义对象结构,其实完全就可以使用列表就可以
@Data
public class ElementStructure {

    private List<Element> list = Lists.newArrayList();

    public void addElement(Element element){
        list.add(element);
    }

    public void accept(ElementVisitor visitor) {
        for (Element elem : list) {
            elem.accept(visitor);
        }
    }
}

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

public class ClientWithVisitor {

    public static void main(String[] args) {
        ElementStructure structure = new ElementStructure();
        structure.addElement(new Wheel("front left"));
        structure.addElement(new Wheel("front right"));
        structure.addElement(new Wheel("back left"));
        structure.addElement(new Wheel("back right"));
        structure.addElement(new Body());
        structure.addElement(new Engine());
        structure.addElement(new Car());

        structure.accept(new DoElementVisitor());
    }
}

@Slf4j
public class ClientWithoutVisitor {

    public static void main(String[] args) {
        ElementStructure structure = new ElementStructure();
        structure.addElement(new Wheel("front left"));
        structure.addElement(new Wheel("front right"));
        structure.addElement(new Wheel("back left"));
        structure.addElement(new Wheel("back right"));
        structure.addElement(new Body());
        structure.addElement(new Engine());
        structure.addElement(new Car());

        structure.getList().forEach(e -> {
            if (e instanceof Body) {
                log.info("Moving my body");
            } else if (e instanceof Engine) {
                log.info("Starting my engine");
            } else if (e instanceof Car) {
                log.info("Starting my car");
            } else if (e instanceof Wheel) {
                log.info("Kicking my " + ((Wheel)e).getName() + " wheel");
            }
        });
    }

}

Результат печати тот же

image-20190107164438915

Проанализировано

В приведенном выше примере используются соответственно режим посетителя и режим непосещаемости.

1. Преимущества использования VIsitor очевидны с первого взгляда: когда вам нужно изменить бизнес-логику некоторых элементов, вам нужно всего лишь изменить соответствующую операционную функцию в классе Visitor. Например, если вы хотите изменить логику колеса, вам нужно изменить только метод посещения (колесо колеса) посетителя.

2. Если нам нужно добавить новый автомобильный элемент люк, нам нужно только добавить новый интерфейс в посетитель для обработки нового элемента, в то время как другие элементы могут оставаться без изменений. Нарушает принцип открытого-закрытого.

3. Когда нам нужно добавить новые бизнес-операции, нам нужно добавить только новых конкретных посетителей, а другие могут остаться без изменений. Соблюдайте принцип открыто-закрыто.

Точно так же есть преимущества и недостатки, поскольку логика находится в посетителе, все посетители и элементы сильно связаны, а возвращаемый тип метода посещения должен быть спроектирован элегантно.В противном случае, как только возвращаемый тип будет изменен позже, область действия влияние будет широким, затронуты все Как интерфейс посетителя, так и реализация. Шаблон посетителя требует, чтобы объект-посетитель имел доступ и вызывал операции каждого объекта-элемента, а это означает, что объект-элемент должен иногда раскрывать некоторые из своих внутренних операций и внутреннего состояния, иначе посетитель не сможет получить к нему доступ, что аналогично закон Деметры и нарушает принцип инверсии зависимостей.

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

Шаблон посетителя, включенный в JDK

Предоставляет удобный и поддерживаемый способ управления набором объектов. Он позволяет изменять или расширять поведение объекта без изменения объекта операции.

javax.lang.model.element.Element and javax.lang.model.element.ElementVisitor

javax.lang.model.type.TypeMirror and javax.lang.model.type.TypeVisitor

Ссылаться на

Шаблон посетителя | Учебник для новичков

Visitor pattern

Шаблоны проектирования JAVA (23): шаблоны поведения посетителей (посетитель)

Добро пожаловать в мой публичный аккаунт WeChat

微信公众号