- Порождающие шаблоны (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
Комментариев нет:
Отправить комментарий