デザインパターンを通して変更に強いソフトウェア設計を考える

## はじめに
Webエンジニア２年目の @nagata03 です。最近「変更に強いプログラミングができるようになりたい！」と思いデザインパターンについて個人的に勉強してみました。

普段業務ではRuby/Railsを使っているので、「[Rubyによるデザインパターン]((https://www.amazon.co.jp/Ruby%E3%81%AB%E3%82%88%E3%82%8B%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-Russ-Olsen/dp/4894712857))」という本を読んでみました。
勉強してわかったことは、大事なのはパターンそのものよりもその根底にある**原則**を意識して設計・実装することでした。
なのでこの記事では、デザインパターンを通してその根底にある原則について理解を深めていきたいと思います。

## デザインパターンって何？
よく知られているデザインパターンは、1995年に[GoF](https://ja.wikipedia.org/wiki/%E3%82%AE%E3%83%A3%E3%83%B3%E3%82%B0%E3%83%BB%E3%82%AA%E3%83%96%E3%83%BB%E3%83%95%E3%82%A9%E3%83%BC_(%E6%83%85%E5%A0%B1%E5%B7%A5%E5%AD%A6))が書籍「[オブジェクト指向における再利用のためのデザインパターン](https://www.amazon.co.jp/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E5%86%8D%E5%88%A9%E7%94%A8%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-%E3%82%A8%E3%83%AA%E3%83%83%E3%82%AF-%E3%82%AC%E3%83%B3%E3%83%9E/dp/4797311126)」で発表したデザインパターンです。その本では23種類のデザインパターンが示されています。
出版されてからもう25年近く経つのですね。

デザインパターンは、**ソフトウェア開発で出会う問題に対して頻出する解決策をパターンとして整理したもの**です。
なので、**適切な問題に適切なパターンを適用すれば問題をスマートに解決することができます**。また、各パターンには名前がつけられているので「そこは●●パターンでいこう」というように**開発者間で設計方針を明確に伝えることができます**。

## 前提にある原則
GoFは書籍の中で、ソフトウェア設計をするうえで大事にすべき原則を示しています。

- **変わるものを変わらないものから分離する**
ほぼ言葉の通りなので説明は割愛します。  

- **インターフェイスに対してプログラムし、実装に対して行わない**
疎結合なコードを書くこと。可能な限り一般的な型に対してプログラミングすること。

- **継承より集約**
オブジェクトが何かの一種である（is-a-kind-of）関係は避けて、何かを持っている（has-a）関係にすること。

- **委譲、委譲、委譲**
仕事を他のオブジェクトに押し付ける「責任転嫁」。

何となーくわかった気になる。けど、インターフェイスに対してプログラムするというのはどういうこと？委譲って具体的にはどういうこと？などの疑問が残ります。

## Strategyパターンを通して原則を理解する
サンタクロースがプレゼントを届ける機能を実装してみましょう。
サンタクラスを作り、その中にプレゼントを届けるメソッドを生やします。

```ruby:santa_sample.rb
class Santa 
  def initialize(present) 
    @present = present 
  end 
  
  def deliver(transporter) 
    if transporter == :sled
      puts "#{@present} をソリでお届け。トナカイ頑張れ。"
    elsif transporter == :car 
      puts "#{@present} を車でお届け。スピードの出し過ぎに注意。"
    elsif transporter == :airplane 
      puts "#{@present} を飛行機でお届け。航空券は早めに予約しよう。"
    else 
      raise "Unknown Transporter: #{transporter}" 
    end 
  end 
end 
  
santa = Santa.new("絵本") 
santa.deliver(:sled) 
santa.deliver(:car) 
```

実行結果は以下です。

```
$ ruby santa_sample.rb
絵本 をソリでお届け。トナカイ頑張れ。
絵本 を車でお届け。スピードの出し過ぎに注意。
```

`Santa`クラスは「**何を使って（if文の判定条件）**、**どのように（各条件での処理内容）**プレゼントを届けるか」に関心を持っています。
サンタは**プレゼントを届けることだけ**に集中し、それよりも詳細なことは気にしないでおきたいです。

まずは**変わるものを変わらないものから分離しましょう**。そして分離したものに**仕事を委譲しましょう**。


```ruby:santa_sample.rb
class Santa 
  def initialize(present, transporter) 
    @present = present
    @transporter = transporter
  end 
  
  def deliver
    if @transporter.is_a?(SledTransporter)
      @transporter.ride(@present)
    elsif @transporter.is_a?(CarTransporter)
      @transporter.drive(@present) 
    elsif @transporter.is_a?(AirplaneTransporter) 
      @transporter.fly(@present)
    else 
      raise "Unknown Transporter: #{@transporter}" 
    end 
  end 
end 

class SledTransporter
  def ride(present)
    puts "#{present} をソリでお届け。トナカイ頑張れ。"
  end
end

class CarTransporter
  def drive(present)
    puts "#{present} を車でお届け。スピードの出し過ぎに注意。"
  end
end

class AirplaneTransporter
  def fly(present)
    puts "#{present} を飛行機でお届け。航空券は早めに予約しよう。"
  end
end

santa = Santa.new("絵本", SledTransporter.new)
santa.deliver
#=> 絵本 をソリでお届け。トナカイ頑張れ。

santa = Santa.new("ぬいぐるみ", CarTransporter.new)
santa.deliver
#=> ぬいぐるみ を車でお届け。スピードの出し過ぎに注意。
```

`SledTransporter`、`CarTransporter`、`AirplaneTransporter`クラスを新たに作り、その中に輸送処理を書きました。
これで**どのように届けるか**を`Santa`クラスから分離しました。

次に**何を使って届けるか**を切り離しましょう。
今のつくりでは、`SledTransporter`、`CarTransporter`、`AirplaneTransporter`クラス毎にメソッドのインターフェイスが異なっているため、`Santa`クラスは`transporter`が何クラスであるかを意識し、その種類毎にメソッドを使い分けなければなりません。
**インターフェイスを共通化**することで、`Santa`クラスから**何を使って届けるか**の関心を分離しましょう。

```ruby:santa_sample.rb
class Santa
  def initialize(present, transporter)
    @present = present
    @transporter = transporter
  end

  def deliver
    @transporter.run(@present)  # 各Transporterで共通のrunメソッドを使う。@transporterが何クラスであるかを意識する必要はない
  end
end

class SledTransporter
  def run(present)
    puts "#{present} をソリでお届け。トナカイ頑張れ。"
  end
end

class CarTransporter
  def run(present)
    puts "#{present} を車でお届け。スピードの出し過ぎに注意。"
  end
end

class AirplaneTransporter
  def run(present)
    puts "#{present} を飛行機でお届け。航空券は早めに予約しよう。"
  end
end
```

各Transporterには`run`という共通のインターフェイスを設けました。これにより`Santa`クラスは**何を使って届けるか**を意識しなくて済むようになりました。
輸送手段が増えたり輸送処理の内容が変わったりしても、`Santa`クラスは何も気にせず**届けること**だけに集中できます。
最初の例と比べるとぐんと変更に強いつくりにすることができました。

このように「別々のオブジェクトにアルゴリズムを引き出す」テクニックをGoFでは**Strategyパターン**と呼んでいます。

## コンテキストとストラテジ間のデータの受け渡し
Strategyパターンでは、`SledTransporter`、`CarTransporter`、`AirplaneTransporter`クラスのように同じ目的（今回の例ではプレゼントを輸送すること）を持った一群のオブジェクトを**ストラテジ**、ストラテジを利用する側（今回でいう`Santa`クラス）を**コンテキスト**と呼んでいます。
コンテキストとストラテジはクラスが異なるので、データの分離にも一役買っています。その代わりコンテキストが持っている情報をストラテジが取得する方法を用意しておく必要があります。
これには基本的に2つの選択肢があります。

### 引数を使う
１つめは、これまで示してきたサンプルコードのようにコンテキストがストラテジオブジェクトのメソッドを呼び出すときに**ストラテジが必要とする情報をすべて引数で渡す方法**です。

```ruby:santa_sample.rbの抜粋
class Santa
  def initialize(present, transporter)
    @present = present
    @transporter = transporter
  end

  def deliver
    @transporter.run(@present)  # プレゼントの情報を引数で渡している
  end
end
```

この方法はコンテキストとストラテジオブジェクトが明確に分離できているのが良いところです。ただ、渡すべき情報が多くなってくると少し厳しくなります。

### コンテキスト自身の参照を使う
２つめの方法は、**コンテキスト自身の参照を使ってストラテジがコンテキストの情報を引き出す方法**です。
ストラテジオブジェクトはデータが必要になったときにコンテキストのメソッドを呼び出すことができます。

```ruby:新たなsanta_sample.rbの一部
class Santa
  attr_reader :present

  def initialize(present, transporter)
    @present = present
    @transporter = transporter
  end

  def deliver
    @transporter.run(self)  # コンテキスト自身の参照を渡す
  end
end

class SledTransporter
  def run(context)
    puts "#{context.present} をソリでお届け。トナカイ頑張れ。"  # コンテキストの参照を使って情報を取得している
  end
end
```

この方法はデータを渡すのが楽になる反面、コンテキストとストラテジ間の結合度を上げてしまうことになります。
状況によってうまく使い分けましょう。


## Rubyだとこう書ける
### ダックタイピング
Strategyパターンのクラス図は以下になります。*（「[Rubyによるデザインパターン]((https://www.amazon.co.jp/Ruby%E3%81%AB%E3%82%88%E3%82%8B%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-Russ-Olsen/dp/4894712857))」より引用）*

[![alt](http://www.plantuml.com/plantuml/svg/SoWkIImgAStDuKhEIImkLd3Epoj9hIXHgEPIK03A5IMMfAPNSjLoGQM3eI9WULrT895Va0eKoipFqz34L6P8f3eZdEhW5jhKwEeYpCKXRiG5Sx-k7LoGoo4rBmNeGm00)](http://www.plantuml.com/plantuml/svg/SoWkIImgAStDuKhEIImkLd3Epoj9hIXHgEPIK03A5IMMfAPNSjLoGQM3eI9WULrT895Va0eKoipFqz34L6P8f3eZdEhW5jhKwEeYpCKXRiG5Sx-k7LoGoo4rBmNeGm00)



このパターンに忠実に従うと、今回示した実装例はストラテジの共通のインターフェイス（`run`メソッド）を規定する`Transporter`基底クラスを用意し、各ストラテジはその`Transporter`クラスを継承する形になります。

```ruby
class Transporter
  def run(present)
    raise 'Abstract method called!'
  end
end

class SledTransporter < Transporter
  def run(present)
    # 省略
  end
end
```

しかしRubyではダックタイピング哲学より、`SledTransporter`、`CarTransporter`、`AirplaneTransporter`クラスはいずれも`run`メソッドを実装しているので、すでにどれも同じインターフェイスを共有しているとみなすことができます。
なのでわざわざ`Transporter`クラスを作ることはしないでしょう。

### Procとブロック
Procオブジェクトを使ってこんな書き方をすることもできます。

```ruby
class Santa
  def initialize(present, &transporter)  # 引数transporterに&をつけてコードブロックを受け取れるようにする
    @present = present
    @transporter = transporter
  end

  def deliver
    @transporter.call(@present)  # Procオブジェクトのcallメソッドでコードブロックを実行
  end
end

SLED_TRANSPORTER = lambda { |present| puts "#{present} をソリでお届け。トナカイ頑張れ。" }
CAR_TRANSPORTER = lambda { |present| puts "#{present} を車でお届け。スピードの出し過ぎに注意。" }

santa = Santa.new("絵本", &SLED_TRANSPORTER)
santa.deliver
#=> 絵本 をソリでお届け。トナカイ頑張れ。
```

こうすれば、ストラテジのためにあえてクラスを作る必要がなくなります。ここで出てきたクラスはコンテキストである`Santa`クラスだけになりました。

このようなコードブロックベースのストラテジは、そのインターフェイスが単純で、処理が１つのメソッドで事足りるようなときに有効です。
ストラテジにもっとやることがあるのなら、クラスベースのやり方で作ることになるでしょう。

## おわりに
今回の勉強を通してまず思ったのはシンプルに「オブジェクト指向おもしろい！」でした。GoFが示した原則にしたがって実装することでオブジェクト指向の真価やおもしろさを垣間見たと思います。
オブジェクト指向を使いこなして変更に強いソフトウェアを作っていきたいですね！

それでは、ちょっと趣向を変えてフロントエンドの世界を覗いてみましょう。
明日は @nakia さんの「[Vue.jsはじめました（ビギナー向けまとめ）](https://qiita.com/nakia/items/857e8f49195b456e9fea)」です。お楽しみに！

## 参考文献

- [Rubyによるデザインパターン（ラス・オルセン）](https://www.amazon.co.jp/Ruby%E3%81%AB%E3%82%88%E3%82%8B%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-Russ-Olsen/dp/4894712857)