Help us understand the problem. What is going on with this article?

オブジェクト指向設計実践ガイドのチェックシート

「オブジェクト指向設計実践ガイド」の内容が大変ためになったため、すぐに振り返れるようにチェックシートを作りました。

単一責任クラス

  • そのクラスの振る舞いとして正しいか。複数の役割を持っていないか。
  • インスタンス変数を直接参照していないか。必ずアクセサでアクセスすること。
  • 1メソッドが複数の責任をもっていないか。
複数の責任を持っている例
def diameters
  ratio * (rim + (tire * 2)) # (rim + (tire * 2)) は別のメソッドに分ける
end
  • 配列の何番目かを知っていないか。ClassやStructを使うこと。
  • 変更が副作用をもたらさないか
  • 要件の変更が小さければコードの変更も相応して小さいこと
  • 再利用可能であるか

依存関係

  • 他のクラスを知りすぎていないか。依存オブジェクトの注入に変えるべき。どうしても注入できない場合はインスタンス変数の作成をメソッドで包み隠す。
他者を知りすぎている例
class Gear
  # (1)Wheelというクラス名を知っている
  # (2)Weelインスタンスがdiameterという振る舞いができることを知っている
  # (3)Wheel.newの引数がrim, tireであることを知っている
  # (4)Whhel.newの引数の順番を知っている
  def gear_inches
    ratio * Wheel.new(rim, tire).diameter
  end
end
注入できない場合は包み隠す
def wheel
  @wheel ||= Wheel.new(rim, tire)
end
  • 外部ライブラリの初期化が引数の順番に依存していないか。ラップしてライブラリ変更に対して自身を守るべき。
外部ライブラリの初期化をラップする例
def rapper(args)
  # ThirdPartyに変更があっても影響を受けるのはここだけ
  ThirdParty.new(args[:one], args[:two], args[:three])
end
rapper({one: 1, two: 2, three: 3})
  • 依存関係が発生する場合は、将来変更されない方に依存すべき
  • 注入により 具象クラス から、◯◯メソッドを持つクラス に依存することになる。Rubyにはインタフェースがないが脳内で考えてみるべき。

インタフェース

  • オブジェクトが何を知っているか( オブジェクトの責任 )、オブジェクトが誰を知っているか( オブジェクトの依存関係 )、どのようなメッセージをするか( インタフェース )
  • 設計がうまくいかないときは メッセージ に集中すると見過ごしていたオブジェクトが明らかになる(例: Finderクラス)
  • クラスに基づく設計 から メッセージに基づく設計 に思想を切り替えること
  • メッセージとは、 「受け手を信頼し送り手が望むことを頼む」 ものであり、 「受け手がどのように振る舞うか伝える」 ものではない
メッセージの段階
(1)あなたがどのようにするか知っている # bad
(2)あなたが何をするか知っている     # not good
(3)何をどのようにするか知らないが、私が望むことに対してあなたの担当をやってくれると信じている # good

例:
(1)旅行が整備士に対し自転車を掃除してと頼む
(2)旅行が整備士に対し自転車を準備してと頼む
(3)旅行が整備士に対し「旅行の準備がしたい」と頼み、整備士が旅行から自転車を取得し準備をしてくれる
  • パブリックメソッドは、(1)そのクラスの主要な責任(2)気まぐれで更新されない(3)テストで振る舞いが定義されている
  • プライベートメソッド は、(1)実装の詳細(2)変更はあり得る(3)テストは必ずしも必須ではない

ダックタイピング

  • ダックタイピングに変えるべきアンチパターン
    • オブジェクトのクラスを確認している( case kind_of? is_a? responds_to? )
    • typecategory といった変数で処理を分けている
  • ダックタイピングには、継承やモジュールの中で 抽象的な共通処理具象化させる為のテンプレートメソッド を用意するのがよく使う手段

継承

  • 継承とは、オブジェクトを階層構造に構成するコストを払う代わりにメッセージ委譲は無料で手に入る というもの。このコストは変更時の膨大なコストに繋がる為、継承の方が良いとはっきり言える場合以外はコンポジションを使うこと。
  • 継承する場合は、「まずはメソッドをすべて下げていくつかを引き上げる」 が良い手順
  • サブクラスによって変更したい部分はメソッドで包み隠しオーバーライドできるようにする(テンプレートメソッドパターン
  • テンプレートメソッドパターンを使う場合は親にもNotImplementedErrorを発生させるメソッドを用意すること
  • superは避けフックメソッドを使うようにすべき
  • スーパークラスのメソッドは全てのサブクラスで使われなければならない。そうでないクラスはサブクラスから外れるべき。

モジュール

  • ロールが存在することを認識し、そのダックタイプインタフェースとしてモジュールを定義する
  • 大抵の場合モジュールは抽象コードでアルゴリズムを定義し、テンプレートメソッドで具象化する
  • ただし、共通で使う振る舞いがあれば、モジュールに具体的な処理を記述してもよい
  • 継承 「 である(is_a) 」モジュール 「 のように振る舞う(behaves-like-a) 」の違いは重要

コンポジション(小さなパーツの組み合わせ)

  • 継承のコストとメリットを逆転させたもの。
  • 明示的なメッセージ委譲というコストの代わりに独立して存在できるというメリット
  • 継承 より コンポジション

設計におけるまとめ

  • コンポジションクラスの継承モジュールを使った振る舞いの共有という3つのテクニックがある。
  • それぞれにコスト利点がある
  • 経験を積み重ねることで選択の誤りがなくなっていく。失敗から学びリファクタすることを恐れない

最後に、
経験を積み重ねることで選択の誤りがなくなっていく。失敗から学びリファクタすることを恐れない」 ということが本当に大事だと感じました。学ぶ為にもリファクタをし続けていきたいと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away