なぜ、カプセル化の理解が重要なのか、その理由
この記事では、当社の新人エンジニア研修の参考に C# を解説します。
前回は「継承(拡張)」について学びました。今回はカプセル化についてです。カプセル化は、データ隠蔽とセットの概念として語られることが多いです。
カプセル化: オブジェクト(クラス)のデータと、そのデータを扱うメソッドを一つの単位にまとめ、外部から直接データを操作させないようにする仕組みです。
データ隠蔽: クラスの内部データに対する直接的なアクセスを制限し、外部からの不正・予期しない変更を防ぐこと。
オブジェクト指向では、プログラムを部品(オブジェクト)とみなし、その部品の組み合わせで大きなシステムを作ります。物理的な世界で「部品の中身(内部構造)をむやみに触れない」ことが重要なように、ソフトウェアでも「クラスの内部データは勝手に触れない」設計が大切なのです。
1. カプセル化の意義
「関連するデータと処理」を1つのクラスにまとめる
既に作ってきたクラスは「フィールド」と「メソッド」を1つにまとめていますが、ポイントは“関連する”データだけをまとめる、ということです。
public class LaptopPc
{
public int Id;
public string Name;
// もしここに会社情報をベタ書きすると…?
// => カプセル化が不十分
}
もし会社名や住所、電話番号など無関係な情報まで LaptopPc
に入れてしまうと、責任の境界が曖昧になり保守しづらくなります。
そこで会社情報は別のクラス Company
にまとめ、LaptopPc
がCompany
をhas-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
残高不足です
ここで balance
をpublicにしていたら、外部から 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. カプセル化まとめ
オブジェクト指向の三大要素と呼ばれるのは
- 継承 (Inheritance)
- ポリモーフィズム (Polymorphism)
- カプセル化 (Encapsulation)
でした。
カプセル化によりオブジェクトは堅牢になり、外部からのアクセスを制御して整合性を保てます。部品交換がしやすい設計にもつながります。
以上、今回は「カプセル化と情報隠蔽で部品の完成度を高める」方法について見てきました。
次回は、「例外処理で想定外の事態に強いシステムにする」です。
例外処理を使って、途中で落ちてしまわない頑強なプログラムを作っていきましょう。
IT企業向け新人研修おすすめ資料 無料公開中 (saycon.co.jp)