Strategyパターン

たまの休日に一部の復習も兼ねて、”Java言語で学ぶデザインパターン入門”を亀のようなペースで読み進めています。
個人的に一番好きなパターンはStrategyパターンです。
このパターンは、インタフェースを定義してアルゴリズムを交換可能にするもので、
典型的な使いどころには、ゲームにおけるプレイヤーAI(CPUなどとも呼ばれますね)の思考ルーチンが挙げられます。
例えば、囲碁や将棋などの対戦ゲームにおいて、戦略の異なるAIをこのパターンで実装します。
以下のような形ですね。


public interface IPlayerStrategy {
    // 入力されたゲーム状態を元に行動を決定
    PlayerAction DetermineAction(GameState state);
}

public class AggressiveStrategy : IPlayerStrategy {
    public PlayerAction DetermineAction(GameState state){
        // 攻撃的な戦略に基づいて行動決定
    }
}

public class DefensiveStrategy : IPlayerStrategy {
    public PlayerAction DetermineAction(GameState state){
        // 防御的な戦略に基づいて行動決定
    }
}

public Player {
    private readonly IPlayerStrategy _strategy;

    public Player(IPlayerStrategy strategy){
        _strategy = strategy;
    }

    public PlayerAction GetNextAction(GameState state){
        return _strategy.DetermineAction(state)
    }
}

名が体を表す良いパターンだと思います。
個人の趣味ですが、典型例がゲームなのも良いですね。

実務経験はほぼないので、憶測になってしまうのですが、
実務であれば例えば、あるクラスにおいて、インスタンス生成時に決定される同一のフラグによって挙動を切り替える処理が何度も登場するような場合に、
Strategyパターンを適用できるのではないかと思っています。
インタフェースを切り、フラグのオンオフの挙動をそれぞれその具象クラスに切り出すことで、
外部のクラスには影響を及ぼさずに、フラグ毎の処理の見通しを良くできるような気がしています。

経験を積んで、パターンの使いどころを見極められるようになりたいです。

C# のコーディング規則

C# のコーディング規則
Microsoft公式ドキュメントに「C# のコーディング規則」が追加されました。
2021/5/14 の新しい内容です。

この記事のガイドラインは、サンプルおよびドキュメントを開発するために Microsoft によって使用されます。これらは、.NET ランタイムの C# コーディング スタイル ガイドラインから採用されました。それらを使用することも、ニーズに合わせて調整することもできます。 主な目的は、プロジェクト、チーム、組織、または会社のソース コード内での一貫性と読みやすさです。

とあるように、Microsoftでも使用される内容なので、
コーディング規則はこれをベースにするのが良さそうです。
フィールドはキャメルケースに_プレフィクスを付ける事が明文化されています。

  • private または internal のフィールドに名前を付ける場合は、キャメルケースを使用し、_を使用してプレフィックスを付けます。
  • ステートメント補完をサポートする IDE でこれらの名前付け規則に従う C# コードを編集するときは、「_」と入力すると、オブジェクト スコープのすべてのメンバーが表示されます。
  • なので自動実装ではないプロパティを使用する場合、
    バッキングフィールド名はプロパティ名の先頭を小文字にして_プレフィックスを付けたものになります。

    あと気になった点は、次のような点でしょうか。
    コメントは文章で書くようにという事ですね。

  • private または internal である static フィールドを使用する場合は、s_プレフィックスを使用し、スレッド静的には t_を使用します。
  • コメントのテキストはピリオドで終了します。
  • もちろん最初に書いてあるように、この規則はニーズに合わせて調整することもできますが、
    基本の規則として知っておくと良いと思います。

    PLINQ(Parallel LINQ)の実行速度

    .netのPLINQ(Parallel LINQ)による並列実行の効果がどれほどなのか理解していなかったのでテストしました。

    Select、Where、AverageをLINQとPLINQで実行し、速度を確認します。

    
    var items = Enumerable.Range(1, 100_000_000).ToArray();
    
    var sw1 = Stopwatch.StartNew();
    var results1 = items.Select(x => Math.Pow(x, 0.5)).ToArray();
    Debug.Print($"Select LINQ {sw1.Elapsed}");
    
    var sw2 = Stopwatch.StartNew();
    var results2 = items.AsParallel().Select(x => Math.Pow(x, 0.5)).ToArray();
    Debug.Print($"Select PLINQ {sw2.Elapsed}");
    
    var sw3 = Stopwatch.StartNew();
    var results3 = items.Where(x => Math.Pow(x, 0.5) >= 100).ToArray();
    Debug.Print($"Where LINQ {sw3.Elapsed}");
    
    var sw4 = Stopwatch.StartNew();
    var results4 = items.AsParallel().Where(x => Math.Pow(x, 0.5) >= 100).ToArray();
    Debug.Print($"Where PLINQ {sw4.Elapsed}");
    
    var sw5 = Stopwatch.StartNew();
    var results5 = items.Average(x => Math.Pow(x, 0.5));
    Debug.Print($"Average LINQ {sw5.Elapsed}");
    
    var sw6 = Stopwatch.StartNew();
    var results6 = items.AsParallel().Average(x => Math.Pow(x, 0.5));
    Debug.Print($"Average PLINQ {sw6.Elapsed}");
    

    結果

    
    Select LINQ 00:00:04.9831283
    Select PLINQ 00:00:01.1474103
    
    Where LINQ 00:00:04.7858871
    Where PLINQ 00:00:00.7447430
    
    Average LINQ 00:00:03.8039131
    Average PLINQ 00:00:00.3454058
    

    今回のテストプログラムの場合、4倍~11倍の速度改善になっています。
    .AsParallel()を追加するだけで、これだけの速度改善になるので、処理が遅くて困った場合にはまずPLINQを試すのが良さそうです。