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

今回から第2部に入ってSOLID原則の説明に入ります。
第5章はSの単一責務の原則の解説になります。
一日遅れましたが誤差の範囲内です。

リファクタリングの方針としては、インタフェースを通じた抽象化とStairwayパターンによる分割で積極的にクラスを分けていくことになります。
この作業を怠って複数の処理を含んだクラスを使うと、クラスの仕様を変更するたびに、そのクラスとクラスを使うクライアントの変更が必要になり、仕様変更が難しくなります。
基本的に継承ではなく委譲を利用してクラスを分割する方針なので、使う手立てとしては

  • Decoratorパターンによる機能追加
  • Compositeパターンによる連結
  • Strategyパターンによる分岐処理

になります。
本書の例ではありませんでしたが、これらをそれぞれちゃんと使うときには、Factoryクラスなどを使うことでインスタンスの生成から変更可能にしておかないといけないと思います。

Decoratorパターン

あるクラスで責務の単一化をしようにも、機能分割が難しい場合があります。
例)

  • ロギング機能
  • グラフ描画機能
  • ファイルパース機能 etc

このあたりの機能はある処理とかなり密に依存する場合があり、単一化が難しくなりがちですが、それをDecoratorで分割します。

public class Decorator : IComponent
{
    public Decorator(IComponent component)
    {
        this.Decorated = component;
    }
    
    public void Do()
    {
        //Decoratorでやりたいこと
        DoSomething();
        //被装飾インスタンスで実行する本来の処理
        Decorated.Do();
    }


    private void DoSomething()
    {
        //ロギングや可視化などの処理
    }
}
<||
このようにインタフェースで追加処理を委譲させることで変更に強くなります。

このDecoratorの応用として、述語Decorator、分岐Decoratorなどがあります。
これらの応用で条件分岐や、非同期処理を別クラスに分離することができます。

** Compositeパターン
このデザインパターンは、共通のインタフェースを持つクラスをリストなどのイテレータにまとめます。
こうすることで、複数のインスタンスを1つのインスタンスのように扱うことができます。
これについてはコード例は省略します。

** Strategyパターン
Switch文で複数の条件分岐を記述すると、新しく分岐を追加するときや、条件式の変更への対応が難しくなります。

>|cs|
public class Strategy
{
    private IDictionary<Case, IComponent> Strategies;
    public Strategy()
    {
        Strategies = new Dictionary<Case, IComponent>();
        Strategies.Add(Case.Case1, new Case1Component());
        Strategies.Add(Case.Case2, new Case2Component());
        Strategies.Add(Case.Case3, new Case3Component());
        Strategies.Add(Case.Case4, new Case4Component());
    }

    public void Do(Case caseIndex)
    {
        Strategies[caseIndex].Do();
    }
}
<||

分岐用の値を外部引数として受け取ることで、分岐の追加や条件式の変更が容易になります。
しかし、新しいクラスを追加する際にはIComponentインタフェースを持つことが必要になり、この例だとクライアント側で各条件と対応する値を知っておく必要はあります。

** まとめ
単一責務の原則では、クラスから処理をインタフェースとして抽出して、複数クラスに分散させることになります。