Вообще, если брать отдельно от Java и прочих таких же ограниченных языков, паттерны – хороший инструмент. Они позволяют не изобретать велосипед, а реализовывать проверенные временем фрагменты архитектуры. Проблема Java (а также C#, C++ и т. д.) в том, что все эти паттерны приходится писать богомерзким copy-paste-ом, т. к. ни реализовать их в виде библиотеки, ни ввести в синтаксис языка — невозможно.
Другое дело – Clojure. Здесь я буду приводить пример декоратора с одним методом для простоты демонстрации. Вот как я себе представляю DSL и использование его для реализации этого паттерна.
(ns jv
(:import (deco Decorable)))
(gen-deco "jv.My2" (println "Hello from jv.My2"))
(gen-deco "jv.My3" (println "Hello from jv.My3"))
Здесь генерируются два декоратора в пространстве имен jv. Скомпилировать их можно вызовом (compile 'jv), и clojure создаст два класса My2 и My3.
user=> (compile 'jv)
jv
Использовать декораторы можно так. Откроем скомпилированное пространство имен jv.
user=> (in-ns 'jv)
#<Namespace jv>
Создадим декорируемый элемент.
jv=> (def a (decorable))
#'jv/a
Его метод run выводит строку:
Hello from Decorable!
nil
Аналогично для классов My2 и My3.
jv=> (def b (jv.My2.))
#'jv/b
jv=> (.run b)
Hello from jv.My2
nil
jv=> (def c (jv.My3.))
#'jv/c
jv=> (.run c)
Hello from jv.My3
nil
Теперь используем динамическую композицию объектов и декорируем объект а функциональностью объекта b.
jv=> (decorate b a)
nil
jv=> (.run b)
Hello from jv.My2
Hello from Decorable!
nil
Теперь динамически добавим функциональность объекта c.
jv=> (decorate c b)
nil
jv=> (.run c)
Hello from jv.My3
Hello from jv.My2
Hello from Decorable!
Как видите, создание и использование паттерна декоратор ограничилось всего тремя синтаксическими конструкциями: gen-deco, decorable, decorate.
Базовый класс deco.Decorable выглядит так.
package deco;
public class Decorable {
private Decorable deco;
public void run() {
System.out.println("Hello from Decorable!");
}
public Decorable getDeco() {
return deco;
}
public void setDeco(Decorable deco) {
this.deco = deco;
}
}
Макросы, реализующие паттерн «декоратор» – ниже.
(defmacro deco-class [new-name old-name method body & params]
(let [this (symbol "this")
prefix (gensym)
new-method (symbol (str prefix method))
call-method (symbol (str "." method))]
`(do
(defn ~new-method [~this ~@params]
~body
(let [deco# (.getDeco ~this)]
(if (not (nil? deco#))
(~call-method deco# ~@params))))
(gen-class
:name ~new-name
:prefix ~prefix
:extends ~old-name))))
(defn decorate [new-obj old-obj]
(.setDeco new-obj old-obj))
(defn decorable []
(deco.Decorable.))
(defmacro gen-deco [name body & params]
(let [run (symbol "run") ]
(if (nil? params)
`(deco-class ~name deco.Decorable ~run ~body)
`(deco-class ~name deco.Decorable ~run ~body ~params))))
Таким образом, Clojure – язык, который позволяет не плодит ужасающий copy-paste для реализации паттернов, а разработать высокоуровневую абстракцию и автоматизировать все повторяющиеся фрагменты.
Комментариев нет:
Отправить комментарий