〈完全なプログラミング〉を目指す会 2020

第二回 オブジェクト指向プログラミング

yewton

オブジェクト指向
プログラミング

〈完全なプログラミング〉を目指す会 2020

の前に

〈完全なプログラミング〉を目指す会 2020

復習

〈完全なプログラミング〉を目指す会 2020

〈完全なプログラミング〉

とは?

〈完全なプログラミング〉を目指す会 2020

「読めば分かるコードを書く」
「必要十分なドキュメントを書く」

〈完全なプログラミング〉を目指す会 2020

余計なモノを
一切必要としない

ソフトウェアづくり

〈完全なプログラミング〉を目指す会 2020

その心は?

〈完全なプログラミング〉を目指す会 2020

コードは圧倒的に
人間によって読まれる

〈完全なプログラミング〉を目指す会 2020

故に〈完全なプログラミング〉は
圧倒的に はやい

〈完全なプログラミング〉を目指す会 2020

さらに

〈完全なプログラミング〉を目指す会 2020

品質の改善は
コストを削減する

〈完全なプログラミング〉を目指す会 2020

故に〈完全なプログラミング〉は
圧倒的に やすい

〈完全なプログラミング〉を目指す会 2020

はやい
やすい

〈完全なプログラミング〉を目指す会 2020

うまい
😋

〈完全なプログラミング〉を目指す会 2020

三つの原則

〈完全なプログラミング〉を目指す会 2020

KISS
DRY
YAGNI

〈完全なプログラミング〉を目指す会 2020

なぜ〈原則〉を知ってほしいか

〈完全なプログラミング〉を目指す会 2020

なぜ 〈原則〉になった のか

〈完全なプログラミング〉を目指す会 2020

原則と呼ばれるものの多くは
経験則

多くの人が同意した
ベストプラクティス集 みたいなもん

〈完全なプログラミング〉を目指す会 2020

同じ轍を踏むことのないよう

DRY に生きよう

〈完全なプログラミング〉を目指す会 2020

今日持ち帰って欲しいこと

  1. オブジェクト指向でなぜつくるのか
  2. よいクラスとは何か
  3. ポリモーフィズムのうまみ
〈完全なプログラミング〉を目指す会 2020

オブジェクト指向の
歴史 に学ぶ

〈完全なプログラミング〉を目指す会 2020

問題

〈完全なプログラミング〉を目指す会 2020

オブジェクト指向の発明
オブジェクトやクラスの登場
どちらが先か

〈完全なプログラミング〉を目指す会 2020

正解は

〈完全なプログラミング〉を目指す会 2020

オブジェクトやクラスの登場

〈完全なプログラミング〉を目指す会 2020
  • 先に オブジェクトクラス という概念を盛り込んだ 言語 が作られた
    • 対象とする問題領域の表現に有用だったから
    • 当初は オブジェクト指向という言葉はなかった
  • 言語から着想を得てオブジェクト指向という概念が生まれ、その概念を体現するオブジェクト指向言語が生まれていった
〈完全なプログラミング〉を目指す会 2020

問題

〈完全なプログラミング〉を目指す会 2020

オブジェクトやクラスが
最初に登場した言語
とは?

〈完全なプログラミング〉を目指す会 2020

正解は

〈完全なプログラミング〉を目指す会 2020

Simula 67

〈完全なプログラミング〉を目指す会 2020

1967年 - Simula 67

1980年 - Smalltalk

1983年 - C++

〈完全なプログラミング〉を目指す会 2020

何が言いたいか

〈完全なプログラミング〉を目指す会 2020

オブジェクト指向とは、

複雑な問題解決のために
遥か昔に発明され、今日まで現役の
「問題の考え方」

〈完全なプログラミング〉を目指す会 2020

役に立たない
ワケがない

〈完全なプログラミング〉を目指す会 2020

問題

〈完全なプログラミング〉を目指す会 2020

クラス とは?

〈完全なプログラミング〉を目指す会 2020

正解は

〈完全なプログラミング〉を目指す会 2020

クラスとは、強くて明確な責務(responsibility)を共有するデータとルーチンの集まりである。
— CODE COMPLETE 第6章 クラスの作成

〈完全なプログラミング〉を目指す会 2020

どうして クラス を作るのか?

〈完全なプログラミング〉を目指す会 2020

プログラム全体を一気に頭に詰め込むべきではない。
プログラムの部分ごとに集中できるように、プログラムを整理して、一度に検討するプログラムの量は、最小限にとどめることを目指すべきである。
— CODE COMPLETE 5.2.1 ソフトウェアの鉄則:複雑さへの対処

〈完全なプログラミング〉を目指す会 2020

有能なプログラマは自分の脳味噌のサイズがいかに制限されたものであるかをよく心得ています
— エドガー・W・ダイクストラ 謙虚なるプログラマ (The Humble Programmer)

〈完全なプログラミング〉を目指す会 2020

こんなときに クラス を作る

〈完全なプログラミング〉を目指す会 2020

現実世界のオブジェクト
または抽象的なオブジェクト( Circle に対する Shape など )を
モデリングする

〈完全なプログラミング〉を目指す会 2020

プログラミングでは、抽象化がShapeのように用意されているわけではないので、
つじつまの合った抽象化を何とか考え出さなければならない。
適切な抽象オブジェクトを考え出すことは、オブジェクト指向設計における主な課題の1つである。
— CODE COMPLETE 6.4 クラスを作成する理由

〈完全なプログラミング〉を目指す会 2020

複雑さを 緩和分離隠蔽 する

〈完全なプログラミング〉を目指す会 2020

クラスを作成する最も重要な理由は、プログラムの複雑さを低減することである。
クラスの抽象化の威力を利用しなければ、複雑なプログラムを頭で整理することは不可能である。
— CODE COMPLETE 6.4 クラスを作成する理由

〈完全なプログラミング〉を目指す会 2020

その他

  • 変更による影響を限定する
  • グローバルデータを隠蔽する
  • 引数の受け渡しを合理化する
  • 制御を一元化する
  • コードの再利用を促進する
  • プログラムのファミリを計画する
    • 独立したプログラムの組み合わせでソフトウェアを構成する
  • 関連する操作をパッケージにまとめる
  • 特定のリファクタリングを実行する
〈完全なプログラミング〉を目指す会 2020

望ましくない
クラス

〈完全なプログラミング〉を目指す会 2020

God クラス

〈完全なプログラミング〉を目指す会 2020

全知全能のクラスを作成してはならない。
クラスがGet()ルーチンやSet()ルーチンを使って他のクラスからデータを取得することに明け暮れているなら
(つまり、他人の領分に首を突っ込み、あれこれ口出ししているようなら)、
それをゴッドクラスにまとめるよりも、他のクラスに整理する方がよいかどうか検討しよう
— CODE COMPLETE 6.4.1 望ましくないクラス

〈完全なプログラミング〉を目指す会 2020

FooManager
BarHelper
FizzHandler
BuzzInfo

〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

Object Calisthenics
オブジェクト指向健康体操

〈完全なプログラミング〉を目指す会 2020

クラスは 50行まで

〈完全なプログラミング〉を目指す会 2020

パッケージは 10ファイルまで

〈完全なプログラミング〉を目指す会 2020

インスタンス変数は 2個まで

〈完全なプログラミング〉を目指す会 2020

" . " は1行につき 1個まで

(デメテルの法則)

〈完全なプログラミング〉を目指す会 2020

よい クラス

〈完全なプログラミング〉を目指す会 2020

SOLID

〈完全なプログラミング〉を目指す会 2020

単一責務の原則
Single responsibility principle

〈完全なプログラミング〉を目指す会 2020

クラスを変更する理由は
常に1つ でなければならない

〈完全なプログラミング〉を目指す会 2020

あるクラスを変更したいと思う人
ひとり だけ

〈完全なプログラミング〉を目指す会 2020
  • 見た目を変えたい
  • ビジネスルールを変えたい
  • ミドルウェアを変えたい
  • 計算量を減らしたい
  • 通信量を減らしたい
  • etc.
〈完全なプログラミング〉を目指す会 2020

オープンクローズドの原則
Open/closed principle

〈完全なプログラミング〉を目指す会 2020

モジュールは 拡張に対して開いて いなければならず
修正に対して閉じて いなければならない

〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

拡張に対して開いている

他の関連機能を実装するベースとして
使えなければならない

〈完全なプログラミング〉を目指す会 2020

修正に対して閉じている

既存の振る舞いを変更することは出来ない、する必要がない

〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

何か新しいことをやりたいときに、
既存のクラスを 修正せずに そのまま、
簡単に利用出来る つくりになっていると素敵だね

〈完全なプログラミング〉を目指す会 2020

リスコフの置換原則
Liskov substitution principle

〈完全なプログラミング〉を目指す会 2020

サブクラスをその基底クラスと
置き換えることが出来なければならない

クラス S がクラス T のサブクラスである場合に、
プログラム中で使われる T のインスタンスは、
何ら修正を加えることなく S のインスタンスに置き換えることが出来なければならない

〈完全なプログラミング〉を目指す会 2020

長方形と正方形

〈完全なプログラミング〉を目指す会 2020
public class Rectangle {
    private int length;
    private int breadth;

    public int getLength() {
        return length;
    }
    public void setLength(int length) {
        this.length = length;
    }
    public int getBreadth() {
        return breadth;
    }
    public void setBreadth(int breadth) {
        this.breadth = breadth;
    }
    public int getArea() {
        return this.length * this.breadth;
    }
}
〈完全なプログラミング〉を目指す会 2020
public class Square extends Rectangle {
    @Override
    public void setBreadth(int breadth) {
        super.setBreadth(breadth);
        super.setLength(breadth);
    }
    @Override
    public void setLength(int length) {
        super.setLength(length);
        super.setBreadth(length);
    }
}
〈完全なプログラミング〉を目指す会 2020

サブクラスであることが制限にしか感じなくなったら怪しい

結局クラスごとの場合分け書いてたら怪しい

それは本当に is-a 関係なのか考えよう

〈完全なプログラミング〉を目指す会 2020

インターフェース分離の原則
Interface segregation principle

〈完全なプログラミング〉を目指す会 2020

クライアントが使用しないインターフェイスに
クライアントを強制的に依存させてはならない

〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

java.util.ArrayList

〈完全なプログラミング〉を目指す会 2020

kotlin.collections.ArrayList

〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

依存関係逆転の原則
Dependency inversion principle

〈完全なプログラミング〉を目指す会 2020

上位モジュールを下位モジュールに依存させるのではなく
両方のモジュールを 抽象化に依存させる べきである

〈完全なプログラミング〉を目指す会 2020

実装ではなく 抽象に依存せよ

〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

制御の流れ

〈完全なプログラミング〉を目指す会 2020

依存関係を逆転

〈完全なプログラミング〉を目指す会 2020

ポリモーフィズム

  • 同一のインタフェースを 複数の型に対して 定義出来ること
  • 異なるクラスが 同一のメッセージに対して 応答出来ること
〈完全なプログラミング〉を目指す会 2020

実行時まで処理するドアの種類を知らないOpen()やClose()のような操作をサポートする言語の機能を「ポリモーフィズム」という。
— CODE COMPLETE 5.3.4 設計が単純になる場合の継承

〈完全なプログラミング〉を目指す会 2020
  • 日本語では 多態性 とか 多相 とも
  • いくつか種類がある
    • サブタイピング(単にポリモーフィズムという場合だいたいこれ)
    • パラメータ(総称型とか)
    • アドホック(オーバーロードとか)
〈完全なプログラミング〉を目指す会 2020

■広範な型チェックよりもポリモーフィズムを選ぶ

case文の数が増えてきたら、継承を使って設計した方がよいという兆候かもしれない。
— CODE COMPLETE 6.3.2 継承(「is a」の関係)

〈完全なプログラミング〉を目指す会 2020

継承による具体例

from Replace Conditional with Polymorphism

〈完全なプログラミング〉を目指す会 2020
class Bird {
  // ...
  double getSpeed()
    switch (mType) {
      case EUROPEAN:
        return getBaseSpeed();
      case AFRICAN:
        return getBaseSpeed() - getLoadFactor() * mNumberOfCoconuts;
      case NORWEGIAN_BLUE:
        return (mIsNailed) ? 0 : getBaseSpeed(mVoltage);
    }
    throw new RuntimeException ("Should be unreachable");
}
〈完全なプログラミング〉を目指す会 2020

〈完全なプログラミング〉を目指す会 2020

何が嬉しいのか

〈完全なプログラミング〉を目指す会 2020

マジックナンバー
7 ± 2
(4 ± 1, 5 ± 3 という説も)

〈完全なプログラミング〉を目指す会 2020
  • 人間が一度に考えられるモノゴトの限界
  • ある特定のケースにだけ関心があるときに、ずらずらと列挙された case 文を読むのは苦行
    • 読み違いも起こりやすい
〈完全なプログラミング〉を目指す会 2020

求めるな、命じよ
Tell-Don't-Ask

  • どう振る舞うべきかはその オブジェクト自身 が知っている
  • データと振る舞いを密接に関連付ける OOP の原則
  • これが ポリモーフィズムを使って実現出来る 場合もある
〈完全なプログラミング〉を目指す会 2020

ただし

〈完全なプログラミング〉を目指す会 2020

継承よりも合成を
Composition over inheritance

  • 継承は強力だが使いこなすのは難しい
    • そもそも真に継承関係にあるような事象は稀
  • リスコフの置換原則が守られないなら使ってはいけない
  • 合成ですんなり表現出来るならそれでいい
〈完全なプログラミング〉を目指す会 2020

そして DIP 再び

  • 依存関係を逆転出来るのは ポリモーフィズムのおかげ
  • 依存関係を逆転出来るということは 関心事のスコープを限定出来る ということ
  • ここに オブジェクト指向の真価がある と言っても過言ではない
〈完全なプログラミング〉を目指す会 2020

まとめ

〈完全なプログラミング〉を目指す会 2020

オブジェクト指向でなぜつくるのか

〈完全なプログラミング〉を目指す会 2020

複雑な問題
人間が解決できるようにする ため

〈完全なプログラミング〉を目指す会 2020

よいクラスとは

〈完全なプログラミング〉を目指す会 2020

SOLID

〈完全なプログラミング〉を目指す会 2020

ポリモーフィズムのうまみ

〈完全なプログラミング〉を目指す会 2020
  • 継承は問題を適切に表現出来る場合に 威力を発揮する
    • 濫用すると 人間の能力を越えた複雑度になりかねない
  • インターフェースを介した 依存関係逆転こそがオブジェクト指向の真価
  • 問題の大きさを 人間が扱えるサイズに収める ために使う
〈完全なプログラミング〉を目指す会 2020

参考書籍

〈完全なプログラミング〉を目指す会 2020

Credits

〈完全なプログラミング〉を目指す会 2020