На информационном ресурсе применяются рекомендательные технологии (информационные технологии предоставления информации на основе сбора, систематизации и анализа сведений, относящихся к предпочтениям пользователей сети "Интернет", находящихся на территории Российской Федерации)

Практически Groovy : Функциональное программирование с помощью использованием замыканий и карринга. Часть 6

Visitor в Groovy

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

В другом сценарии система будет обходить тот же набор, но выполнять другие действия. Обычно это можно сделать с помощью шаблонов разработки Visitor (см. раздел Ресурсы). Интерфейс Visitor предоставляет протокол действия для обработки элемента коллекции. Отдельные подклассы определяют различные необходимые режимы работы. После этого вводится метод, который обходит коллекцию и применяет действие Visitor к каждому элементу.

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

Например, рассмотрим отношение "один ко многим" между классом Library и классом Book, которые используются для инвентаризации запасов библиотеки. Для реализации такого отношения можно использовать List или Map; однако у Map есть преимущество в том, что она обеспечивает быстрый поиск, скажем, по заданному в качестве ключа номеру книги по каталогу.

В листинге 9 показано простое отношение "один ко многим", использующее Map. Обратите внимание на два метода "display" класса Library. Введение visitor делает оба этих метода кандидатами на реорганизацию с использованием visitor.


Листинг 9. Приложение библиотеки

class Book {    
@Property title
@Property author
@Property catalogNumber
@Property onLoan = false

String toString() {
return "Title: ${title}; author: ${author}"
}
}

class Library {
@Property name
@Property stock = [ : ]

def addBook(title, author, catalogNumber) {
def bk = new Book(title : title, author :
author, catalogNumber : catalogNumber)
stock[catalogNumber] = bk
}

def lendBook(catalogNumber) {
stock[catalogNumber].onLoan = true
}

def displayBooksOnLoan() {
println "Library: ${name}"
println "Books on loan"
stock.each { entry ->
if(entry.value.onLoan == true) println entry.value
}
}

def displayBooksAvailableForLoan() {
println "Library: ${name}"
println "Books available for loan"
stock.each { entry ->
if(entry.value.onLoan == false) println entry.value
}
}
}

def lib = new Library(name : 'Napier')
lib.addBook('Groovy', 'KenB',
'CS123')
lib.addBook('Java', 'JohnS', 'CS456')
lib.addBook('UML', 'Ken and John',
'CS789')
lib.lendBook('CS123')
lib.displayBooksOnLoan() // Title: Groovy; author: KenB
lib.displayBooksAvailableForLoan() // Title: UML; author: Ken and John
// Title: Java; author: JohnS

 

В класс Library, который имитирует использование visitor, показанный в листинге 10, включены несколько замыканий. Замыкание action (чем-то похожее на замыкание map) применяет замыкание action к каждому элементу List. Замыкание displayLoanedBook отображает книгу в том случае, если она выдана читателю, а замыкание displayAvailableBook выводит книгу, если она в хранилище. Оба действуют как visitor и связанные действия. Применение карринга к замыканию apply с результатом displayLoanedBook в замыкании displayLoanedBooks, подготовленным для обработки коллекции книг. Похожая схема используется для обработки вывода книг, доступных для выдачи, как показано в листинге 10.


Листинг 10. Исправленный visitor библиотеки

import fp.*

class Book {
@Property title
@Property author

@Property catalogNumber
@Property onLoan = false

String toString() {
return " Title: ${title}; author: ${author}"
}

}

class Library {
@Property name
@Property stock = [ : ]

def addBook(title, author, catalogNumber) {
def bk = new Book(title : title, author :
author, catalogNumber : catalogNumber)
stock[catalogNumber] = bk
}

def lendBook(catalogNumber) {
stock[catalogNumber].onLoan = true
}

def displayBooksOnLoan() {
println "Library: ${name}"
println "Books on loan"
displayLoanedBooks(stock.values())
}

def displayBooksAvailableForLoan() {
println "Library: ${name}"
println "Books available for loan"
displayAvailableBooks(stock.values())
}


private displayLoanedBook = { bk -> if(bk.onLoan == true)
println bk }
private displayAvailableBook = { bk -> if(bk.onLoan == false)
println bk }

private displayLoanedBooks =
Functor.apply.curry(displayLoanedBook)
private displayAvailableBooks =
Functor.apply.curry(displayAvailableBook)
}

def lib = new Library(name : 'Napier')
lib.addBook('Groovy', 'KenB',
'CS123')
lib.addBook('Java', 'JohnS', 'CS456')
lib.addBook('UML', 'Ken and John',
'CS789')
lib.lendBook('CS123')
lib.displayBooksOnLoan() // Title: Groovy; author: KenB
lib.displayBooksAvailableForLoan() // Title: UML; author: Ken and John
// Title: Java; author: JohnS
Рекомендуем
Популярное
наверх