読者です 読者をやめる 読者になる 読者になる

Today Fortkle Learned.

知らないことの方が多いので今更調べています。さらに一歩先に行けたら嬉しいです。

schooの『オブジェクト指向入門』を見ました

アジェンダ

第1回. オブジェクト指向のキホン
第2回. ポリモーフィズムとカプセル化
第3回. 継承と委譲
第4回. オブジェクト指向実践
第5回. オブジェクト指向設計

オブジェクト指向を学ぶ理由

  • オブジェクト指向は言語にとらわれない。応用力が身につく。
  • 拡張性、保守性の高いコードを書けるようになる
  • OSSに貢献しやすくなる

第1回. オブジェクト指向のキホン

講義のゴール

オブジェクト指向とは何か

  • プログラミングに オブジェクト という概念を持ち込んだ考え方
  • オブジェクトの相互作用 としてシステムを作っていく
  • オブジェクト指向で行うプログラミングの事を、オブジェクト指向プログラミング(OOP)という

オブジェクト指向の必要性・利点

  • 人間の思考の構造(メンタルモデル)に近いコードが書けるようになる=可読性が高くなる
  • 呼び出し方法を変えずに処理を拡張する仕組みがあり、拡張性が高まる
  • オブジェクトに関連するデータや振る舞いを1つのクラスとしてまとめることができるため、プログラムの構造が整理しやすい

クラス、プロパティ、メソッド

  • オブジェクト指向の世界ではまず最初にオブジェクトを定義する。その際に使うのが「クラス」「プロパティ」「メソッド」。

クラス

  • クラスとは、オブジェクトの型であり定義。

プロパティ

  • クラスに定義された変数
  • オブジェクトの持つデータ

メソッド

  • クラスに定義された関数
  • オブジェクトの振る舞い

その他のキーワード

インスタンス

  • クラスを元に実態化されたオブジェクトのこと(オブジェクトと同義)

コンストラクタ

  • オブジェクトの初期化を行うメソッド

アクセス修飾子

  • プロパティ/メソッドが「オブジェクトの利用者から自由に呼び出せるか」「自分自身からしか呼び出せないか」を制御するもの
  • public/protected/private
    • PHPではあまりprivateは使わない。拡張性を重視してこの講義ではprotectedを使う

静的プロパティ/メソッド

  • 通常のプロパティやメソッドはインスタンスを生成する必要がある
  • 一方、staticキーワードをつけて定義したものはインスタンスがなくても使える
  • 注意点
    • 通常のプロパティはオブジェクト毎に異なる値を保持できるが、staticなプロパティはグローバル変数のような扱いとなるため注意が必要
    • 使いすぎると拡張性を低下させる

クラスの継承

  • 継承によってクラスの定義を拡張することができる
    • 拡張の元となるクラス = 基底クラス
    • 拡張したクラス = 派生クラス
  • 可能な拡張
    • プロパティやメソッドの追加
    • メソッドの上書き
  • メリット
    • 定義を必要なだけ拡張することができる
    • 拡張したコードを呼び出し側を変えずに利用できる

第1回.オブジェクト指向のキホン のまとめ

  • オブジェクト指向を導入すると理解しやすく拡張性の高いコードを書くことができる
  • クラスにデータであるプロパティ、振る舞いであるメソッドを定義する
  • クラスを元にオブジェクトを生成する
  • クラスは継承によって拡張できる

第2回. ポリモーフィズムとカプセル化

講義のゴール

今回の講義のアジェンダ

1.オブジェクト指向の三大要素 2.抽象と実装 3.カプセル化ポリモーフィズム

1.オブジェクト指向の三大要素

三大要素

三大要素をつきつめていくと

  • 抽象という考え方が大切になってくる
    • 拡張性の向上、依存性を少なく出来る

2.抽象と実装

コードから読み解く

class OrderAcceptor
{
    protected $logger

    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
    }

    public function acceptOrder(Cart $cart)
    {
        $order = $this->createOrderFromCart($cart);
        $this->saveOrder($order);
        $this->logger->log('注文を受け付けました');
    }
}

コードの内容

  • カート情報をもとに注文を受け付ける処理
      1. CartからOrderを作成
      1. Orderを保存
      1. ログを出力

ログの出力部分に注文

  • コンストラクタでLoggerオブジェクトを受け取っている
  • ログがどうやって出力されるかはLoggerの実装次第

Loggerクラス

class Logger
{
    protected $file;

    public function __construct($filepath)
    {
        $this->file = new SplFileObject($filepath, 'a');
    }

    public function log($message)
    {
        $this->file->fwrite($message."¥n");
    }
}

もしシステムの要件が変わって

  • ログをファイルに出力するのではなくDBに出力したくなった場合
  • 従来であれば既存のコードを書き換えて対応していた
    • しかしその場合、既存のコードを破棄することになる(再利用できない)
    • また、不要なバグを生み出すリスクもある

抽象という考え

  • 「ログを出力する」という抽象的な手続きをオブジェクト指向では表現することができる
    • 書きだすのがファイルでもデータベースでも関係なし。

抽象を定義するインターフェイス

  • 通常メソッドには具体的な処理の内容を記述するが、インターフェイスを使うことでメソッドの呼び出し方のみを定義できる
interface LoggerInterface
{
    public function log($message);
}

抽象と実装

  • 具体的な処理内容を伴わない、呼び出しに必要な情報部分を 抽象 と呼ぶ
  • 「ファイルに出力する」のような具体的なコードの内容を 実装 と呼ぶ
  • OOPではインターフェイスが抽象を、クラスが実装を担う

抽象と実装を意識してLoggerを改良してみる

// LoggerクラスにLoggerInterfaceを実装する
// また、拡張を考慮して名前をFileLoggerにする
class FileLogger implements LoggerInterface
{
    protected $file;

    // インターフェイスの定義と一致させる
    public function log($message) ...
}

呼び出し元をLoggerInterfaceに依存するように修正。

class OrderAcceptor
{
    public function __construct(
        LoggerInterface $logger
    ) {
        $this->logger = $logger;
    }
}

抽象に依存する

  • 特定のクラス(実装)のみを扱うコードになっていると、変更が発生した場合に既存のコードを直接修正しなくてはならない
  • インターフェイス(抽象)を指定しておくことで変更に強い拡張性の高いコードを書くことが出来る

試しにLoggerの実装変える

class DatabaseLogger implements LoggerInterface
{
    protected $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function log($message)
    {
        $stmt = $this->pdo->prepare(
            "INSERT INTO log (message) VALUES (:message)");
        $stmt->bindValue(':message', $message);
        $stmt->execute();
    }
}

このDatabaseLoggerを使うようにするのも簡単。

// 渡すオブジェクトを差し替えるだけで呼び出し側の実装を
// 変えずに処理を切り替えられる

// $logger = new FileLogger(...);
$logger = new DatabaseLogger(...);
new OrderAcceptor($logger);

オブジェクト指向の真髄

  • システムを設計する際、「カートに商品を追加する」「カートから注文に進む」のようにユースケースから考えていくが、オブジェクト指向ではこの考えそのものを表現できる
  • オブジェクトの相互作用 に繋がっている

3.カプセル化ポリモーフィズム

カプセル化

  • オブジェクトの具体的なデータや構造など、つまり 実装 を隠蔽すること
  • オブジェクト指向において大切なことは抽象であり、具体的な実装内容に依存しないための考え方
  • そのため、privateやprotectedアクセス修飾子を用いて不要な情報にアクセスできなくする
    • その代わりにアクセサメソッドを使う

なぜアクセサメソッドを用いるか

  • プロパティを直接操作することは実装に依存している状態
  • 抽象を取り入れるためメソッドを経由してアクセスする
    • 単にプロパティへのアクセスだけでなく、データの整合性を保つために処理を追加するなども行える

ポリモーフィズム(多様性)

  • 今回であればFileLoggerとDatabaseLoggerのように実際には異なる実装であっても同じような振る舞いを持つオブジェクトであれば許容する性質のこと

カプセル化ポリモーフィズム

  • カプセル化によって実装の詳細は隠蔽される、抽象に着目して様々な実装を受け入れる
  • オブジェクトの相互作用が抽象だけでも成り立つようになる

第2回.ポリモーフィズムカプセル化 のまとめ

  • 抽象とは、実装コードを伴わない「カートに商品を追加する」のような手続きの情報を指す
  • 抽象を意識することで可読性・拡張性の高いコードが書けるようになる

第3回. 継承と委譲

講義のゴール

  • 継承が分かる
  • 委譲が分かる

今回の講義のアジェンダ

  • 1.継承
  • 2.委譲
  • 3.継承と委譲

1.継承

継承とは(第1回目より)

  • クラスを拡張する仕組み。

拡張していくクラス

// 基底クラス
class Logger
{
    public function log($message)
    {
        $message = $this->formatMessage($message);
        $this->saveLog($message);
    }

    protected function saveLog($message)
    {
        // ログを保存する処理
    }

    protected function formatMessage($message)
    {
        return $message;
    }
}

TimestampLogger

class TimestampLogger extends Logger
{
    protected function formatMessage($message)
    {
        $time = new DateTime('now');

        return sprintf('[%s] %s',
            $time->format('Y-m-d H:i:s'), $message);
    }
}

抽象クラス(Abstract Class)

  • 継承されることを前提とし、部分的に処理のみを実装したクラス
    • abstract キーワードを用いてクラスを定義
  • 抽象メソッド という 実装を持たないメソッド を定義できる
  • 通常のクラスは 具象クラス という

さっきのLoggerクラスを抽象クラスにする

// 慣例的に Abstract プレフィックスをつけることが多い
abstract class AbstractLogger
{
    public function log($message)
    {
        $message = $this->formatMessage($message);
        $this->saveLog($message);
    }

    // ログの保存方法を継承によって拡張可能としている
    abstract protected function saveLog($message)
    {
        // ログを保存する処理
    }

    protected function formatMessage($message)
    {
        return $message;
    }
}

FileLogger

class FileLogger extends AbstractLogger
{
    public function __construct($filepath)
    {
        $this->file
             = new SplFileObject($filepath, 'a');
    }

    protected function saveLog($message)
    {
        $this->file->fwrite($message."¥n");
    }
}

DatabaseLogger

class DatabaseLogger extends AbstractLogger
{
    protected $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    protected function saveLog($message)
    {
        $stmt = $this->pdo->prepare(
            "INSERT INTO log (message) VALUES (:message)");
        $stmt->bindValue(':message', $message);
        $stmt->execute();
    }
}

抽象クラスとインターフェイス

  • 抽象クラスは、実装の共通化など、実装の観点から用いる
  • インターフェイスは抽象(振る舞い方)を定義するために用いる
  • 目的が異なるため、組み合わせて使うことが多い
interface LoggerInterface
{
    public function log($message);
}

abstract class AbstractLogger implements LoggerInterface
{
    public function log($message)
    {
        $message = $this->formatMessage($message);
        $this->saveLog($message);
    }

    abstract protected function saveLog($message);

    protected function formatMessage($message) ...
}

サンプルコードを振り返る

  • 現状2種類の拡張方法が出てきている
    • ログの保存方法を拡張する
    • ログのフォーマットを拡張する
  • ファイルへ保存しつつ、時間を付与したログを出力したい場合がでてきたら
    • FileLogger と TimestampLoggerを組み合わせられるのか
    • できない。多重継承はできない。

複数の異なる拡張をしたい場合

  • 拡張したい処理部分を別のクラスに分け、インスタンス同士を組み合わせることで対処する
    • 委譲と呼ぶ
  • 他にも Mix-in と呼ばれる手法も存在する
    • PHPでは トレイト という機能で実現

2.委譲

委譲とは

  • あるオブジェクトの一部の振る舞いを、別のオブジェクトに代替させることを 委譲 と呼ぶ。
  • 実現するためにはクラスを分割し、それぞれのインスタンス(オブジェクト)を組み合わせる

役割を整理する

  • 拡張の種類、つまり 処理の役割 を整理する
    • ログの保存方法を拡張する
    • ログのフォーマットを拡張する
  • 今回はフォーマットの拡張を別のクラスに委譲する
    • LogFormatter とする

まずインターフェイスの定義

// オブジェクトを組み合わせて実現するため
// インターフェイスを定義することが望ましい
interface LogFormatterInterface
{
    public function formatMessage($message);
}

LogFormatterの作成

class LogFormatter implements LogFormatterInterface
{
    public function formatMessage($message)
    {
        return $message;
    }
}

// LogFormatter を継承しても問題ないが、インターフェイスさえ実装されていれば
// どのような実装でも結果的には変わらない(カプセル化の考え方)
class TimestampFormatter implements LogFormatterInterface
{
    public function formatMessage($message)
    {
        $time = new DateTime('now');

        return sprintf('[%s] %s',
            $time->format('Y-m-d H:i:s'), $message);
    }
}

分割したLogFormatterをLoggerクラスで使う

abstract class AbstractLogger
{
    protected $formatter;

    // LogFormatterInterface オブジェクトをプロパティにセットする
    public function setFormatter(LogFormatterInterface $formatter)
    {
        $this->formatter = $formatter;
    }

    public function log($message)
    {
        // LogFormatterInterface オブジェクトに委譲する
        $message = $this->formatter->formatMessage($message);
        $this->saveLog($message);
    }

    abstract protected function saveLog($message);
}

FileLogger と TimestampLoggerを組み合わせる

class FileLogger extends AbstractLogger
{
    ...
}

$logger = new FileLogger('/path/to/file');

// 委譲したいオブジェクトを設定する
$logger->setFormatter(
    new TimestampLogFormatter()
);

$logger->log('メッセージ');

3.継承と委譲

継承と委譲の使い分け

  • どちらもオブジェクトの拡張を行う方法
  • 委譲を活用すると複雑な処理に対しても柔軟に拡張することができるようになる
  • 一般的には多重継承はできないため継承のみで拡張しようとすると却って複雑化の元となってしまう
  • 継承より委譲 と言われることが多い

委譲を使うデメリット

  • 委譲を用いた場合は複数のオブジェクトを組み合わせる必要が出てくるため、オブジェクトの構造が複雑化してしまう

どちらを使うべきか

  • オブジェクトの持つ処理の役割に着目し、複数の役割を持っている場合は役割毎に別オブジェクトに委譲することを検討する
  • オブジェクトの役割が1つならシンプルに継承で良い

役割に着目する

  • 保存方法の拡張、フォーマットの拡張のように、どのような種類の拡張パターン(役割)があるかを考える
  • どのような役割を持つオブジェクトを、どのように組み合わせるかを考える

オブジェクト指向設計

  • スムーズにできるようになると拡張性の高いシステムが作れる
    • まずは継承か委譲か、といったように手段を知ることが大切
    • その上で判断力は経験を積んで身につける
  • デザインパターンなども勉強する

やりすぎは禁物

  • 拡張を想定してクラスの分割を行っても結果的に使われない場合も多々ある
  • 最初はシンプルに作っておき必要に合わせてクラスを分割するやり方の方が現実的
  • KISSの原則 や YAGNI と呼ばれる考え方

第3回.継承と委譲 のまとめ

  • 継承によってクラスの拡張が可能
  • 処理の一部を別のオブジェクトに代替させることを委譲と呼び、継承以外の拡張手段として用いられる
  • 役割に応じたオブジェクトの組み合わせを考えることをオブジェクト指向設計と呼ぶ

第4回. オブジェクト指向実践

講義のゴール

  • 実際のアプリケーションでオブジェクト指向がどのように取り入れられているかを知る
  • MVCについて知る

今回の講義のアジェンダ

  1. プログラムの種類とオブジェクト指向
  2. Webアプリケーションとオブジェクト指向
  3. MVC
  4. 今後の学び方

1. プログラムの種類とオブジェクト指向

プログラムの種類と特徴

アプリケーション

  • 画面やビジネスロジックを処理する
  • 基本的に再利用は考慮しない
    • 再利用したいコードはライブラリ化する
  • 大体のコードは自分で修正が可能なのであまり高い拡張性は求められない

アプリケーションとオブジェクト指向

ライブラリ

  • 再利用可能なコードをまとめたもの
  • 様々な使われ方を考慮する必要がある
    • 拡張性が大切になってくる

フレームワーク

  • ライブラリの中でもある一連の仕組みの土台となるようなプログラム

ライブラリとオブジェクト指向

2. Webアプリケーションとオブジェクト指向

Webアプリケーションの場合

  • Webアプリケーションフレームワークを用いて構築していくのが近年では一般的

Webアプリケーションの処理の流れ

  1. リクエストされた画面を読み込む
  2. 必要な情報をデータベースから取得する
  3. 入力フォームからデータが送信された場合、内容に基づいてデータベースの更新処理などビジネスロジックを実行する
  4. HTMLを組み立てる
  5. レスポンスを返す

Webアプリケーションフレームワークの機能

  • リクエスト/レスポンスの処理
  • データベースアクセス
  • 画面制御
  • 入力フォーム制御
  • etc.

Webアプリケーションをオブジェクトで表現する

  • リクエスト
  • レスポンス
  • 画面
  • データベースコネクション
  • 入力フォーム

3. MVC

MVCアーキテクチャ

  • Model / View / Controller の頭文字
  • Model
  • View
    • モデルを視覚化した情報
  • Controller
    • ユーザとのやりとりを制御するもの

MVC

  • MVCという役割にわけて考えると整理しやすい
  • あくまでMVC自体は抽象的な考えで、実装の方法を指すものではない

MVCの考えをベースとした実装

  • Model
    • データやロジックなどを要件に応じてオブジェクトにする
  • View
    • Webの場合はHTMLを構築する
  • Controller
    • 画面ごとにコントローラクラスを用意する

注文情報を制御するコントローラ

class OrderController
{
    // ユーザが注文確定リクエストを送るとこのメソッドが実行される
    public function acceptAction(Request $request)
    {
        // 適切なモデルを操作し、ビジネスロジックを実行する
        $cart = $this->getCart();

        $order = $this->orderAcceptor->acceptOrder($cart);

        // 適切なビューを返す
        return $this->render('order/accept.html', [
            'order' => $order
        ]);
    }
}

注文を受け付けるモデル

class OrderAcceptor
{
    public function acceptOrder(Cart $cart)
    {
        $order = $this->createOrderFromCart($cart);
        $this->saveOrder($order);
    }    

    protected function createOrderFromCart(Cart $cart)...
    protected function saveOrder(Order $order) ...
}

注文完了画面を表示するビュー

<!-- order/accept.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>注文完了</title>
</head>
<body>
  <h1>スクーショッピング</h1>
  <h2>注文ありがとうございました</h2>
  <p>注文ID: <?= htmlspecialchars($order->getId()) ?></p>
</body>
</html>

MVCフレームワーク

  • MVCという考えをベースに、その構造を極力素直にコードに表現しようとするフレームワークMVCフレームワーク と一般的に呼ぶ
  • コントローラに必要な機能やビューを組み立てる機能など、土台となる仕組みを提供

ビジネスロジック部分

4. 今後の学び方

フレームワークを使って開発する

ソースコードを読む

ビジネスロジックをどう扱うか

  • 設計力が求められる
  • ドメイン駆動設計などを学ぶ
    • ビジネスの課題をどう扱うかをパターン化したもの

第4回.オブジェクト指向実践 のまとめ

  • アプリケーションを構成する様々な要素がオブジェクトとして扱われている
  • MVCという役割に基づくと整理しやすい
  • ステップアップのためにはフレームワークを使ったりソースコードを読んでみるのがよい

第5回. オブジェクト指向設計

講義のゴール

今回の講義のアジェンダ

  1. クラスの役割
  2. クラスの依存性
  3. オブジェクト指向設計原則
  4. デザインパターン

1. クラスの役割

拡張性と委譲

  • 拡張性を高めるためには委譲のようなテクニックが効果的
    • 処理を別のオブジェクトに委ねる

オブジェクト指向設計の基本

  • 役割に応じてクラスを分割し、1つ1つのクラスの持つ役割・機能を小さくまとめる
  • インターフェイスを用いてオブジェクト間でどのようなやりとりが行われるかを定義する
  • 拡張性だけでなく、処理の見通しも良くなる
  • テストもしやすくなる

役割をどのように分ければよいか

  • 結果的に拡張性を高めることが大切
  • 拡張理由 から役割を推測する
    • ログの保存方法を変えたい
    • フォーマットを変えたい

どのような拡張が考えられるか

  • データベースや特定ライブラリ、WebAPIなどに依存する処理は外的な要因によって変更を求められることがある
    • ライブラリがメンテされなくなった
    • ファイルで管理していたがスケールを考え、データベースに保存したい

経験も大切

  • システムの仕様変更の場合は企業によって様々な要件が考えられる
    • 固定のロジックでよかった部分が複数のロジックを切り替えて使う必要がでてきた
  • 拡張の必要性がでてきてから対応すれば良い

役割と抽象

  • 拡張理由は 抽象的 に考える
  • 例えば「データベースに保存したい」「ファイルに保存したい」は「保存方法を変更したい」という1つの抽象的な拡張として捉えることができる

2. クラスの依存性

依存性

  • あるクラスが別のクラスの機能を必要とすることを クラスの依存性 と呼ぶ
  • 依存するクラスが増えると複雑性が増す
  • 役割毎にクラスを分けることは依存性を少なくすることにも繋がる

抽象に依存する例

// Logger は LogFormatterInterfaceに依存している
class Logger implements LoggerInterface
{
    protected $formatter;

    public function setFormatter(
        LogFormatterInterface $formatter)
    {
        $this->formatter = $formatter;
    }

    public function log($message) ...
}

実装に依存する例

// Logger は LogFormatterに依存している
class Logger implements LoggerInterface
{
    protected $formatter;

    public function setFormatter(
        LogFormatter $formatter)
    {
        $this->formatter = $formatter;
    }

    public function log($message) ...
}

抽象に依存することが基本

  • クラス(実装)を指定するのではなくインターフェイス(抽象)を指定する
  • 拡張したい内容が既存の実装内容とまったく異なるような場合もあるため

依存するオブジェクトの組み立て

  • クラスを分割していても、使い方次第では拡張性につながらない場合もある

直接インスタンス化するのも実装に依存している

// Logger は LogFormatterに依存している
class Logger implements LoggerInterface
{
    protected $formatter;

    public function log($message)
    {
        // LogFormatter を直接インスタンス化している
        // これも「実装に依存」している状態
        $formatter = new LogFormatter();

        $message = $formatter
            ->formatMessage($message);
        $this->saveLog($message);

    }

    public function saveLog($message) ...
}

依存するオブジェクトを直接インスタンス化すること

  • 拡張時に既存のコードを修正しなければならずバグを生み出すリスクを高める
  • ライブラリの場合はコードを変更できない可能性もある
  • 必然的に実装に依存してしまう

依存性を注入する

  • 依存するオブジェクトを自身でインスタンス化せずメソッドを通じて受け渡すことを、通称 **依存性の注入(Dependency Injection)と呼ぶ
  • 一般的には DI と呼ばれるパターン
  • 特定の実装に依存しないようにするためには必然的に用いられるテクニック
    • コンストラクタインジェクション、セッターインジェクション

基本的にはコンストラクタで注入する

  • コンストラクタを用いることで、依存性が必須のものであることが明確になる
  • セッターの場合、オブジェクトが中途半端な状態になる可能性があり、考慮しなければならない点が増える

DIを活用するために

  • 依存性が増えてくるとオブジェクトの組み立てが複雑になる
  • 実際の現場ではオブジェクトの依存性を管理する仕組みやツールを用いることが多い
    • ファクトリー
    • DIコンテナ

3. オブジェクト指向設計原則

オブジェクト指向設計における原則

  • 「役割毎にクラスを分割する」「抽象に依存する」といった考え方はオブジェクト指向の世界における設計原則として普及されている

SOLID原則

  • 単一責任の原則(SRP)
  • オープン・クローズドの原則(OCP)
  • リスコフの置換原則(LSP)
  • インタフェース分離の原則(ISP
  • 依存性逆転の原則(DIP

SOLID原則の一部内容

  • 「役割毎にクラスを分割する」
    • 単一責任お原則(SRP)
  • 「抽象に依存する」
    • 依存性逆転の原則(DIP

原則を学ぶ

  • 拡張性を高める、バグを作りにくくする、といった多くの現場で求められる基本的な課題に対する解決策として原則を知っておく
  • あくまで原則なので、必要な範囲で使用する事を心がけると良い

4. デザインパターン

デザインパターンとは

  • 様々な設計上のノウハウ・テクニックを集めたもの
  • GoFデザインパターンが有名
  • 継承や委譲といった基本的な仕組みやテクニックを組み合わせていく

Strategyパターン

  • ロジックの切り替えを委譲を用いて実現するパターン
  • LogFormatterもStrategyパターンに該当

目的と手法をセットで理解する

  • どのような課題を解決するためにはどのような設計にするのがよいか、を一緒に理解することが大切
  • 拡張の必要がないのに拡張性を高めるパターンを適用しても、ただ単に複雑にしているだけになってしまう

鵜呑みにしない

  • 言語自体の発達により、言語自体に同等の機能があったり、より簡潔に実現できる場合もある
  • Singletonパターンには弊害もあり、使わずに済むならその方がよい場合が多い

パターンは進化していく

  • パターンは知識の積み重ねで設計の歴史でもある
  • 小さな設計部分だけでなく、アーキテクチャ全体やプロジェクトの進め方など様々なものがパターン、フレームワーク化され、より本質的な問題に注力できるようになってきている

パターンを作る

  • 既存のパターンを学ぶだけでなく、自分の周りのこともパターン化していくことでより効率的に物事が進められる環境になる

第5回.オブジェクト指向設計 のまとめ

  • 役割毎にクラスを分割する
  • 実装ではなく抽象に依存する
  • 設計原則やパターンを学んで基本的な考えや問題の対処法を身につける
  • 設計には経験も求められるので日々よりよい設計を追求することが大切