LoginSignup
1
0

More than 1 year has passed since last update.

OpenStructを用いる オブジェクト指向設計実践ガイドより

Last updated at Posted at 2021-06-15

はじめに

本題はここOpenStructからです

読者対象

読者対象はプログラミング初学者です。
といっても全くの初学者ではなく、Railsチュートリアルを終えたレベルと定義します。

オブジェクト指向設計実践ガイド

オブエジェクト指向設計実践ガイドの記事は、他の方も書いてありますが、
一度に全部まとめたものが多かったです。
この記事は、オブエジェクト指向らしく、各章ごとに、単一責任に、シンプルさを意識して書きました。
余談ですが、とある弁護士が弁護士は六法全書を丸暗記してないが、各条文を見ただけで、関係ある条文、知識を思い出すそうです。
この記事は、オブジェクト指向設計実践ガイドってこんなこと書いてあったなーと思いだすための記事でもあります。
また、オブエジェクト指向設計実践ガイドを読んだことがなくても、こういうことが書いてあるとのかー、とさわりだけでも理解して頂けたら幸いです。

オブジェクト指向設計実践ガイドで得た3つのポイント

リファクタリング前のコードを読み、コードの危うさを察知できる。
リファクタリングするために、より抽象的なコードの書き方を学べる。
gemなどの抽象的なコードを読解できる。

一問一答

一問一答風の構成にしています。
リファクタリング前のコードを読み、コードの危うさ、嗅覚を養っていきましょう。
その後、リファクタリング前のコードの危うさを説明し、自分なりにリファクタリングしたコードを書いてみましょう。
最後に、もう一度、リファクタリング前のコードと、リファクタリング後のコードを読み比べ、
抽象的な思考、抽象的な書き方を共に学んでいきましょう。

OpenStruct

今回はOpenStructについて説明します。
OpenStructを用いて、よりコードを簡潔にしていきましょう。
オブエジェクト指向実践ガイドの第8章 「コンポジションでオブジェクトを組み合わせる」、
この章を理解していると言えるでしょう。

リファクタリング前

class Bicycle
  attr_reader :size, :parts

  def initialize(args = {})
    @size = args[:size]
    @parts = args[:parts]
  end
  def spares
    parts.spares
  end
end
require 'forwardable'
class Parts
  extend Forwardable
  def_delegators :@parts, :size,:each
  include Enumerable
  def initialize(parts)
    @parts = parts
  end

  def spares
    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
module PartsFactory
  def self.build(config,part_class = Part,parts_class = Parts)
    parts_class.new(
      config.collect{ |part_config|
        part_class.new(
          name: part_config[0],
          description: part_config[1],
          needs_spare: part_config.fetch(2,true)
        )
      }
    )
  end
end
road_config = [
  ["chain","10-speed"],
  ["tire_size","23"],
  ["tape_color","Fox"]
]
mountain_config =
  [
    ["chain","10-speed"],
    ["tire_size","Manitou",false ],
    ["rear_shock","Fox"]
  ]

road_bike =
  Bicycle.new(
    size: 'L',
    parts: PartsFactory.build(road_config)
  )
road_bike.spares
mountain_bike =
  Bicycle.new(
    size: "L",
    parts: PartsFactory.build(mountain_config)
  )
mountain_bike.spares
mountain_bike.parts.size

考察

PartsFactoryモジュールのbuildメソッドが複雑ですっきりさせたいですね。
また、PartクラスとPartsFactoryモジュールが重複しています。

リファクタリング後

class Bicycle
  attr_reader :size, :parts

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

  def spares
    parts.spares
  end
end

require 'forwardable'
class Parts
  extend Forwardable
  def_delegators :@parts, :size,:each
  include Enumerable

  def initialize(parts)
    @parts = parts
  end

  def spares
    select {|part| part.needs_spare}
  end
end
require 'ostruct'
module PartsFactory
  def self.build(config,parts_class = Parts)
    parts_class.new(
      config.collect{|part_config|
        create_part(part_config)
      }
    )
  end
  def self.create_part(part_config)
    OpenStruct.new(
      name: part_config[0],
      description: part_config[1],
      needs_spare: part_config.fetch(2,true)
    )
  end
end
road_config = [
  ["chain","10-speed"],
  ["tire_size","23"],
  ["tape_color","Fox"]
]
mountain_config =
  [
    ["chain","10-speed"],
    ["tire_size","Manitou",false ],
    ["rear_shock","Fox"]
  ]

road_bike =
  Bicycle.new(
    size: 'L',
    parts: PartsFactory.build(road_config)
  )
road_bike.spares
mountain_bike =
  Bicycle.new(
    size: "L",
    parts: PartsFactory.build(mountain_config)
  )
mountain_bike.spares
mountain_bike.parts.size

OpenStruct

新たにcreate_partメソッドを作り、buildメソッドを簡潔にしました。
そして、OpenStructを用いることによって、Partクラスを全て削除できました!

後記

OpenStructのより詳しい説明を後日加筆しようか考えてます

参考文献

オブエジェクト指向設計実践ガイド ~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