ScalaDay 16

Scalaで今更GoFのデザインパターンを書いてみた

More than 3 years have passed since last update.

Scala Advent Calender 2014の記事です。遅れてすみません。

デザインパターンをScalaで書いてみるとどうなるか?ということで、今更ですがGoFのデザインパターンをScalaで書いてみました。デザインパターンについての説明は他の人にお任せしますw

Wikipedia

に載っている順に紹介していきます。なかには、流石Scalaといったのもありますが、Javaとかとあまり変わらないのもあります。


AbstractFacotry

Javaとかとほとんど変わりません。

trait AbstractFactory{

def createA() : A
def createB() : B
def createC() : C
}

class ConcreteFactory extends AbstractFactory{

def createA() : A = ...
def createB() : B = ...
def createC() : C = ...
}


Builder

自己タイプを使うことで、実装先でメソッドを追加したときに便利にできます。


trait Builder{ self =>

def withA(a : A) : self.type
def withB(b : B) : self.type
def build() : T

}

class ConcreteBuilder extends Builder{
def withA(a : A) = { ...; this}
def withB(b : B) = { ...; this}
def withC(c : C) = { ...; this}
def build() = ...
}

new ConcureteBuilder().withA(a).withC(c).build()


FactoryMethod

traitが実装を持てるので、簡単に様々な組み合わせがつくれます。


trait FactoryMethodUser{

def doAction() = {
val product = createProduct()
product.use()
}
def createProduct() : Product
}

trait AFactory{ self : FactoryMethodUser =>
def createProduct() = new ProductA()
}
trait BFactory{ self : FactoryMethodUser =>
def createProduct() = new ProductB()
}

object ForA extends FactoryMethodUser with AFactory
object ForB extends FactoryMethodUser with BFactory


Prototype

case classで一撃。(ただし、コピーしたい変数は全てコンストラクタに書く必要あり

case class Proto(a : Int, b : String, c : Double)

val proto = Proto(1,"aaa",2.4)

val copied = proto.copy()


Singleton

objectはSingletonです。

object Singleton


Adapter

Javaとあまり変わらず。


class A{
def name : String = ...
}
class B{
def namae : String = ...
}

trait NameAdapter{
def name : String
}

class AAdapter(a : A) extends NameAdapter{
def name = a.name
}
class BAdapter(b : B) extends NameAdapter{
def name = b.namae
}


Bridge

Javaとあまり変わらず。


trait Abstraction{
def implementor : Implementor
}

trait Implementor{
}

case class RefinedAbstraction(implementor : Implementor) extends Abstraction
class RefinedImplementor() extends Implementor


Composite

Javaとあまり変わらず。

trait Component{

def operation() : T
}

class Leaf extends Component {
def operation() : T = ...
}

class Composite extends Component {
def operation() : T = ...
def add(child : Component) = ...
}


Decorator

abstract traitを使用することでデコりたい機能だけを分離実装可能


trait DecoratedObject{
def funcA()
}

class ImplA extends DecoratedObject{
def funcA() = ...
}
class ImplB extends DecoratedObject{
def funcA() = ...
}

trait LoggingDecorator{
abstract override def funcA() = {
print("Logging")
super.funcA()
}
}

class DecoratedA extends ImplA with LoggingDecorator
class DecoratedB extends ImplB with LoggingDecorator


Facade

Javaと変わらず


class Facade(a : A){
def walk(speed : Int) = {
a.goFront(speed)
}
}


Flyweight

mutableMap使うと楽

import scala.collection.mutable.Map

class FlyWeight{
val map = Map.empty[String,HeavyObject]

def get(key : String) = {
map.getOrElse(key,{
createHeavyObject(key)
})
}
}

また、ConcurrentHashMapを使うと非同期対応も楽々できます。


Proxy

implicit conversionを使ったテクニックで楽にかけます。


case class User(name : String, age: Int)

object ProxyUser{
implicit def toUser(pu : ProxyUser) = pu.user
}
case class ProxyUser(user : User){
def name = "Mr." + user.name
}

val proxy = ProxyUser(User("Tom",20))

println(proxy.name) // "Mr.Tom"
println(proxy.age) // "20"


Chain of Responsibility

PartialFunctionで実装できます。

val responsibility1 : PartialFunction[String,Unit] = {

case "Hello" => println("Hello world")
case "こんにちは" => println("こんにちは世界")
}

val responsibility2 : PartialFunction[String,Unit] = {
case "你好" => println("你好世界")
}

val chain = responsibility1 orElse responsibility2

println(chain("Hello")) // "Hello world"
println(chain("你好")) // "你好世界"


Command

もう関数で良くね?


val command1 = (s : String) => println(s)
val command2 = (s : String) => println(s + s)

val commands = List(command1,command2)

commands.foreach( _("hoge"))


Interpreter

ParserCombinator使えばいいと思うよ!ここには書ききれないのでこことか見てください。


Iterator

もう、当たり前過ぎてこれデザインパターンか?ってレベル

for(i <- 0 until 100){

println(i)
}


Mediator

Javaと変わらないので割愛


Memento

immutableに実装すればだいたい同じことできます。sbtみたいな感じ。

case class Memento(state1 : Int , state2 : String){

def withState1( s : Int) = this.copy(state1 = s)
}

val first = Memento(1,"hoge")
val memo = first.withState1(2).copy(state2 = "aaa")
val last = memo.copy(state1 = 4,state2 = "cccc")

print(memo) // "Memento(2,"aaa")"


Observer

記号メソッドを使えるので、C#のeventみたいにいい感じにかけます。


class Subject{
val event = new Event
def fireEvent() = event.trigger("hoge")
}

class Event{
private var handlers : List[String => Any] = Nil
def +=( handler : String => Any) = handlers = handlers :+ handler
def trigger(v : String) = handlers.foreach(_(v))
}
var subject = new Subject()
subject.event += s => println(s)
subject.event += s => if(s.startsWith("h")) println("のび太さんのH")

subject.fireEvent()


State

Javaと大差ないので割愛。

むしろ、Mutableな実装になりやすいのでScalaではあまり使いたくないパティーン


Strategy

traitで実現できます。

または、implicitパラメーターも有りかと

trait Strategy{

def execute()
}

trait StrategyA extends Strategy{
def execute() = ...
}
trait StrategyB extends Strategy{
def execute() = ...
}

trait Action{ self : Strategy =>
def action() = {
execute()
}
}

val a = new Action with StrategyA
a.action()
val b = new Action with StrategyB
b.action()

implicit parameterだと

trait Strategy{

def execute()
}

class StrategyA extends Strategy{
def execute() = ...
}
class StrategyB extends Strategy{
def execute() = ...
}

class Action{
def action()(implicit strategy : Strategy) = {
strategry.execute()
}
}

{
implicit val strategy = new StrategyA
val action = new Action()
action.action()
}

{
implicit val strategy = new StrategyB
val action = new Action()
action.action()
}


TemplateMethod

traitのmixinを使うことで、機能を小分けにしやすいです。

trait Base { self : FuncA with FuncB => {

def work() = {
val v = doA()
val result = doB(v)
result
}
}

trait FuncA{
def doA() : String
}
trait FuncB{
def doB(s : String) : Int
}

object Imple1_1 extends Base with FuncAImpl1 with FuncBImpl1
object Imple1_2 extends Base with FuncAImpl1 with FuncBImpl2
object Imple2_1 extends Base with FuncAImpl2 with FuncBImpl1


Visitor

Javaと変わらず。むしろ、わりと避けるべきデザインパターン。


総括

途中力尽きたところもありましたが、Scalaでデザパタを一通り実装してみました。

是非みなさんもデザインパターンを学んで、ウンコードを撲滅しましょう。