静的設計とインスタンス設計
静的な状態が適切な場面を判断します。
「静的設計とインスタンス設計」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。
設計上の選択
static と instance のどちらを選ぶかは、設計上の判断です。重要なのは、その動作がオブジェクトごとの状態に依存するのか、それとも入力だけに依存するのかという点です。
using System;
class Program
{
// Depends only on inputs -> good static method
static int Max(int a, int b) => a > b ? a : b;
static void Main()
{
Console.WriteLine(Max(3, 8));
}
}状態を持たないロジックには static を使う
メソッドがインスタンスフィールドを必要とせず、入力を変換するだけなら static にします。数学用のヘルパーやフォーマッターが代表的な例です。
using System;
static class Convert2
{
public static double MilesToKm(double miles) => miles * 1.60934;
}
class Program
{
static void Main()
{
Console.WriteLine(Convert2.MilesToKm(10).ToString("0.0") + " km");
}
}状態には instance を使う
動作がオブジェクトごとに異なるデータに依存する場合は、instance を使用します。それぞれのオブジェクトが独自の状態を保持します。
using System;
class Counter
{
private int _count;
public void Increment() => _count++;
public int Value => _count;
}
class Program
{
static void Main()
{
var a = new Counter();
var b = new Counter();
a.Increment();
a.Increment();
b.Increment();
Console.WriteLine("a=" + a.Value + ", b=" + b.Value);
}
}共有状態はグローバル状態
static フィールドは実質的にグローバルです。プログラムの多くの部分から読み書きされると、見つけにくいバグの原因になることがあります。ユーザーごとに独自のコピーが必要な場合は、インスタンス状態を優先しましょう。
using System;
class BadCart
{
public static int Items; // shared by EVERYONE -> usually wrong for a cart
}
class Program
{
static void Main()
{
BadCart.Items++;
BadCart.Items++;
Console.WriteLine("All carts share: " + BadCart.Items);
}
}テスト容易性
純粋な static メソッドは、同じ入力に対して同じ出力を返すため、テストが簡単です。一方、変更可能な static 状態はテスト間に影響を漏らし、テストを壊れやすくします。
using System;
static class Calc
{
public static int Add(int a, int b) => a + b; // trivially testable
}
class Program
{
static void Main()
{
Console.WriteLine(Calc.Add(2, 2) == 4 ? "pass" : "fail");
}
}ファクトリーヘルパーには static を使う
static ファクトリーメソッドは、設定済みのインスタンスを説明的な名前で作成できる、すっきりした方法です。
using System;
class Color
{
public int R, G, B;
private Color(int r, int g, int b) { R = r; G = g; B = b; }
public static Color Red() => new Color(255, 0, 0);
public static Color White() => new Color(255, 255, 255);
}
class Program
{
static void Main()
{
var c = Color.Red();
Console.WriteLine(c.R + "," + c.G + "," + c.B);
}
}new を避けるためだけに static にしない
オブジェクトの作成を避けるために、すべてを static にしてはいけません。型が状態を持つものを表す場合は、インスタンスを使うことで、その状態を分離して安全に管理できます。
using System;
class Player
{
public string Name;
public int Health = 100;
public void TakeDamage(int d) => Health -= d;
}
class Program
{
static void Main()
{
var p1 = new Player { Name = "A" };
var p2 = new Player { Name = "B" };
p1.TakeDamage(30);
Console.WriteLine(p1.Name + ": " + p1.Health + ", " + p2.Name + ": " + p2.Health);
}
}両方を適切に組み合わせる
多くのクラスでは、共有する定数やカウンターに static メンバーを使い、オブジェクトごとのデータにインスタンスメンバーを使います。この組み合わせは、ごく自然な書き方です。
using System;
class Invoice
{
private static int _seq = 1000;
public readonly int Number;
public decimal Amount;
public Invoice(decimal amount)
{
Number = _seq++;
Amount = amount;
}
}
class Program
{
static void Main()
{
var i1 = new Invoice(50m);
var i2 = new Invoice(75m);
Console.WriteLine(i1.Number + " / " + i2.Number);
}
}並行性に関する注意
共有された static 状態には複数のスレッドが同時にアクセスできるため、競合状態が発生する危険があります。1つのスレッドに限定されたインスタンス状態は、デフォルトでより安全です。
using System;
class SafeCalc
{
// No shared mutable state -> naturally thread-safe
public static long Factorial(int n)
{
long result = 1;
for (int i = 2; i <= n; i++) result *= i;
return result;
}
}
class Program
{
static void Main()
{
Console.WriteLine(SafeCalc.Factorial(5));
}
}簡単な判断基準
Static = 型に属し、オブジェクトごとの状態を使わない動作です。Instance = 特定のオブジェクトを読み取る、または変更する動作です。迷った場合は、状態を持つものには instance を優先しましょう。
using System;
class Temperature
{
public double Celsius; // instance state
public static double ToF(double c) => c * 9 / 5 + 32; // stateless helper
}
class Program
{
static void Main()
{
var t = new Temperature { Celsius = 30 };
Console.WriteLine(Temperature.ToF(t.Celsius));
}
}まとめ
優れた設計では、状態を持たないヘルパー(static)と状態を持つモデル(instance)を分離し、共有される変更可能な状態を意図的に最小限に抑えます。
using System;
static class TaxRules
{
public static decimal Apply(decimal amount, decimal rate) => amount + amount * rate;
}
class Sale
{
public decimal Subtotal;
public decimal Total(decimal rate) => TaxRules.Apply(Subtotal, rate);
}
class Program
{
static void Main()
{
var s = new Sale { Subtotal = 100m };
Console.WriteLine(s.Total(0.2m));
}
}理解度チェック
static と instance の設計について理解できているか確認しましょう。
まとめ
入力だけに依存する状態を持たないロジック(ヘルパー、ファクトリー、定数)には static を、オブジェクトごとの状態に結び付いた動作には instance を選びます。変更可能な static 状態は実質的にグローバルで、テストが難しく、並行性に関するバグの原因にもなるため、最小限にとどめ、意図を明確にしましょう。
using System;
static class Util { public static int Sq(int n) => n * n; }
class Box { public int Size; public int Area() => Util.Sq(Size); }
class Program
{
static void Main()
{
var box = new Box { Size = 4 };
Console.WriteLine(box.Area());
}
}AI チューターと学ぶ C# — 無料
ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。
- コース
- 93
- レッスン
- 346
よくある質問
「静的設計とインスタンス設計」レッスンは無料ですか?
はい。「静的設計とインスタンス設計」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。
「静的設計とインスタンス設計」で何を学びますか?
静的な状態が適切な場面を判断します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C# Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「静的設計とインスタンス設計」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC# Academyレッスンでコードを書いて実行できますか?
はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 静的メソッドとフィールド
- ユーティリティ用の静的クラス
- 定数とreadonlyフィールド
- 静的設計とインスタンス設計