- Порождающие шаблоны (Creational)
- Структурные шаблоны (Structural)
- Поведенческие шаблоны (Behavioral)
Порождающие шаблоны (Creational)
абстрагирование процесса создание объектовСтроитель (Builder)
шаблон создания сложного объекта, у которого куча параметров, которые устанавливаются через сеттеры:* Pizza - класс с множеством переменных, который нужно инициализировать
* HawaiianPizzaBuilder / SpicyPizzaBuilder - классы, которые инициализируют нужные сеттеры
* Waiter - класс вызова нужного билдера (тот кто заказывает пиццу)
* BuilderTest - вызов всего вместе
package object builder { class Pizza { private var sauce: String = "" private var topping: String = "" def setSauce(sauce: String) : Unit = { this.sauce = sauce } def setTopping(topping: String) : Unit = { this.topping = topping } def outputReceipt() : Unit = { println(s"Sauce: $sauce\nTopping: $topping") } } abstract class AbstractPizzaBuilder { protected var pizza : Option[Pizza] = None def getPizza : Option[Pizza] = pizza def createPizza() : Unit = { pizza = Some(new Pizza) } def buildSauce() : Unit def buildTopping() : Unit } class HawaiianPizzaBuilder extends AbstractPizzaBuilder { override def buildSauce() : Unit = { pizza.get.setSauce("mild") } override def buildTopping() : Unit = { pizza.get.setTopping("ham+pineapple") } } class SpicyPizzaBuilder extends AbstractPizzaBuilder { override def buildSauce() : Unit = { pizza.get.setSauce("hot") } override def buildTopping() : Unit = { pizza.get.setTopping("pepperoni+salami") } } class Waiter { private var pizzaBuilder : Option[AbstractPizzaBuilder] = None def setPizzaBuilder(pizzaBuilder: AbstractPizzaBuilder) : Unit = { this.pizzaBuilder = Some(pizzaBuilder) println(s"Builder ${pizzaBuilder.getClass.getName} is set as default") } def createPizza() : Pizza = { pizzaBuilder.get.createPizza() pizzaBuilder.get.buildSauce() pizzaBuilder.get.buildTopping() pizzaBuilder.get.getPizza.get } } object BuilderTest { def main(args: Array[String]) : Unit = { val waiter = new Waiter println("Output:") waiter.setPizzaBuilder(new HawaiianPizzaBuilder) waiter.createPizza().outputReceipt() waiter.setPizzaBuilder(new SpicyPizzaBuilder) waiter.createPizza().outputReceipt() } } }
Фабричный метод ( Factory )
метод для создания нужного подкласса, в зависимости от параметрареализуется просто через case
trait Animal private class Dog extends Animal private class Cat extends Animal object Animal { def apply(kind: String) = kind match { case "dog" => new Dog() case "cat" => new Cat() } } Animal("dog")
Отложенная инициализация (Lazy)
func вызовется при первом обращении к xlazy val x = func()Пример однопоточной реализации на java:
private volatile Helper helper = null; public Helper getHelper() { if (helper == null) helper = new Helper(); return helper; }
Прототип ( Prototype )
паттерн создания объекта через клонирование другого объекта вместо создания через конструктор.class Waffle( protected var name: String, protected var primaryFilling: String, protected var specialFilling: Option[String] = None ) extends Cloneable { override def clone(): Waffle = { super.clone().asInstanceOf[Waffle] } def output() : Unit = { println(s"Waffle $name with primary filling $primaryFilling" + (if (specialFilling != None) specialFilling.get else "")) } } object PrototypeTest { def main(args: Array[String]) : Unit = { println("Output:") val chocolateWaffle = new Waffle("ChocolateWaffle", "Chocolate") chocolateWaffle.output() chocolateWaffle.clone().output() val coconutWaffle = new Waffle("CoconutWaffle","Condensed milk", Some("Coconut")) coconutWaffle.output() coconutWaffle.clone().output() } }
Одиночка (Singleton)
гарантирующий, что в однопроцессном приложении будет единственный экземпляр некоторого класса, и предоставляющий глобальную точку доступа к этому экземпляруВ java реализуется через статичную переменную
object Singleton {}
Структурные шаблоны (Structural)
Адаптер ( Adapter )
позволяет объектам с несовместимыми интерфейсами работать вместе.В Java нужен явный класс LoggerToLogAdapter который инициализируеся классом, который нужно преобразовать и наследуюется от класса к которому нужно привести
В Scala преобразование делается автоматически через подмену "implicit"
trait Log { def warning(message: String) def error(message: String) } final class Logger { def log(level: Level, message: String) { /* ... */ } } implicit class LoggerToLogAdapter(logger: Logger) extends Log { def warning(message: String) { logger.log(WARNING, message) } def error(message: String) { logger.log(ERROR, message) } } val log: Log = new Logger()В java:
Log log = new LoggerToLogAdapter(new Logger());
Мост (Bridge)
используемый в проектировании программного обеспечения чтобы «разделять абстракцию и реализацию так, чтобы они могли изменяться независимо»В объект передается класс API с конкретной реализацией:
trait DrawingAPI { def drawCircle(x: Double, y: Double, radius: Double) } class DrawingAPI1 extends DrawingAPI { def drawCircle(x: Double, y: Double, radius: Double) = println(s"API #1 $x $y $radius") } class DrawingAPI2 extends DrawingAPI { def drawCircle(x: Double, y: Double, radius: Double) = println(s"API #2 $x $y $radius") } abstract class Shape(drawingAPI: DrawingAPI) { def draw() def resizePercentage(pct: Double) } class CircleShape(x: Double, y: Double, var radius: Double, drawingAPI: DrawingAPI) extends Shape(drawingAPI: DrawingAPI) { def draw() = drawingAPI.drawCircle(x, y, radius) def resizePercentage(pct: Double) { radius *= pct } } object BridgePattern { def main(args: Array[String]) { Seq ( new CircleShape(1, 3, 5, new DrawingAPI1), new CircleShape(4, 5, 6, new DrawingAPI2) ) foreach { x => x.resizePercentage(3) x.draw() } } }
Декоратор (Decorator)
для динамического подключения дополнительного поведения к объекту.Шаблон Декоратор предоставляет гибкую альтернативу практике создания подклассов с целью расширения функциональности.
В Java нужно было бы передавать класс параметром и вызывать функции из класса-параметра.
В Scala нужные функции можно подмешать через with нужного trait (см. патерн внедрение зависимостей)
trait OutputStream { def write(b: Byte) def write(b: Array[Byte]) } class FileOutputStream(path: String) extends OutputStream { /* ... */ } trait Buffering extends OutputStream { abstract override def write(b: Byte) { // ... super.write(buffer) } } new FileOutputStream("foo.txt") with Buffering
Фасад (Facade)
позволяющий скрыть сложность системы путём сведения всех возможных внешних вызовов к одному объекту, делегирующему их соответствующим объектам системыПросто внедрение нескольких классов переменными и создание функций под методы каждого класса.
trait SubSystemA { def methodA1() def methodA2() } trait SubSystemB { def methodB() } class ConcreteSubSystemA extends SubSystemA { override def methodA1() { println("System A") } override def methodA2() { println("System A") } } class ConcreteSubSystemB extends SubSystemB { override def methodB() { println("System B") } } class Facade { val subsystemA = new ConcreteSubSystemA() val subsystemB = new ConcreteSubSystemB() override def methodA1() { subsystemA.methodA1() } override def methodA2() { subsystemA.methodA2() } override def methodB() { subsystemB.methodB() } } // Client object FacadeClient extends Application { var facade = new Facade() facade.methodA1() facade.methodA2() facade.methodB() }
Заместитель (Proxy)
предоставляющий объект, который контролирует доступ к другому объекту, перехватывая все вызовы (выполняет функцию контейнера).trait IMath { def add(x: Double, y: Double): Double } class Math extends IMath { def add(x: Double, y: Double) = x + y } class MathProxy extends IMath { private lazy val math = new Math def add(x: Double, y: Double) = math.add(x, y) } object Main extends App { val p: IMath = new MathProxy System.out.println("4 + 2 = " + p.add(4, 2)) }
Поведенческие шаблоны (Behavioral)
Цепочка обязанностей (Chain of responsibility)
предназначенный для организации в системе уровней ответственности.Каждый последующий класс добавляет или изменяет поведение.
Итератор (Cursor Iterator)
Объект, позволяющий получить последовательный доступ к элементам объекта-агрегата без использования описаний каждого из агрегированных объектов.import scala.collection.mutable.ListBuffer class AlertIterator(alertAgg: AlertAggregator) extends CustomIterator[Alert] { var list: ListBuffer[Alert] = alertAgg.list //запоминаем позицию var position: Int = 0 //и движемся, постепенно увеличивая счетчик def next(): Alert = { val alert = list(position) position += 1 alert } }
Посредник (Mediator)
обеспечивающий взаимодействие множества объектов, формируя при этом слабую связанность и избавляя объекты от необходимости явно ссылаться друг на другаNull object
инкапсулирование отсутствия объекта путём замещения его другим объектом, который ничего не делаетСинглтон SoundSource возвращает Option class = Option[Sound] , который может возвращаться Some, так и None, которые оба (для Option класса можно использовать getOrElse )
В Java пришлось бы создать спецаильный пустой объект NullSound , наследника Sound
trait Sound { def play() } class Music extends Sound { def play() { /* ... */ } } object SoundSource { def getSound: Option[Sound] = if (available) Some(music) else None } for (sound <- SoundSource.getSound) { sound.play() }
Наблюдатель или Издатель-подписчик (Observer)
Реализует у класса механизм, который позволяет объекту этого класса получать оповещения об изменении состояния других объектов, и тем самым наблюдать за ними* Набиваем массив с классами, которые будет оповещать.
* Реализуем метод, который обходит массив и выполняет publish
* Оповещаемые классы наследуем от базового (AlertObservable и AlertObserver) и делаем частную релаизацию publish
import scala.collection.mutable.ArrayBuffer class PublishAlert private extends AlertObservable { val observerList: ArrayBuffer[AlertObserver] = ArrayBuffer[AlertObserver](); def register(observer: AlertObserver): Unit = { observerList += observer; } def unregister(observer: AlertObserver): Unit = { val index: Int = observerList.indexOf(observer) observerList.remove(index) } def notifyObservers(alert: Alert): Unit = { observerList.foreach { observer => observer.publish(alert); } } } class FileAlertSubscriber(observable: AlertObservable) extends AlertObserver { observable.register(this); def publish(alert: Alert): Unit = { println("FileAlertSubscriber called") } }
Состояние (State)
объект должен менять своё поведение в зависимости от своего состояния.* Создаем несколько классов State с конкретной реализацией handle
* В основной класс Context передаем указатель на нужный State
class Context { var state:State = _ state = new NullState() def handle() { state.handle() } } // Concrete Implementation class NullState extends State { override def handle() { println("blank algorithm, maybe throw an exception of unitilized context") } } class StateA extends State { override def handle() { println("algorithm for state A") } } // Client object StateClient extends Application { var context = new Context() context.state = new StateA() context.handle() context.state = new NullState() context.handle() }
Стратегия (Strategy)
Выбор алгоритма путём определения соответствующего класса.В основной класс передаем ссылку на case class с конкретной реализацией
type Strategy = (Int, Int) => Int class Context(computer: Strategy) { def use(a: Int, b: Int) { computer(a, b) } } val add: Strategy = _ + _ val multiply: Strategy = _ * _ new Context(multiply).use(2, 3)
Шаблонный метод (Template method)
определяющий основу алгоритма и позволяющий наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.Просто наследуем класс и переопределяем часть функций:
abstract class SomeFramework { def templateMethod() { // some implementation here someAbstractMethod() // some implementation here } def someAbstractMethod() } // Concrete Implementation class MyFramework extends SomeFramework { override def someAbstractMethod() { println("partial implementation of the framework") } } // Client object TemplateMethodClient extends Application { var framework = new MyFramework() framework.templateMethod() }
Внедрение зависимостей (Dependency injection)
Внедрение зависимостей используется для выбора из множества реализаций конкретного компонента в приложенииВ scala реализуется через подмешивание trait
trait Repository { def save(user: User) } trait DatabaseRepository extends Repository { /* ... */ } trait UserService { self: Repository => // requires Repository def create(user: User) { // ... save(user) } } new UserService with DatabaseRepository
Комментариев нет:
Отправить комментарий