Поиск по сайту:

Шаблон проектирования посетителей в Java


Шаблон проектирования посетителей — это один из шаблонов поведенческого проектирования.

Шаблон дизайна посетителей

Шаблон посетителя используется, когда нам нужно выполнить операцию над группой похожих объектов. С помощью паттерна посетитель мы можем перенести операционную логику из объектов в другой класс. Например, подумайте о корзине для покупок, куда мы можем добавлять различные типы товаров (элементы). Когда мы нажимаем кнопку «Оформить заказ», он вычисляет общую сумму к оплате. Теперь у нас может быть логика расчета в классах элементов или мы можем перенести эту логику в другой класс, используя шаблон посетителя. Давайте реализуем это в нашем примере шаблона посетителя.

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

Чтобы реализовать шаблон посетителя, прежде всего мы создадим различные типы элементов (элементов), которые будут использоваться в корзине. ItemElement.java

package com.journaldev.design.visitor;

public interface ItemElement {

	public int accept(ShoppingCartVisitor visitor);
}

Обратите внимание, что метод accept принимает аргумент посетителя. У нас могут быть некоторые другие методы, также специфичные для элементов, но для простоты я не буду вдаваться в подробности и сосредоточусь только на шаблоне посетителей. Давайте создадим несколько конкретных классов для разных типов элементов. Book.java

package com.journaldev.design.visitor;

public class Book implements ItemElement {

	private int price;
	private String isbnNumber;
	
	public Book(int cost, String isbn){
		this.price=cost;
		this.isbnNumber=isbn;
	}
	
	public int getPrice() {
		return price;
	}

	public String getIsbnNumber() {
		return isbnNumber;
	}

	@Override
	public int accept(ShoppingCartVisitor visitor) {
		return visitor.visit(this);
	}

}

Фрукты.java

package com.journaldev.design.visitor;

public class Fruit implements ItemElement {
	
	private int pricePerKg;
	private int weight;
	private String name;
	
	public Fruit(int priceKg, int wt, String nm){
		this.pricePerKg=priceKg;
		this.weight=wt;
		this.name = nm;
	}
	
	public int getPricePerKg() {
		return pricePerKg;
	}


	public int getWeight() {
		return weight;
	}

	public String getName(){
		return this.name;
	}
	
	@Override
	public int accept(ShoppingCartVisitor visitor) {
		return visitor.visit(this);
	}

}

Обратите внимание на реализацию метода accept() в конкретных классах, его вызов метода visit() класса Visitor и передачу самого себя в качестве аргумента. У нас есть метод visit() для разных типов элементов в интерфейсе посетителя, который будет реализован конкретным классом посетителя. ShoppingCartVisitor.java

package com.journaldev.design.visitor;

public interface ShoppingCartVisitor {

	int visit(Book book);
	int visit(Fruit fruit);
}

Теперь мы реализуем интерфейс посетителя, и у каждого товара будет своя логика расчета стоимости. ShoppingCartVisitorImpl.java

package com.journaldev.design.visitor;

public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {

	@Override
	public int visit(Book book) {
		int cost=0;
		//apply 5$ discount if book price is greater than 50
		if(book.getPrice() > 50){
			cost = book.getPrice()-5;
		}else cost = book.getPrice();
		System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
		return cost;
	}

	@Override
	public int visit(Fruit fruit) {
		int cost = fruit.getPricePerKg()*fruit.getWeight();
		System.out.println(fruit.getName() + " cost = "+cost);
		return cost;
	}

}

Давайте посмотрим, как мы можем использовать пример шаблона посетителя в клиентских приложениях. ShoppingCartClient.java

package com.journaldev.design.visitor;

public class ShoppingCartClient {

	public static void main(String[] args) {
		ItemElement[] items = new ItemElement[]{new Book(20, "1234"),new Book(100, "5678"),
				new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};
		
		int total = calculatePrice(items);
		System.out.println("Total Cost = "+total);
	}

	private static int calculatePrice(ItemElement[] items) {
		ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
		int sum=0;
		for(ItemElement item : items){
			sum = sum + item.accept(visitor);
		}
		return sum;
	}

}

Когда мы запускаем вышеуказанную клиентскую программу шаблона посетителя, мы получаем следующий вывод.

Book ISBN::1234 cost =20
Book ISBN::5678 cost =95
Banana cost = 20
Apple cost = 25
Total Cost = 160

Обратите внимание, что реализация метода accept() во всех элементах одинакова, но может различаться, например, может быть логика, чтобы проверить, свободен ли элемент, а затем вообще не вызывать метод visit().

Диаграмма классов шаблона проектирования посетителей

Преимущества шаблона посетителя

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

Ограничения шаблона посетителя

Недостаток шаблона посетителя заключается в том, что мы должны знать возвращаемый тип методов visit() во время проектирования, иначе нам придется изменить интерфейс и все его реализации. Другим недостатком является то, что если существует слишком много реализаций интерфейса посетителя, его трудно расширять. Это все, что касается шаблона дизайна посетителей, дайте мне знать, если я что-то пропустил. Пожалуйста, поделитесь им с другими, если вам понравилось.