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でデザパタを一通り実装してみました。
是非みなさんもデザインパターンを学んで、ウンコードを撲滅しましょう。