読者です 読者をやめる 読者になる 読者になる

Tbpgr Blog

Ruby プログラマ tbpgr(てぃーびー) のブログ

Ruby | Collecting Inputs | Use transparent adapters to gradually introduce abstraction

Confident Ruby Ruby

概要

Use transparent adapters to gradually introduce abstraction

前提

Confident Rubyではメソッド内の処理を次のように分類しています。
・Collecting Inputs(引数チェック、変換など)
・Performing Work(主処理)
・Delivering Output(戻り値に関わる処理)
・Handling Failure(例外処理)

当記事は上記のうち、Collecting Inputsに関する話です。

詳細

状況

以前から存在するAdapterの導入が困難な特定の型のCollaboratorへの依存性を多く持ったクラス

概要

委譲によってAdapterを作成し、より依存性の低い設計への変更を容易にする。

理由

私たちが一度に少しのステップごとに成し遂げるならば、コード内の関心の分離を改善することは、より親しみやすい問題です。

サンプルコード仕様

数値、文字列、配列、三角形、四角形のクラスをX倍にする
メソッドを定義します。
※三角形、四角形クラスは直接変更不可能,という制約があるものとしてDelegateClassを利用して委譲する

サンプルコード(委譲アダプタを利用しない場合)

require 'pp'

def multiply(input, count)
  case input
  when Triangle
    Triangle.new(input.width * count, input.height * count)
  when Square
    Square.new(input.width * count, input.height * count)
  else
    input*count
  end
end

class Triangle
  attr_accessor :width, :height
  def initialize(width, height)
    @width, @height = width, height
  end

  def area
    @width * @height / 2.0
  end
end

class Square
  attr_accessor :width, :height
  def initialize(width, height)
    @width, @height = width, height
  end

  def area
    @width * @height
  end
end

puts multiply(3, 4)
puts multiply("abc", 2)
print multiply([1,2,3], 2), "\n"
triangle = multiply(Triangle.new(2, 3), 2)
square = multiply(Square.new(2, 3), 2)
pp triangle
puts triangle.area
pp square
puts square.area
12
abcabc
[1, 2, 3, 1, 2, 3]
#<Triangle:0x26a9570 @height=6, @width=4>
12.0
#<Square:0x26a9510 @height=6, @width=4>
24

サンプルコード(委譲アダプタを利用した場合)

require 'pp'
require 'delegate'

class Triangle
  attr_accessor :width, :height
  def initialize(width, height)
    @width, @height = width, height
  end

  def area
    @width * @height / 2.0
  end
end

class Square
  attr_accessor :width, :height
  def initialize(width, height)
    @width, @height = width, height
  end

  def area
    @width * @height
  end
end

class MultiplyTriangle < DelegateClass(Triangle)
  def initialize(width, height)
    super(Triangle.new(width, height))
  end

  def *(other)
    self.width = width * other
    self.height = height * other
    self
  end
end

class MultiplySquare < DelegateClass(Square)
  def initialize(width, height)
    super(Square.new(width, height))
  end

  def *(other)
    self.width = width * other
    self.height = height * other
    self
  end
end

class Multiplyer
  def multiply(input, count)
    input*count
  end
end

puts Multiplyer.new.multiply(3, 4)
puts Multiplyer.new.multiply("abc", 2)
print Multiplyer.new.multiply([1,2,3], 2), "\n"
triangle = Multiplyer.new.multiply(MultiplyTriangle.new(2, 3), 2)
square = Multiplyer.new.multiply(MultiplySquare.new(2, 3), 2)
puts triangle.area
puts square.area
12
abcabc
[1, 2, 3, 1, 2, 3]
#<Triangle:0x26a9570 @height=6, @width=4>
12.0
#<Square:0x26a9510 @height=6, @width=4>
24