オブジェクト指向のヒューリスティクス


はじめに/ ヒューリスティクス

■はじめに さらに、どうやったら良いクラス設計が出来るのか、 という具体的な指針について、 経験的・発見的に得られた蓄積である「ヒューリスティクス」があり、 更に実際の実装設計において1つ以上のクラスが関連しあう時の 設計指針のカタログとして「デザインパターン」があります。 良いクラス設計のための貴重な薀蓄だと思えば良いでしょう。

■ヒューリスティクス
/************************************************************/
/*****                                                  *****/
/*****         ヒューリスティクス (#1 〜 #61)           *****/
/*****                                                  *****/
/************************************************************/
Heuristics for Object-orientd Analysis and Design

■Classes and Objects: The Building Blocks of the
  Object-Oriented Paradigm
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
  ◆Heuristic #1
    全てのデータはクラス内部に隠蔽されるべきである。

  ◆Heuristic #2
    クラス利用者は、クラスのパブリック・インタフェースに依存するべきだが、
    クラスの方がクラス利用者に依存してはいけない。

  ◆Heuristic #3
    クラスのプロトコルにあるメッセージ数は最小限にせよ。

  ◆Heuristic #4
    全てのクラスが理解できるような、最小限度のパブリック・インタフェース
    を実装せよ。
    (例えば、次のような操作。コピー(deep vs. shallow)、同値チェック、
     簡易印刷、ASCII表現からの構文解釈、など。)

  ◆Heuristic #5
    「common-codeプライベート関数群」のような細部を、
    クラスのパブリック・インタフェースとして実装するな。
      ※クラス内部にとっては共通的で重要なメソッドであっても、
        利用者から見て利用価値の無いメソッドは隠蔽しておけ。

  ◆Heuristic #6
    クラス利用者が使いこなせないような、とか、
    利用する気にならないようなものと、パブリック・インタフェースを、
    ごちゃごちゃに混ぜておかないこと。

  ◆Heuristic #7
    クラスは、他のクラスに対する、「外部的結合」か「無結合」のみを
    示すべきである。
    (例えば、あるクラスは、もう一方のクラスのパブリック・インタフェース
     の操作のみを利用するか、又は何もしないべきである。)

  ◆Heuristic #8
    1つのクラスは、1つの、そして1つだけの、
    キーとなる抽象性を捉えているべきである。

  ◆Heuristic #9
    関連する「データ」と「作用」は、一箇所にまとめておきなさい。

  ◆Heuristic #10
    無関係な情報は他のクラスに振り分けてしまいなさい。
    (例えば、伝達の必要の無い作用。)
      ※non-communicationg behavior
      ※クラスの中の幾つかのメソッドを見ると、お互いに全く関係の無い
        処理をしているものが一つのクラスの中に同居していることがある。
        それらは別のクラスに分けるべきである。

  ◆Heuristic #11     
    あなたがモデル化した抽象性は、「クラス」であり、
    「単にオブジェクトが演じる役割」では無い、と確信しなさい。

■Topologies of Action-Oriented Vs. Object-Oriented Applications
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
  ◆Heuristic #12
    システムの機能は、可能な限り統一的に水平に分散させなさい。
    (例えば、あるデザインのトップレベルクラス群は、
     仕事を均一に分割するべきである。)

  ◆Heuristic #13
    「神のクラス/オブジェクト」をシステムの中に創ってはいけない。
    「ドライバー」「システム」「サブシステム」というような名前を
    含む抽象化は、よく疑ってみるべきだ。

  ◆Heuristic #14
    パブリック・インタフェースにアクセッサ・メソッドが数多く
    出てくるクラスは要注意だ。
    そういったクラスの多くは、関連する「データ」と「作用」が
    一箇所にまとまってない可能性がある。
      ※データに対して必要な作用が、どこか別の箇所に
        実装されている可能性がある。

  ◆Heuristic #15
    非伝達性の作用を数多く持つクラスには要注意だ。
    例えば、純粋にクラスの一部分であるデータに作用するメソッド群
    などがそうである。「神のクラス」は、しばしば外部に対して
    非伝達性の作用を見せてしまっている。
      ※non-communicating behavior
      ※「神のクラス」は、良く見ると互いに何ら伝達するべきことの無い
        多くのメソッドの寄せ集めになっている。使う側から見ても、
        開発・保守する側から見ても、図体が大きく有り難くないクラスである。

  ◆Heuristic #16
    ユーザーインタフェースとの相互作用を持つオブジェクト指向の
    モデルで組み立てられたアプリケーションでは、絶対に、
    インタフェースに依存したモデルにしてはいけない。
    インタフェースの方がモデルに依存しているべきである。

  ◆Heuristic #17
    可能な限り、実世界をモデル化しなさい。
    (このヒューリスティックは、次のような理由からしばしば破られる。
      -システムの知能の分散配置
      -「神のクラス」の回避
      -関連する「データ」と「作用」を一箇所にまとめる )

  ◆Heuristic #18
    無関係なクラスはデザインの中から削除しなさい。
      ※クラス数の増殖を抑える目的。→SpecializationPattern

  ◆Heuristic #19
    システムの外にあるクラスは削除しなさい。

  ◆Heuristic #20
    「操作」を、「クラス」として実現しないように。
    動詞や、動詞から派生した名前を持つようなクラスには要注意。
    特に、意味のある作用をたった1つしか持たないようなクラス。
    (「意味のある作用」には、「get」「set」「print」などは数えない。)
    そのような「意味のある作用」が、既存のクラスまたは未発見の
    クラスに移住させる必要が無いか、確認せよ。
      ※例えば単純にobjectのcopyを取る操作があったとして、
        これは未発見の「オブジェクトのコピーを履歴管理する金庫クラス」
        の操作とした方が良いかも知れない。

  ◆Heuristic #21
    アプリケーションの分析モデルに、よく「エージェント・クラス」
    が策定される。デザイン段階では、無関係だったり削除される
    べきエージェントがよく見られる。

■The Relationships Between Classes and Objects
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
  ◆Heuristic #22
    あるクラスと協調動作するクラスの数は最小限にしなさい。

  ◆Heuristic #23
    あるクラスと、協調動作するものとの間で交わされる
    メッセージの数は最小限にしなさい。

  ◆Heuristic #24
    あるクラスと、協調動作するものとの間の「協調作業量」を
    最小限にしなさい。
    (例えば、送られる相異なるメッセージの数。)

  ◆Heuristic #25
    クラスの「ファンアウト」(fanout)を最低限にしなさい。
    (例えば、クラスで定義されているメッセージ数と、
     送出されたメッセージ数の積。)

  ◆Heuristic #26
    もしあるクラスが他のクラスのオブジェクトを含んでいるならば、
    含んでいる側のクラスが、含まれている側のオブジェクトに
    メッセージを送っているべきである。
    (包含関係は、大抵、利用関係を意味している。)
      ※→InterruptPattern
        含まれている側から、含んでいる側にメッセージを送るのは、
        含まれている側のクラスの再利用性を低下させる。

  ◆Heuristic #27
    クラスで定義されている大部分のメソッドによって、
    データメンバと時間の大部分を使っているべきである。
      ※ごく一部分のメソッドだけがメンバと処理時間の大半を
        消費しているようなら、もっと良い機能分割や
        メソッドの統廃合が可能かもしれない。

  ◆Heuristic #28
    クラスは、開発者の短期記憶に収まる以上の数のオブジェクトを
    含まないようにするべきである。その数は6以下が望ましい。

  ◆Heuristic #29
    システムの知能を、狭くて深い管理階層に、垂直下方に展開(分配)せよ。

  ◆Heuristic #30
    意味的制限を実装する時には、それをクラス定義として実装
    するのが最も良い。しかしこのことは、次のような場合に
    クラス数の増殖につながる。それは、この意味的制限が、
    クラスの作用として実装され、そして大抵、コンストラクタには
    実装される必要のない時である。

  ◆Heuristic #31
    意味的制限をクラスのコンストラクタに実装する時は、
    コンストラクタでの制限検査を、ドメインの許すかぎり、
    管理階層の下方に位置付けよ。

  ◆Heuristic #32
    制限のベースとなっている意味的情報は、その情報が揮発性である時、
    中心的なサードパーティー・オブジェクトに位置付けられているのが
    最も良い。

  ◆Heuristic #33
    制限のベースとなっている意味的情報は、その情報が不揮発性である時、
    制限に影響を受けるクラス群内に分散されているのが最も良い。

  ◆Heuristic #34
    あるクラスは、何を含んでいるかを知っている必要があるが、
    何に含まれているのかは決して知るべきではない。

  ◆Heuristic #35
    語彙範囲を共有するオブジェクト群は、それらの間で利用関係を
    持つべきではない。
    (例えば、同じクラスに包含されるオブジェクト群)
      ※lexical scope

■The Inheritance Relationship
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
  ◆Heuristic #36
    継承は、「特殊化階層」のモデルとしてのみ使われるべきである。

  ◆Heuristic #37
    派生クラスは、定義によって基本クラスのことを知っているはずだが、
    基本クラスは、派生クラスについて何事も知っていてはならない。

  ◆Heuristic #38
    基本クラスの全てのデータはプライベートであるべきだ。
    例えば、プロテクトデータは使うべきでない。

  ◆Heuristic #39
    理論上、継承階層は深くあるべきだ。
    (深ければ深い程良い。)

  ◆Heuristic #40
    実践的には、平均的な人間の短期記憶に収まる以上には
    継承階層を深くしない方が良い。
    一般的には6段程度とされている。

  ◆Heuristic #41
    全ての抽象クラスは基本クラスであるべきである。
      ※継承元のクラスを基本クラス、継承先のクラスを派生クラスという。
      ※実体のない抽象メソッドを含むクラスを抽象クラスという。
        抽象クラスがそれ自体のインスタンスを生成することはできない。
        派生クラスにおいて抽象メソッドをオーバーライドしなかった場合、
        その派生クラスも抽象クラスとなる。

  ◆Heuristic #42
    全ての基本クラスは抽象クラスであるべきである。
      ※GeneralizationPattern
        抽象クラスによって、柔軟性を確保できる。
        但し、柔軟性のために無闇に抽象クラスを策定すると、
        管理するクラスが多くなってクラス群の維持が大変になる。

  ◆Heuristic #43
    共通のデータ、作用、インタフェースは、継承階層の出来るだけ高い
    ところに因子化しておくこと。

  ◆Heuristic #44
    もし、2つかそれ以上のクラスが(共通作用でなく)共通データを共有
    しているだけならば、その共通データをあるクラスの中に置き、
    そのクラスを各共有クラスに含ませるようにするべきである。

  ◆Heuristic #45
    もし、2つかそれ以上のクラスが、共通データや共通作用(メソッドなど)
    を持っているならば、それらは、その共通データやメソッドを捉えている
    共通基本クラスから、各々継承されるべきである。

  ◆Heuristic #46
    もし、2つかそれ以上のクラスが、(メソッドではなく、メッセージなど
    の)共通インタフェースのみを共有しているならば、多態的に使われる
    時に限り、共通基本クラスから、各々継承されるべきである。

  ◆Heuristic #47
    オブジェクトの「型」による明示的なケース分類は、大抵、誤りである。
    設計者は、多くの場合、多態性を利用するべきである。

  ◆Heuristic #48
    属性の値による明示的なケース分類は、大抵、誤りである。
    このクラスは、各属性値が派生クラスに対応するような
    継承階層に分解してみるべきである。

  ◆Heuristic #49
    クラスの動的な意味を、継承関係を用いてモデル化してはいけない。
    動的な意味を静的な意味関係でモデル化しようと試みると、
    実行時の「型のトグル動作」を引き起こすことになる。

  ◆Heuristic #50
    あるクラスのオブジェクト群を、そのクラスからの派生クラスだと
    見倣さないように。1つしかインスタンスを持たないような
    「派生」クラスは、疑ってみるべきである。
      ※The One Instance Pattern

  ◆Heuristic #51
    もし実行時に新しいクラスを作成する必要があると感じたら、
    一歩ひいて、今やろうとしていることは、オブジェクトの作成なのだ
    ということを確認しなさい。そして、それらのオブジェクトを、
    あるクラスに一般化しなさい。

  ◆Heuristic #52
    派生クラスが、基本クラスのメソッドをNOPメソッドでオーバーライド
    するのは不法行為である。(例えば、何もしないメソッドなど。)
      ※The Inverted Inheritance Pattern
        派生クラスのNOPによるオーバーライドを許せば、理論的には
        あらゆる無関係なクラスを継承関係にすることができてしまう。

  ◆Heuristic #53
    付加的な制限と、継承の必要性を混同しないこと。
    付加的な制限を継承によってモデル化すると、
    クラス数の増加につながる。

  ◆Heuristic #54
    継承階層を構築しようとする時には、再利用可能な「部品」というよりは、
    むしろ再利用可能な「フレームワーク」を創ろうと試みよ。

■Multiple Inheritance
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
  ◆Heuristic #55
    もし設計の中に多重継承の例があったら、何か誤りがあったと考え、
    他の場合への改善を試みよ。

  ◆Heuristic #56
    オブジェクト指向の設計に継承があったら、必ず以下の2点を
    自分に問いかけてみよ。
    (1)私は、継承元に対する特殊例になっているだろうか?
    (2)継承元は、私の一部になっているだろうか?

  ◆Heuristic #57
    オブジェクト指向設計に多重継承関係を見付けたら、
    実際には「他の基本クラスから派生する基本クラスなど無い」
    ということを確認せよ。
    例えば、偶然多重継承になっているとか。

■The Association Relationship
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
  ◆Heuristic #58
    オブジェクト指向設計で、管理関係と連携関係を選ぶ機会があったら、
    抑止関係の方を選択しなさい。
      ※containment relationship and association relationship

■Class Specific Data and Behavior
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
  ◆Heuristic #59
    クラス内のオブジェクト群に対する簿記情報となるように
    グローバルデータを使ってはならない。
    そのかわりにクラス変数を利用するべきである。

■Physical Object-Oriented Design
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
  ◆Heuristic #60
    オブジェクト指向の設計者は、決して物理設計の判断基準が
    論理設計を壊すのを許してはならない。しかしながら、
    非常にしばしば、物理設計の判断基準が論理設計時の
    決断に利用される。

  ◆Heuristic #61
    パブリック・インタフェースを介さずにオブジェクトの状態
    を変更してはならない。
      ※The Broadcast Pattern


■メモ
 ・元資料は、大雑把には以下のように構成されている。
      ◆オブジェクト指向設計のヒューリスティクスとは?
      ◆「神のクラス」を避けるには
        -アクセッサメソッドについて
        -知能の分散
        -必要条件のチェック
        -heuristics #1 〜 #6
      ◆「クラス数の増殖」を避けるには
        -本質的なトレードオフ『複雑さを低減させるのと、柔軟性を
         増すのとは、どちらがより重要なのだろうか?』
        -heuristics #1 〜 #10
      ◆設計変化型(Design Transformation Patterns)
        -The Broadcast Pattern
           ※共有オブジェクトの状態変化は所有オブジェクト群全てに
             通知されるべきである。
        -The Interrupt Pattern
           ※包含されているオブジェクトから包含しているオブジェクトに
             メッセージを送る場合、包含される側のクラスの再利用性は
             低下してしまう。
        -The Interrupt-polling Pattern
           ※包含する側が、包含される側に情報を取りに行く方が、
             包含される側の独立性、再利用性が高く好ましい。
        -The Generalization Pattern
           ※抽象クラスとしての基本クラスを策定せよ。
        -The Specialization Pattern
           ※クラス数を増殖させないため、基本クラスを排除せよ。
             「The Generalization Pattern」と「The Specialization
              Pattern」は、時と場合によって使い分ける必要がある。
             具象基本クラスに、将来、派生クラスが必要としない
             情報が付加されそうなら、抽象基本クラスを策定せよ。
        -The Invereted Inheritance Pattern
           ※基本クラスのメソッドをNOP(無操作)で置換するな。
             NOPに関するメソッドを含まない、共通の抽象クラスを
             策定せよ。
        -The Lexical Scope Pattern
           ※クラス内に包含されているオブジェクト間同士で利用関係を
             持たず、包含するクラスによって制御されるようにせよ。
        -The One Instance Pattern
           ※1つしかインスタンスを持たない派生クラスは、基本クラス
             から直接得られるオブジェクトに出来ないか検討せよ。
        -The Data Hiding Pattern
           ※全てのクラス保有データは厳密にプライベートとせよ。
             -最悪、パブリックメソッドは、単にアクセッサメソッドに
              なるかも知れない。
             -最良の場合には、クラスが表現する、キーとなる抽象性が、
              より高級な作用の必要性を含んでいる。
      ◆将来の研究
        (1)全てのヒューリスティクスに対応する設計変化型を見付け、
           文書化、カタログ化すること。
        (2)型に対応する良いカテゴリサイエンスを探す。
        (3)設計変化型の他の関係や特性を探す。
        (4)ユースケースが及ぼす設計へのインパクトを考察する。
        (5)文法のオブジェクト指向設計への写像を考察する。
           ※文法は、より複雑になるのを許す。これを考察することで、
             オブジェクト指向設計の成長計画の雛型を見付けようとする。
        (6)設計変化型を多数揃えて、設計の最適化を自動的に行なわせる
           ようなツールを作れないか?
      ◆ヒューリスティクス(まとめ)
        -heuristics #1 〜 #61 

      ※「設計変化型」(design transformation patterns)
        …あるパターン(型)は、こういう理由によって、このような
          パターンに変化させるべきだ、という対応を示したもの。
          元資料では、The Broadcast Pattern 〜 The Data Hiding Pattern
          の9つの変形を例示している。

  ・以下の語彙については、より良い訳を探している。

    (#10) (#15) ・non-commnunicating behavior 
           「非伝達性作用」
    (#29)・containment hierarchies 「管理階層」
    (#58)・containment relationship / association relationship
           「管理関係」/「連携関係」

[参考文献]
  OOPSLA'95
  “Object-oriented Design Heuristics and 
    Their Relationship to Design Patterns”
      October 15-19 Austin Texas
      Artur J.Reil

[改版履歴]
    第0.0版 1996.05.01 hijk 
            ・元資料入手。極一部の対訳をメモ程度で示す。
    第0.3版 1996.05.07 hijk
            ・元資料の全部を詳細に読み、誤訳や、こなれていない
              部分を多少訂正。
              system intelligence →「システムの知能」?
            ・関連する項目や補足を随時「※マーク」後に挿入した。
    第0.4版 2006.03.18 hijk
            ・冒頭部分に導入を加えて、表現を見直し。