90
84

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-12-18

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

90
84
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
90
84

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?