この記事ではオブジェクト指向プログラミングにおけるカプセル化を解説します。
カプセル化とは?
オブジェクト指向プログラミング(OOP)における「カプセル化(encapsulation)」とは、オブジェクトが持つデータへの外部からのアクセスを制限することをいいます。
復習になりますが、オブジェクトとは「プロパティ(変数)」と「メソッド(関数)」を持つデータ構造のことです。
そしてオブジェクトを作るための設計図がクラスです。
カプセル化とは、オブジェクトが持つ「プロパティ」や「メソッド」への外部からのアクセスを制限することです。
クラスを作成する際に、プロパティやメソッドに「private(プライベート)」や「public(パブリック)」などの「アクセス修飾子」を記述します。
アクセス修飾子を記述すると、オブジェクトのプロパティやメソッドに対して、外部からのアクセスを許可したり、拒否したりできます。
このようにオブジェクトへのアクセスを制御することを「カプセル化」といいます。
カプセル化が必要な理由
カプセル化はどのように使うのでしょうか?
なぜオブジェクトのプロパティやメソッドへのアクセスを制御するのでしょうか?
カプセル化の最大のメリットは「オブジェクトのデータを保護できること」にあります。
オブジェクトには、外部から使用されるべきでない変数や関数が含まれる場合があります。
例として、図形の「円」を表現するオブジェクトを考えてみましょう。
以下のような円クラスを考えます。
using System;
class Circle
{
public double Radius { get; set; }
public double Pi { get; set; }
public Circle(double radius)
{
Radius = radius;
Pi = 3.141592;
}
public double Area()
{
return Radius * Radius * Pi;
}
}
円オブジェクトは、円クラスから作成します。
作成する際に、円の半径を指定します。
そして、円オブジェクトのAreaメソッドを呼び出すと面積を計算して返します。
class Program
{
static void Main()
{
Circle circle = new Circle(3); // 半径3の円オブジェクトを作成する
Console.WriteLine(circle.Area());
// 出力: 28.274328
}
}
ここで定義した円クラスでは、プロパティやメソッドがカプセル化されていません。
すべてのプロパティとメソッドのアクセス修飾子がpublicになっているためです。
つまり、Circleオブジェクトのすべてのプロパティやメソッドを外部から参照し、変更できます。
たとえば、円周率を表現するプロパティ「Pi」をオブジェクトの外部から変更してみましょう。
class Program
{
static void Main()
{
Circle circle = new Circle(3);
circle.Pi = 0; // オブジェクトのプロパティ「Pi」を外部から変更する
Console.WriteLine(circle.Area());
// 出力: 0.0
}
}
カプセル化されていないので、プロパティの「Pi」を外部から変更できてしまいました。
ここで変更したのは円周率です。
円の面積は円周率(Pi)が正しい値でなければ、正しく計算できません。
円周率が「0」に変更されたCircleオブジェクトは、もはや円を表現するオブジェクトではなくなってしまいました。
このように、オブジェクトには、外部から変更されたり見られたりするべきではない変数や関数があります。
Circleオブジェクトを円を表現するオブジェクトとして維持するには、プロパティ「Pi」はオブジェクトの外部から変更できないようにすべきです。
このような場合にカプセル化が役に立ちます。
アクセス修飾子
プロパティやメソッドへのアクセスを制限するには「アクセス修飾子」を使います。
クラスを定義するときに「private」や「public」などの「アクセス修飾子」を書くことによって、プロパティやメソッドへのアクセス許可を設定するのです。
アクセス修飾子には以下のような種類があります。プログラミング言語によって多少の差はあるものの、以下のアクセス修飾子はほとんどのオブジェクト指向言語で使われています。
- public
- protected
- private
public
publicはすべてのクラスからアクセスできるようにする修飾子です。
プロパティやメソッドを定義するときにpublicキーワードを書くと、そのクラスだけでなく、他のクラスからもアクセス可能になります。
protected
protectedは、そのクラスとそのクラスの子クラス(サブクラス)からのみアクセスできるようにする修飾子です。
外部のクラスからは隠したいけど、そのクラス自身とそのクラスを継承したクラスでアクセス可能にしたい場合に使います。
private
privateは、そのクラスの中でだけアクセスできるようにするアクセス修飾子です。
privateを付けたプロパティやメソッドには、外部のクラスからはアクセスできなくなります。
カプセル化を使ってみよう
アクセス修飾子を使って先ほどのCircleオブジェクトをカプセル化しましょう。
前述のCircleオブジェクトで、は円周率を表すプロパティ「Pi」への外部からのアクセスを制限できていませんでした。
プロパティ「Pi」へのアクセスを適切に制御して、Circleオブジェクトを「円を表現するオブジェクト」として維持できるようにします。
プロパティ「Pi」への外部のクラスからのアクセスを制限するには「private」アクセス修飾子を使います。
Circleクラスを定義するときに、プロパティ「Pi」のアクセス修飾子をpublicからprivateに書き換えることで、円周率の情報をカプセル化できます。
using System;
class Circle
{
public double Radius { get; set; }
private double Pi { get; set; } // publicをprivateに書き換えた
public Circle(double radius)
{
Radius = radius;
Pi = 3.141592;
}
public double Area()
{
return Radius * Radius * Pi;
}
}
プロパティ「Pi」のアクセス修飾子をpublicからprivateに書き換えただけです。
これによってCircleオブジェクトのプロパティ「Pi」に外部からアクセスすることはできなくなります。
試しにCircleオブジェクトの外部から「Pi」を書き換えてみましょう。
class Program
{
static void Main()
{
Circle circle = new Circle(3);
circle.Pi = 0; // オブジェクトのプロパティ「Pi」を外部から変更する
// Compilation failed: 1 error(s), 0 warnings
// Main.cs(26,16): error CS0122: `Circle.Pi' is inaccessible due to its protection level
// Main.cs(6,30): (Location of the symbol related to previous error)
Console.WriteLine(circle.Area());
// 出力: 0.0
}
}
外部から「Pi」の値を変更しようしてもコンパイルエラーとなり、変更できなくなりました。
以下のエラーメッセージが出力されます。
'Circle.Pi' is inaccessible due to its protection level
Circleオブジェクトのプロパティ「Pi」を保護することができています。
privateにすべきかpublicにすべきか悩んだら?
クラスの実装をしていて「この変数はpublicにすべき?」「このメソッドはprivateにすべき?」と悩む場面があるかもしれません。
もっとも簡単な考え方は「他のクラスに公開する必要がない限りはprivateを使う」というものです。
これを最小権限の原則(Principle of least privilege)といいます。
プロパティやメソッドをむやみにpublicにすると、予期しないバグの原因になります。
とりあえずprivateにしておけば、他のクラスから間違ってアクセスされるリスクを減らすことができます(意図的にprivateなデータにアクセスされる場合を除く)。
カプセル化をまだ使いこなせていないなら、とりあえずはprivateなプロパティやメソッドとして実装し、公開する必要が出てきたらpublicにしましょう。
それで大きな問題に発展することはないはずです(少なくともすべてpublicにするよりはマシです)。
まとめ
この記事では、オブジェクト指向におけるカプセル化を解説しました。
- カプセル化とはオブジェクトが持つデータへの「外部からのアクセス」を制限することです。
- カプセル化によってオブジェクトのデータを適切に保護できます
- privateやprotectedなどのアクセス修飾子を使ってクラスの特定のデータへのアクセスを制限できます
カプセル化の考え方は良いシステム設計のために重要です。
ぜひ使いながら理解して覚えていきましょう。