フリーランスエンジニア必見!!独自案件豊富に揃えています

カプセル化とはなにか?超わかりやすく解説します!

最近は「オブジェクト指向」というキーワードをよく聞くようになりました。

これは、いままでのプログラミング手法と異なり、

モノが自主的に作用しあう形でプログラムを組むというようなプログラミング手法

などと言われます。

「こんな難しく言われてもわかんねえよ!」って方。私もそうでした。

こんにちは。今井@ima_maru)です。

私はこの概念をしっかりと理解するのに一か月くらいはかかりました。

そのくらい理解が難しいものであるということは確かです。

大まかに言って、オブジェクト指向を構成する要素は3つあります。

  1. 継承
  2. カプセル化(今回解説)
  3. ポリモーフィズム

これら3つはオブジェクト指向の基盤を担っているものです。
オブジェクト指向を理解するならば避けては通れない道でしょう。

今回はその中でもデータを保護する役目とされる「カプセル化」について解説します。

皆さんにより効率的に理解してほしいという想いから、初めてプログラミングを学ぶ方にも優しい言葉で、できるだけわかりやすく解説していこうと思います。

カプセル化を理解するには、ざっくりとカプセル化というのがどのようなものなのかというのを知ってから、カプセル化の狙いやメリットを知るという順に学習したほうが理解が深まると思いますので、その順で解説していきます。

  1. カプセル化というのがどのようなものなのか
  2. カプセル化の狙いやメリット

それでは、オブジェクト指向の3つの特徴の1つのカプセル化というのはどういうものなのでしょうか。

見ていきましょう!

\ じゃぱざむブログ公式フリーランスグループ!! /

カプセル化(encapsulation)とは?

カプセル化とは、オブジェクトの情報を隠蔽することによって、不整合を引き起こすような操作をできなくさせる仕組みです。

今となっては「よく表現できてるなあ」と思いますが、初めにこんなこと書かれても「そんなこと言われてもわかんねえよ!」って思うでしょう。そのような方向けに解説していくのでご安心ください。

カプセル化のイメージ

カプセル化とは、オブジェクトやデータをカプセルのように保護するという意味から来ています。

カプセルというと、どのようなものをイメージしますか?

一般的なのは、カプセル状のお薬をイメージする人が多いのではないでしょうか。プログラミングにおいてのカプセル化もそのようなイメージです。

カプセルのイメージ

外から防御されている

包まれている

では何を包んで何から守るのか?

すごく簡単に、具体的に表せば以下のようなことです。

「オブジェクト内のデータ」を包み
「オブジェクト外からの不正アクセス」から守る

要は、オブジェクト外からのデータアクセスを拒否するのです。

これらのデータアクセスの可否アクセス指定子と呼ばれるものによって決められます。

実際にカプセル化がどのように行われるか見てみましょう。

アクセス指定子とカプセル化

カプセル化はデータを保護する役目といいましたが、

  • 実際にどのように保護するのか?
  • 保護をするとどのようなことができなくなるのか?

というところを疑似言語を使って解説していきます。

まずこのようなコードを用意しました。

疑似言語
クラス データ群モデル {

[保護されている]   データA;
[保護されていない] データB;

};

この状態だと、データAは保護されていて、データBは保護されていないデータになります。

データA保護されている → カプセル化されている

データB保護されていない → カプセル化されていない

実際のプログラミング言語だと「private」「public」などのアクセス指定子を用います。

C++
//プログラミング言語C++の場合
class Data_model {

private: int dataA;
public:  int dataB;

};

ここで、保護されていない状態と保護されている状況は何が違うかというと、

「クラス外からのデータへのアクセスができるかできないか」が違います。

疑似言語
メイン文{

	データ群モデル データ群X;

	データ群X.データA = 12; // ×アクセス不可
	データ群X.データB = 34; // 〇アクセス可能

}
データAはカプセル化によって保護されているためオブジェクト外からのアクセスができません。

一方データBは保護されていないためアクセスができます。

実際、プログラミング言語C++で書くとこんな感じになります。

C++
int main() {

	Data_model dataX;

	dataX.dataA = 12; // ×アクセス不可
	dataX.dataB = 34; // 〇アクセス可能

}
もう一度言うようですが、カプセル化されているデータに対してはメイン文などの外からのアクセスができないようになります。

アクセスができなければ、データを変更することができません。

「使えないなら意味ないのでは?」

こう思いませんか?

しかし、オブジェクト外からでもカプセル化されたデータを変更する方法が存在します

それは、クラス内に保護されていない関数を作って、その関数によってデータを変更してもらうという方法です。

カプセル化はこれとセットで使われます。

例えばこれと似た例で、「生徒」というクラスを作り「名前」を持たせます

この「名前」はカプセル化して、直接的な変更はできないようにします。

最後に「改名」という名前を変更する関数をクラス内に用意します。この関数は外からのアクセスもできるように保護しないでおきます。

疑似言語
クラス 生徒{

[保護されている] 名前;
[保護されていない] 改名(新しい名前) { 名前 = 新しい名前 };

}

メイン文{
	
	生徒 生徒1;
	
	生徒1.名前 = "田中 太郎"; // ×アクセス不可
	生徒1.改名("田中 太郎");  // 〇アクセス可能

}

このように、クラス内にカプセル化されていない「改名」という関数を作り、それを経由して名前を変えてもらう形にします

でもまだ疑問が残ります。

「カプセル化を使ってデータの直接変更ができなくする。」
「関数を使って間接的なデータ変更ができることも分かった。」

「なんでデータを直接変更しちゃダメなんだ???」

そうなんです。私はここで相当な時間悩みました。

では、なぜカプセル化をしてデータのアクセスをクラス内だけに限定するのでしょうか?

主人公の体力を使ってメリットとともに解説していきます。

カプセル化とは
  1. オブジェクトのデータが外から直接変更されることを禁止する
  2. アクセス指定子によってカプセル化するかしないか決められる
  3. カプセル化されているデータはクラス内の関数によって変更できる

カプセル化の狙いとメリット

カプセル化の狙いは「データを保護する」ということです。それに尽きます。

データの変更をオブジェクト自身に任せることによって、バグ(不整合)を引き起こさせないようにすると言ったりもします。

先ほど学んだように、カプセル化を用いれば、クラス≒オブジェクト内の関数を一度経由させないといけない状態を作れます。

この関数に不正なデータ変更を禁止する条件や設定を設ければ、データのバグを防げます。

要は、

  1. 直接データを操作させずに、一度関数を通す状況を作る
  2. この関数に条件や設定を設けることで、不正なデータ操作を防ぐ

こんな感じのイメージです。

カプセル化のわかりやすい例

まずは、主人公のもとになるクラス「ユニット」を作りましょう。

疑似言語
クラス ユニット{

[保護されていない]
	HP = 50;     //体力
	MAX_HP = 80; //最大体力

};

このクラスには「体力」「最大体力」を持たせました。

初期値はHP 50MAX_HP 80に設定しました。なので、

HP 50/80

と表現することができますね。

まずは主人公のオブジェクトを作りましょう。

疑似言語
メイン文{

	ユニット 主人公;
	...

これで今、クラス「ユニット」をもとに作られた主人公というオブジェクトができました。

もちろん体力は、

HP 50/80

初期値でこの値になっています。

この状態で、主人公のHPに「直接50を足す」ということをしてみましょう。

疑似言語
主人公.HP += 50; // →[HP100/80]

こうしたらHPは最大体力の80を超え100になってしまいました。

HP 100/80

このような最大体力を超える表記を許しているゲームもありますが、これを許していないゲームではこれらを一般的にバグ(不整合)と言います。

逆に、主人公のHPから「直接200を引く」ということをしてみましょう。

疑似言語
主人公.HP -= 200; // →HP[-100/80]

今度はHPが0を下回ってマイナスの値をとってしまいました。これもバグの一種です。

HP -100/80

これらのバグは直接データを操作したことが原因です。

直接データを操作することの危険性
  1. 体力の値を直接操作
  2. 適正値を超え体力が異常値

なら直接データを操作できなくさせましょう。カプセル化の出番ですね。

まずはデータを保護=カプセル化をしてみましょう。

疑似言語
クラス ユニット{

[保護されている]
	HP = 50;     //体力
	MAX_HP = 80; //最大体力

}

このようにすると、HPやMAX_HPはメイン文やクラス外からは操作できなくなります

でもこれだとデータの変更ができないので、HPを変更できる関数を作りましょう

疑似言語
クラス ユニット{

[保護されている]
	HP = 50;     //体力
	MAX_HP = 80; //最大体力

[保護されてない]
	体力の増減(値) {

		HP += 値;
		if (HPが0以下なら) HP = 0;
		if (HPがMAX_HP以上なら) HP = MAX_HP;

	}
}

このようにすると、HPやMAX_HPのデータの値を変更するときは、一度「体力の増減」という関数を経由して変更されるという形にすることができます。

しかも、体力の増減の関数では、条件分岐により体力が0からMAX_HPまでしか設定できなくなっています。ここがすごい重要。

「体力の増減」関数に「+1000」という値を送っても、最大体力を超えることはありえません。

逆に「-1000」を送っても0を下回りません。

先ほどのバグが防げていると思えませんか?

図にするとこんな感じです。

カプセル化とは、オブジェクトのデータを外からアクセスできなくして保護することを言います。

データに変更を加えるときは、オブジェクト自身が持っている関数にアクセスしてそれによって変更を加えるため、仮に異常なデータ変更が来た場合、適切な値に戻したり、変更を拒否することもできます

こんな風に、オブジェクト自体が自らのデータを変えるようなプログラミング方法が、オブジェクト指向プログラミングのイメージにとても近いです。

重要なのは、「オブジェクト自体が」という部分と「オブジェクト」という概念です。

これらの理由より、カプセル化はオブジェクト指向の三本柱の一つなのです。

カプセル化のメリットとは?

カプセル化はこのHPが上限値を超えてしまわないようなプログラム設計からもわかる通り、

  • 情報を直接操作することをできなくして情報が壊れてしまうことを防止する

こんな役目を担っています。

オブジェクト自身に適切な情報操作を委ねるというのがポイントとなります。

 終わりに

オブジェクト指向3大要素の一つともされる「カプセル化」をできる限りわかりやすく説明したつもりですが、どうでしたでしょうか?

カプセル化というのは、最初聞いたとき「なんで情報を隠す必要があるんだ?」と思いましたが、C++を学習していくうちに少しずつ理解が深まりました。

この記事を見て「なるほどなぁ、そういうことか」と思ってくださる方がいれば、とても嬉しいです。

最後まで読んでいただきありがとうございます。

オブジェクト指向のメリットとは?例に例えてわかりやすく解説!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です