読書記録 C#実践開発手法 第1部-第3章

第3章ではインタフェースにまつわる設計や依存関係について解説されています。

インタフェースと依存関係

オブジェクト指向な設計を行う際には、具体ではなく抽象に依存させるという指標があります。
これは、多態性や再利用性を高めるためであり、個人的にはオブジェクト指向で最も大切な指標だと思います。

まずはインタフェースを使用しない例を示します。

public class Motor
{
    private int speed;

    public void Start(){ ...}
    public void Stop(){...}
    public void ChangeStart(int speed){...}
}

public class Car
{

    public Car()
    {
        Motor motor = new Motor();
        motor.Start();
    }
}

一部省略していますが、CarクラスからMotorクラスを生成及び使用している箇所が大切です。
この例では、CarクラスはMotorクラスにStartメソッドがあることを知っている必要があり、呼び出す際に引数を指定してはいけないことを知っている必要があります。
つまりCarクラスはMotorクラスに依存していることになり、Motorクラスの変更によりCarクラスの変更が余儀なくされている状態です。

一方で、インタフェースを使用している例を示します。

public interface IMotor
{
    void Start();
    void Stop();
    void ChangeSpeed(int speed);
}

public class Motor : IMotor
{
    ....
}

public class Car
{

    public Car()
    {
        IMotor motor = new Motor();
        motor.Start();
    }
}

こうなるとCarクラスが依存しているのはMotorクラスという実態ではなく、IMotorインタフェースという抽象になります。
言い換えると、Motorクラスを「モータ」たらしめているのはIMotorインタフェースであり、CarクラスはIMotorインタフェースを持つものしか「モータ」として認めない、ということです。
こうなるとMotorクラスの実態はCarクラスに依存します。
ほかにも「モータ」として動作するクラスを作成するときにも、IMotorインタフェースを追加しないとCarクラスで使えなくなるからです。
これがオブジェクト指向で耳にする依存性の逆転になります。
今回の例ではインスタンスを直接生成していますが、本来はFactoryパターンを活用するべきです。

デザインパターン

紹介されているデザインパターンは大まかにNull Object、Adapter、Strategyの3種類でした。
コードでの説明は省略しますが、ざっくり説明すると

Null Object
結果としてnullを返すのではなく、IObjectインタフェースをもつNullObjectクラスを定義することで、null参照例外の発生を防ぎます。
これによって各所に散らばるnull参照チェックが不要になり保守性の向上につながります。

Adapter
使いたいクラスに実装されていないインタフェースをクライアントが使うとき、使いたいメソッドへ委譲するようなインタフェースを持つクラスを参照することで再利用性を高めます。

Strategy
共通のインタフェースを持つクラスに動的にアクセスして、クラスのふるまいを実行中に変更できるようにします。
条件分などでコンストラクタ部分を制御することで、同じ名前のメソッドでも異なる処理に分岐できます。

まとめ

3.3.1以降の内容は今回は省きます。ここまで書いて疲れました。
ダックタイピングはクラスの汎用性を向上させる非常に有効な手段なので、いずれちゃんと調べます。

出典元は忘れてしまいましたが、「オブジェクト指向におけるクラスの継承は、手続き型言語におけるgotoと同じ」ということを聞いたことがあります。
これは適切な利用により有効性を保てるが、過度な活用は保守性を下げるということも含めて言い得て妙だと思います。
インタフェースも過度に使ってしまうと、クラスの実態を追うのが難しくなったり、新しいクラスの作成時の制限が大きくなってしまいます。

次回の更新は2/17を予定しています。