ソフトウェアをビルドする

多くの場合、複数のアルゴリズムまたは実装が同じタスクを正しく解決できます。 選択したコードが設計を伝えます。 コードを読み取る開発者とコーディング エージェントは、その構造を使用して、各操作に必要なもの、属する場所、およびいつ実行されるかという意図を推測します。

Tip

この記事は、基本的な C# の式、ステートメント、メソッドを知っている開発者向けの Fundamentals セクションの一部です。 プログラミングを初めて使用する場合は、最初に「 はじめ に」チュートリアルから始めてください。

このセクションでは、アルゴリズムに対する有効なアプローチを比較し、その動作の微妙な違いと、読者がアルゴリズムを解釈する方法について説明します。 型、式、ステートメント、メソッドなどの使い慣れた言語要素を組み合わせて、意図したデザインを伝えるフォームを選択します。

動作と設計意図を比較する

実装を選択する前に、他のコードで使用する方法を検討してください。 そのコードは 呼び出し元です。入力を提供し、操作を呼び出し、結果を使用します。 すぐに完了した結果が必要ですか、それとも値を一度に 1 つずつ処理できますか? この操作のヘルパー部分は単独ですか、それとも他の操作でもそれを使用しますか? これらの質問は、必要な動作とコードが伝える設計の両方を比較するのに役立ちます。

短いインライン操作では、読者が使用時点の手順を示します。 たとえば、 Where(task => task.Priority > 3) はフィルター処理ルールをすぐに表示します。 Where(IsUrgent)のように、名前付きヘルパーはルールの目的を強調し、閲覧者がその詳細を個別に検査できるようにします。 ステップまたはその目的が周囲のコードを最もよく説明しているかどうかに基づいて選択します。

ヘルパーを宣言すると、その目的のスコープが伝えられます。 ShowDashboard内のローカル関数は、ヘルパーがその操作に属し、他のメンバーがその操作を呼び出さないようにすることを示します。 包含型のプライベート メソッドを使用すると、他のメンバーがヘルパーを使用できるようになり、型内の別の場所で再利用することが推奨されます。 どちらも同じ計算を整理できますが、読者の役割に関するさまざまな期待に導きます。

ヘルパーの入力を指定する方法は、その依存関係を伝えます。 IsUrgent(task, minimumPriority)などのパラメーターとして制限を渡すと、その入力が各呼び出しで表示されます。 ローカル関数またはラムダは、代わりにminimumPriority引数として受け取ることなく、周囲のスコープから変数を使用してキャプチャできます。 Capture は、ヘルパーを含む操作のコンテキストに接続します。明示的なパラメーターを指定すると、指定されたデータの識別が容易になります。 周囲の状態をキャプチャしないように強制する場合は、 static ローカル関数またはラムダを使用します。

結果を返す方法は、作業が行われるときに通信します。 コレクションをビルドして返すメソッドは、呼び出し元がコレクションを受け取る前に機能します。 反復子は、呼び出し元が要求すると要素を生成するため、すべての要素が生成される前に呼び出し元が停止できます。 どちらも同じ値を生成する可能性がありますが、呼び出し元は、完了した結果を受け取っているか、まだ計算が進んでいるシーケンスを受け取っているかを理解する必要があります。

選択ごとに、最初に呼び出し元のコードに必要な動作を特定します。 次に、別の開発者またはコーディング エージェントが名前、スコープ、入力、結果から推論するものを検討します。 これらの期待値がデザインに一致するフォームを選択し、コードの図形だけでは明らかにならない重要な動作を説明します。 目標は、任意の場所に 1 つのフォームを適用するのではなく、有効な選択肢から意図的に選択することです。

例のデザインの選択肢を読み取る

次の例は、複数の選択肢が設計のさまざまな部分を伝える方法を示しています。 緊急で開始する準備ができているタスクが表示されます。 ProjectTaskは、タスクのタイトル、優先度、ブロック状態を保持します。

var tasks = new[]
{
    new ProjectTask("Confirm venue", Priority: 3, IsBlocked: false),
    new ProjectTask("Print badges", Priority: 2, IsBlocked: true),
    new ProjectTask("Test microphones", Priority: 2, IsBlocked: false),
};

ShowDashboard("Community meetup", tasks, minimumPriority: 2);

static void ShowDashboard(
    string projectName,
    IEnumerable<ProjectTask> tasks,
    int minimumPriority)
{
    var urgentTasks = tasks.Where(
        task => task.Priority >= minimumPriority);

    foreach (ProjectTask task in ReadyTasks(urgentTasks))
    {
        Console.WriteLine(FormatTask(task));
    }

    string FormatTask(ProjectTask task) =>
        $"{projectName}: {task.Title}";
}

static IEnumerable<ProjectTask> ReadyTasks(
    IEnumerable<ProjectTask> tasks)
{
    foreach (ProjectTask task in tasks)
    {
        if (!task.IsBlocked)
        {
            yield return task;
        }
    }
}

record ProjectTask(string Title, int Priority, bool IsBlocked);

各選択肢は、作業の異なる部分を明確に保ちます。

  • Whereに渡されるラムダは、それを使用する操作の横に短い優先順位の規則を設定します。
  • ローカル関数 FormatTask は、 ShowDashboardにのみ属する書式設定ステップに名前を付けます。
  • 反復子 ReadyTasks を使用すると、呼び出し元は中間コレクションを作成せずに準備完了タスクを使用できます。
  • foreach ループは最終的なアクションを実行し、各結果を明示的に書き込みます。

比較のために、この準備テストは単純なフィルターであるため、Where(static task => !task.IsBlocked)ReadyTasksを置き換えることができます。 どちらのフォームも、列挙までフィルターを延期します。 ラムダは、呼び出しサイトにルールを配置します。 ReadyTasks 規則に名前を付けます。 読者が呼び出し元を理解するのに役立つ情報に基づいて、それらを選択します。

以下の記事では、次のような比較を使用して、各アプローチが適合するタイミングと、読者が自分の選択から推測できる内容を説明します。

次のステップ