1
0

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 3 years have passed since last update.

コンポジション オブジェクト指向設計実践ガイドより

Last updated at Posted at 2021-06-15

#はじめに
本題はここコンポジションからです
##読者対象
読者対象はプログラミング初学者です。
といっても全くの初学者ではなく、Railsチュートリアルを終えたレベルと定義します。
##オブジェクト指向設計実践ガイド
オブエジェクト指向設計実践ガイドの記事は、他の方も書いてありますが、
一度に全部まとめたものが多かったです。
この記事は、オブエジェクト指向らしく、各章ごとに、単一責任に、シンプルさを意識して書きました。
余談ですが、とある弁護士が弁護士は六法全書を丸暗記してないが、各条文を見ただけで、関係ある条文、知識を思い出すそうです。
この記事は、オブジェクト指向設計実践ガイドってこんなこと書いてあったなーと思いだすための記事でもあります。
また、オブエジェクト指向設計実践ガイドを読んだことがなくても、こういうことが書いてあるとのかー、とさわりだけでも理解して頂けたら幸いです。
##オブジェクト指向設計実践ガイドで得た3つのポイント
リファクタリング前のコードを読み、コードの危うさを察知できる。
リファクタリングするために、より抽象的なコードの書き方を学べる。
gemなどの抽象的なコードを読解できる。
##一問一答
一問一答風の構成にしています。
リファクタリング前のコードを読み、コードの危うさ、嗅覚を養っていきましょう。
その後、リファクタリング前のコードの危うさを説明し、自分なりにリファクタリングしたコードを書いてみましょう。
最後に、もう一度、リファクタリング前のコードと、リファクタリング後のコードを読み比べ、
抽象的な思考、抽象的な書き方を共に学んでいきましょう。
#コンポジション
今回は第8章コンポジションについて説明します。
下記リファクタリング前のコードは継承、フックメッセージが適切に使われおり、十分に使えるコードです。
今回は、継承を使わない観点、コンポジションという観点からリファクタリングして見て下さい。
オブエジェクト指向実践ガイドの第8章 「コンポジションでオブジェクトを組み合わせる」、
この章を一部理解していると言えるでしょう。(残りの知識は別記事に書きます)
##リファクタリング前

class Bicycle
  attr_reader :size, :parts

  def initialize(args = {})
    @size = args[:size]
    @parts = args[:parts]
  end
  def spares
    parts.spares
  end
end

class Parts
  attr_reader :chain, :tire_size

  def initialize(args={})
    @chain = chain || default_chain
    @tire_size = tire_size || default_tire_size
    post_initialize(args)
  end

  def spares
    {
      tire_size: tire_size,
      chain: chain
    }.merge(local_spares)
  end

  def post_initialize(args)
    nil
  end

  def local_spares
    {}
  end

  def default_chain
    "10-speed"
  end

  def default_tire_size
    raise NotImplementedError
  end
end

class RoadBikeParts < Parts
  attr_reader :tape_color

  def post_initialize(args)
    @tape_color = args[:tape_color]
  end

  def local_spares
    {tape_color: tape_color}
  end

  def default_tire_size
    "23"
  end
end

class MountainBikeParts < Parts
  attr_reader :front_shock, :rear_shock

  def post_initialize(args)
    @front_shock = args[:front_shock]
    @rear_shock = args[:rear_shock]
  end

  def local_spares
    {rear_shock: rear_shock}
  end

  def default_tire_size
    "2.1"
  end
end
road_bike = Bicycle.new(size: "L",parts: RoadBikeParts.new(tape_color: "red"))
road_bike.size
road_bike.spares

###継承を使わない書き方
Partsクラスを各サブクラスが継承しています。
継承を使わない観点からリファクタリングしてみましょう。
##リファクタリング後

class Bicycle
  attr_reader :size, :parts

  def initialize(args = {})
    @size = args[:size]
    @parts = args[:parts]
  end
  def spares
    parts.spares
  end
end

class Parts
  attr_reader :parts

  def initialize(parts)
    @parts = parts
  end

  def spares
    parts.select{|part| part.needs_spare}
  end
end

class Part
  attr_reader :name, :description, :needs_spare

  def initialize(args)
    @name = args[:name]
    @description = args[:description]
    @needs_spare = args.fetch(:needs_spare,true)
  end
end
chain = Part.new(name: "chain",description: "10-speed")
road_tire =
  Part.new(name: "tire_size",description: "23")
tape =
  Part.new(name: "tape",description: "red")
mountain_tire =
  Part.new(name: "tire_size",description: "2.1")
rear_shock =
  Part.new(name: "rear_shock",description: "Foc")
front_shock =
  Part.new(name: "front_shock",
           description: "Manitou",
           needs_spare: false)
road_bike_parts =
  Parts.new([chain,road_tire,tape])
road_bike =
  Bicycle.new(
    size: "L",
    parts:   road_bike_parts
  )
road_bike.size
road_bike.spares

##コンポジション
かなりすっきりしましたね!
ただ、インスタンス作成の処理がごちゃごちゃしているので、
まだ改善の余地はあります。(後日、別記事で書きます)
継承使って書くか?
コンポジションで書くか?
私の現在のレベルではうまく説明できません。
引用します。

一般的なルールとしては、直面した問題がコンポジションによって解決できるなら、
コンポジションで解決することを優先するべきです。(中略)
継承がより良い選択肢であるのは、継承が低いリスクで高い利益を生み出してくれるときです。
 『オブジェクト指向設計実践ガイド』 p229

#参考文献
オブエジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方

1
0
0

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?