なぜ、カプセル化の理解が重要なのか、その理由

この記事では、当社の新人エンジニア研修の参考に C# を解説します。

前回は「継承(拡張)」について学びました。今回はカプセル化についてです。カプセル化は、データ隠蔽とセットの概念として語られることが多いです。

カプセル化: オブジェクト(クラス)のデータと、そのデータを扱うメソッドを一つの単位にまとめ、外部から直接データを操作させないようにする仕組みです。

データ隠蔽: クラスの内部データに対する直接的なアクセスを制限し、外部からの不正・予期しない変更を防ぐこと。

オブジェクト指向では、プログラムを部品(オブジェクト)とみなし、その部品の組み合わせで大きなシステムを作ります。物理的な世界で「部品の中身(内部構造)をむやみに触れない」ことが重要なように、ソフトウェアでも「クラスの内部データは勝手に触れない」設計が大切なのです。


1. カプセル化の意義

「関連するデータと処理」を1つのクラスにまとめる

既に作ってきたクラスは「フィールド」と「メソッド」を1つにまとめていますが、ポイントは“関連する”データだけをまとめる、ということです。

public class LaptopPc
{
    public int Id;
    public string Name;
    // もしここに会社情報をベタ書きすると…?
    // => カプセル化が不十分
}

もし会社名や住所、電話番号など無関係な情報まで LaptopPc に入れてしまうと、責任の境界が曖昧になり保守しづらくなります。
そこで会社情報は別のクラス Company にまとめ、LaptopPcCompanyhas-a関係(フィールドとして持つ)にすると、自然な設計となります。

単一責任の原則 (SRP: Single Responsibility Principle)

  • 変更理由が同じものは1つのクラスにまとめる
  • 変更理由が異なるものは別クラスに分ける

これを意識して「責任を1クラスに1つにする」ことで、カプセル化がよりしやすくなります。


2. データ隠蔽の意義

外部からデータを直接触らせない

C#でも、フィールドやプロパティの公開・非公開が重要です。外部に公開されていないメンバーは勝手に変更できないので、意図しない操作を防ぐことができます。

例: .NETのクラスにも「private フィールド」「public プロパティ」が多い

たとえば、.NETの内部ライブラリを見ると、多くのクラスで内部データ(private field)を持ち、外部アクセスはpublicのプロパティやメソッドに制限しています。

// イメージ例
public class SomeClass
{
    private int _someValue;

    public int SomeValue
    {
        get { return _someValue; }
        set
        {
            if (value < 0) throw new ArgumentException("...");
            _someValue = value;
        }
    }
}

こうすることで_someValue を直接いじれないため、不正な値が入らないように制御できます。


データ隠蔽を実際に行う例

public class Player
{
    private int balance = 1000; // 初期値1000

    // 外部から残高を減らす操作はメソッドで行う
    public void Withdraw(int amount)
    {
        if (balance - amount < 0)
        {
            Console.WriteLine("残高不足です");
        }
        else
        {
            balance -= amount;
            Console.WriteLine($"現在の残高: {balance}");
        }
    }
}

public class Example03
{
    public static void Main()
    {
        Player p = new Player();
        p.Withdraw(200);   // OK => 残高 800
        p.Withdraw(1000);  // 残高不足
    }
}

<実行例>

現在の残高: 800
残高不足です

ここで balancepublicにしていたら、外部から p.balance = -999999; のように無茶な操作が可能になってしまいます。privateで隠蔽し、メソッド経由で操作することで整合性を守れます。


アクセサ(Getter/Setter)メソッド

C#ではJプロパティを通じてフィールドにアクセスします。

public class Calculator
{
    private int _num1;
    private int _num2;

    // プロパティを使ったアクセサ
    public int Num1
    {
        get { return _num1; }
        set { _num1 = value; }
    }

    public int Num2
    {
        get { return _num2; }
        set { _num2 = value; }
    }

    public int Add()
    {
        return _num1 + _num2;
    }
}

public class Example
{
    public static void Main()
    {
        Calculator c = new Calculator();
        c.Num1 = 5;
        c.Num2 = 10;
        Console.WriteLine(c.Add()); // 15
    }
}

  • こうしておけば、数値を設定(setter)・取得(getter)する際に検証ロジックや変換を入れることも可能です。
  • 「フィールドはprivate」「外部はプロパティかメソッドからアクセス」 という設計が一般的です(自動実装プロパティなどの機能もあります)。

3. クラスやインタフェースのアクセス修飾子

C#におけるクラスやインタフェースのアクセス修飾子は、基本的に以下です:

  • public : どのアセンブリ(プロジェクト)からでもアクセス可能
  • internal : 同一アセンブリ(プロジェクト)内からのみアクセス可能
  • (private/protected) : クラス/インタフェース自体には使えません(内部クラスを除く)

4. カプセル化まとめ

オブジェクト指向の三大要素と呼ばれるのは

  1. 継承 (Inheritance)
  2. ポリモーフィズム (Polymorphism)
  3. カプセル化 (Encapsulation)

でした。
カプセル化によりオブジェクトは堅牢になり、外部からのアクセスを制御して整合性を保てます。部品交換がしやすい設計にもつながります。


<まとめ:隣の人に正しく説明できたらチェックを付けましょう>

□ カプセル化とは、関連する情報(データ)とそれに対する処理(メソッド)を1つにまとめ、クラスとして構築することである。これにより、データと処理が密接に結び付き、設計がシンプルかつ分かりやすくなる。

□ データ隠蔽とは、クラスのメンバー(フィールドやメソッド)の公開範囲を必要最低限に限定することである。アクセス修飾子を使用して、外部からの不要な操作を防ぎ、クラスの内部構造を保護する。

「フィールドの値を更新するときには必ずプロパティやメソッドを介して行い、フィールドには直接アクセスさせないこと」がデータ隠蔽の基本的な実践である。C#では、プロパティ(getとset)を使用してこれを実現する。

□ 他のプログラマに設計者が意図しない操作をさせないことがデータ隠蔽の最大の効果である。これにより、クラスの利用方法が制約され、意図しない不正な値の設定や、予期しない動作を防ぐことができる。

□ C#の基本的なクラス設計においては、フィールドはprivateまたはprotectedに設定し、外部からアクセスが必要な場合は、プロパティをpublicにすることが推奨される。

以上、今回は「カプセル化と情報隠蔽で部品の完成度を高める」方法について見てきました。

次回は、「例外処理で想定外の事態に強いシステムにする」です。

例外処理を使って、途中で落ちてしまわない頑強なプログラムを作っていきましょう。

カプセル化と情報隠蔽で部品の完成度を高める 最後までお読みいただきありがとうございます。