CodeGym /コース /C# SELF /カプセル化を実践で使ってみよう

カプセル化を実践で使ってみよう

C# SELF
レベル 17 , レッスン 4
使用可能

1. アクセスをコントロールしよう

カプセル化って「全部隠す」ことじゃなくて、コントロールされたアクセスを提供することなんだ。

初心者はよく、カプセル化=全部のフィールドをprivateにすることだと思いがち。でも確かにフィールドは普通privateだけど、それだけじゃないよ。大事なのはデータを隠すことじゃなくて、アクセスを管理することなんだ。

時には完全に変更禁止(例えばget-onlyプロパティ)にしたり、時にはバリデーション付きで読み書きOKにしたり、時にはクラス内だけで読み書きOK(public Type Property { get; private set; })にしたりすることもあるよ。

カプセル化を使うと、オブジェクトの「ルール」を作れるんだ。どうやって変更できるか決めて、その変更がいつも正しい状態になるように保証できる。

実際のプロジェクト、特に大きなチームだと、カプセル化なしだとカオスになるよ。みんなが他人のオブジェクトの中身を直接いじれちゃって、バグや衝突が無限に発生する。カプセル化が秩序を作って、各モジュール(クラス)が自分のデータに責任を持てるようにしてくれるんだ。

これはSOLID原則の中でも特に単一責任の原則(Single Responsibility Principle)オープン/クローズド原則(Open/Closed Principle)の土台だよ。詳しくは後でやるけど、とりあえずカプセル化はコードを強く、柔軟で、分かりやすく、メンテしやすくしてくれるって覚えておこう!


// カプセル化の目的:大事なデータの周りに見えないフェンスを作る
// 誰も外からうっかり「やらかす」ことができない!
カプセル化はオブジェクトを変な変更から守ってくれる

カプセル化は、オブジェクトの大事なデータの周りに見えないフェンスを立てる感じ。誰かがうっかりやらかすのを防いでくれる。例えば、犬の年齢がマイナスになったら困るよね?カプセル化がそれを見張ってくれるんだ。それに、クラスの使い方も簡単になる。他のプログラマーは中身を覗かなくても、君が見せたい部分だけ使えばいい。しかも、もし明日クラスの中身を変えても(例えば年齢を数値じゃなくて誕生日で持つようにしても)、外からは全然気づかれない。インターフェースが同じならOK!これがカプセル化の本当の価値だよ!

2. カプセル化のメリット

なんでこれがプロジェクトやキャリアに大事なの?

データの整合性(Data Integrity):
カプセル化で、オブジェクトのデータがいつも正しくて論理的な状態に保てる。これでバグがめっちゃ減るよ。面接でOOPについて聞かれたとき、カプセル化がどうやってデータの整合性を守るか説明できたら、かなりポイント高い!

柔軟性とメンテしやすさ(Flexibility & Maintainability):
例えば、最初は犬の年齢をint Ageで持ってたとする。でも1年後にクライアントが「やっぱり年齢じゃなくて誕生日が知りたい!」って言い出したら?

カプセル化なし: もしAgepublic intだったら、コードのあちこちでmyDog.Ageに直接アクセスしてるはず。全部書き換えてintDateTimeに変えて、年齢計算のロジックも直さなきゃいけない。地獄だよ!

カプセル化あり: もしprivate int _age;public int Age { get; set; }だったら、内部のフィールドをprivate DateTime _dateOfBirth;に変えて、getsetのロジックを書き換えるだけでOK。外のコードはmyDog.Ageを使い続けられる。外からは何も変わったように見えない。これが疎結合(loose coupling)ってやつ!

ね、すごいでしょ?クラスの「中身」だけ変えて、「外側」(公開インターフェース)はそのまま!

デバッグが楽になる(Easier Debugging):
オブジェクトのデータがおかしくなった時、ちゃんとカプセル化してれば、バグの原因はそのクラスの中か、公開プロパティ経由しかないって分かる。バグ探しの範囲がクラス内に絞れるから楽!

APIデザインが良くなる:
クラスをカプセル化すると、どこが公開「契約」(API)で、どこが内部実装かハッキリする。これでクリーンで分かりやすいインターフェースが作れる。他の開発者(や未来の自分)も使いやすい!

セキュリティ(Security):
C#はC++みたいな超セキュリティ重視の言語じゃないけど、カプセル化はやっぱ大事。誰がどうやってデータに触れるかコントロールできるし、オブジェクト内の機密情報への変なアクセスや変更も防げるよ。

3. C#でカプセル化はどうやる?

C#でカプセル化するには主にこれを使うよ:

アクセス修飾子(private, publicなど):
クラスのフィールドをprivateにして、外から直接いじれないようにする。これが情報隠蔽(information hiding)ってやつ。
メソッドやプロパティをpublicにして、コントロールされたアクセスを提供する。これが公開インターフェースだよ。

プロパティ(Properties):
もう勉強したけど、プロパティはget(読み取り)とset(書き込み)メソッドのシンタックスシュガー。フィールドを隠しつつ、公開の窓口を作れる。しかもsetアクセサにバリデーションロジックも入れられる!

例:Dogクラスのカプセル化

まずはやっちゃダメな例(カプセル化なし)から:


public class Dog
{
    public string Name;
    public int Age;

    public void Bark()
    {
        Console.WriteLine($"{Name}が言う: ワン!");
    }
}

このバージョンだと、他のクラスが犬の名前や年齢を好き勝手に変えられる。例えば:

Dog dog = new Dog();
dog.Name = "";      // 名前を空にできちゃう!
dog.Age = -100;     // めっちゃ「古い」犬にできちゃう

現実ではこんなこと滅多にないけど、コードだとカプセル化しないとよく起きるよ。

データを守る:フィールドをprivateにしよう

フィールドをprivateにしよう。これでクラス以外から直接いじれなくなる:


public class Dog
{
    private string _name;
    private int _age;

    public void Bark()
    {
        Console.WriteLine($"{_name}が言う: ワン!");
    }
}

これでもうこんなアクセスはできない:

Dog dog = new Dog();
dog._name = "レックス"; // コンパイルエラー!

プロパティ(Properties)経由でデータにアクセス

でもやっぱり犬の名前を知りたいし(画面に出したいとか)、たまに変えたい時もあるよね。そんな時はプロパティを使おう!


public class Dog
{
    private string _name;
    private int _age;

    public string Name
    {
        get { return _name; }
        set
        {
            // バリデーション追加:名前は空じゃダメ
            if (string.IsNullOrWhiteSpace(value))
            {
                Console.WriteLine("エラー: 名前は空にできません!");
            }
            else
            {
                _name = value;
            }
        }
    }

    public int Age
    {
        get { return _age; }
        set
        {
            // ちょっとリアルに:年齢はマイナス不可!
            if (value < 0)
            {
                Console.WriteLine("エラー: 年齢はマイナスにできません!");
            }
            else
            {
                _age = value;
            }
        }
    }

    public void Bark()
    {
        Console.WriteLine($"{_name}が言う: ワン!");
    }
}

これでデータは守られるけど、コントロールされたインターフェース経由で使える:

Dog dog = new Dog();
dog.Name = "バルボス";      // OK!
dog.Age = 3;

dog.Name = "";            // エラーが出て、フィールドは変わらない!
dog.Age = -1;             // またエラー

自動プロパティ

バリデーションとかいらないなら、フィールドを書かずに自動プロパティを使おう:


public class Dog
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void Bark()
    {
        Console.WriteLine($"{Name}が言う: ワン!");
    }
}

この場合、NameAgeは実質「カプセル化」されてる。直接アクセスできず、プロパティ経由でしか読んだり書いたりできない。

4. 振る舞いもカプセル化:必要なメソッドだけ公開しよう

カプセル化はデータだけじゃなくてメソッドにも大事!クラスのメソッドの中には、内部の「歯車」みたいなもので、外に見せなくていいやつもある。

例:


public class Dog
{
    // 内部専用
    private void WagTail()
    {
        Console.WriteLine("犬がしっぽを振ってる。");
    }

    // みんなに公開
    public void Bark()
    {
        WagTail(); // クラス内で隠しメソッドを呼ぶ
        Console.WriteLine("ワン!");
    }
}

これで、犬自身以外はしっぽを振らせることはできない:

Dog dog = new Dog();
dog.WagTail();     // エラー!メソッドはprivate。
dog.Bark();        // Barkの中でWagTailが呼ばれる

違いまとめ:フィールド・メソッド・プロパティと可視性

まとめよう:

クラスの部分 privateにできる? publicにできる? なぜアクセス制限する?
フィールド はい はい(でもやめとこう!) データを守るため
メソッド はい はい 実装の詳細を隠すため
プロパティ はい はい 読み書きをコントロールするため

おすすめ:フィールドはprivateにして、外にはプロパティやメソッドだけ見せよう。

1
アンケート/クイズ
プロパティ (Properties)、レベル 17、レッスン 4
使用不可
プロパティ (Properties)
プロパティとアクセス修飾子
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION