Design Pattern Visitor — Посетитель

Одиннадцатый шаблон проектирования в серии.

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

А теперь простыми словами — используется этот шаблон в тех случаях, когда необходимо часто менять одну и ту же операцию (можно и больше) для некоторой группы классов. Кто-то скажет что можно просто менять методы в абстрактном классе, от которого наследуются все остальные, но это проблематично в случае с множественным наследованием, поэтому мы будем использовать интерфейс и шаблон посетитель.

От слов к делу.

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

Для начала выделим интерфейс продукта

Код пишем на котлине, поэтому можем позволить себе положить свойство прямо в интерфейс. Здесь у нас те методы, которые необходимы для отображения в чеке, вся прелесть в том, что всегда можно добавить или убрать не только из самого интерфейса метод, но и из реализации конкретного посетителя (увидим позже).

interface Good {

var quantity: Int

fun getName(): String

fun getPrice(): Int

fun getDescription(): String
}

Создадим 3 товара, которые реализуют данный интерфейс. Здесь для упрощения работы выделим абстрактный класс, который по умолчанию хранит 0 штук товара.

abstract class AbstractGood(override var quantity: Int = 0) : Good

class Telephone : AbstractGood() {

override fun getName(): String {
return «Telephone N1»
}

override fun getPrice(): Int {
return 10
}

override fun getDescription(): String {
return «Simple telephone for home and office»
}
}

class MediaPlayer : AbstractGood() {

override fun getPrice(): Int {
return 5
}

override fun getDescription(): String {
return «MediaPlayer with USB, supports all well-known audio formats»
}

override fun getName(): String {
return «MediaPLayer N2»
}
}

class Radio : AbstractGood() {

override fun getName(): String {
return «Radio N3»
}

override fun getPrice(): Int {
return 2
}

override fun getDescription(): String {
return «Portable radio station, supports all MHz»
}
}

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

interface Visitor {

fun visitTelephone(telephone: Telephone): String

fun visitMediaPlayer(mediaPlayer: MediaPlayer): String

fun visitRadio(radio: Radio): String

fun incrementQuantity(good: Good)
}

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

Взглянем на реализацию интерфейса.

class GoodVisitor : Visitor {

override fun incrementQuantity(good: Good) {
good.quantity++
}

override fun visitTelephone(telephone: Telephone): String {
return «Telephone. \n» + toString(telephone)
}

override fun visitMediaPlayer(mediaPlayer: MediaPlayer): String {
return «MediaPlayer. \n» + toString(mediaPlayer)
}

override fun visitRadio(radio: Radio): String {
return «Radio. \n» + toString(radio)
}

private fun toString(good: Good): String {
val price: Int = good.getPrice()
return «Name: » + good.getName() +
«, description: » + good.getDescription() +
«, quantity: » + good.quantity +
«, price: $» + price +
«, total: $» + good.quantity * price
}
}

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

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


Ссылка на основную публикацию