オブジェクト指向を支えている3つの概念として、「継承」「カプセル化」「ポリモーフィズム」という概念があります。
本記事では、カプセル化の概念がざっくりどんな感じなのか知りたい方向けに例を交えて解説していきます!
「オブジェクト指向って???」という方はまずこちらの記事からご覧いただいた方が理解しやすいかと思います。
はじめに
「カプセル化」という単語のみでどのようなイメージが浮かびますか?
おそらく、「薬いれるやつ」「閉じ込める」「らくらく服用ゼリー」などを連想するかと思います。
この3つのイメージの中で、オブジェクト指向におけるカプセル化に一番近いのは「閉じ込める」に該当します。ゼリーではありません。
ざっくり解釈すると、あるプロパティやメソッドといった情報を閉じ込めることで、外部からアクセスできないようにし、保護する概念です。
抽象的な話ばかりしててもしょうがないので、実際にコードを交えてゆっくり見ていきましょう!
システムを作ろう
ここからは、ある設定を設けて話をすすめていきたいと思います。
今回、あなたは「毎日映画のタイトルを1つ出力するシステム」の一部を作ることになりました。
このシステムは次のように2分割することができます。
- 映画のタイトルをどこからか自動で取得する(スクレイピングなど)。
- 取得した映画のタイトルをあるフォーマットに沿って出力する。
あなたの担当は、2の出力の部分です。1.の取得に関しては別の人が担当するので、あなたは「映画のタイトルを受け取って出力するシステム」を作成します。
最終的な出力内容はこんな感じにしましょう。
XX月XX日、映画「[映画のタイトル]」絶賛上映中!
何番煎じかわからないような内容ですね。
タイトルを受け取る
まずは、別の人が作ってくれた「映画のタイトルを取得する関数」を簡易的にこのように定義します。
// 映画のタイトルを返す関数
function getMovieTitle() {
return '崖の上のポニョ';
}
この部分の具体的な内部ロジックは今回関係ないので、絶対に「崖の上のポニョ」が返ってくる関数にしました。
ではいよいよ自分でオブジェクトを作成し、このタイトルを受け取るようにしましょう!
// オブジェクト
var logMovieTitle = function() {
// 映画のタイトルを受け取る
this.title = getMovieTitle();
}
まずはlogMovieTitleというオブジェクトを作成。
そのプロパティとしてthis.titleを定義し、別の人が作成した関数「getMovieTitle」を使ってタイトルを受け取っています。
日付も取得
今回の最終的な出力の中に、「日付」が入っています。
となると、先ほど作成したオブジェクトに日付を表すプロパティを定義する必要があります。
// オブジェクト
var logMovieTitle = function(date) {
// 映画のタイトルを受け取る
this.title = getMovieTitle();
// 日付
this.date = date;
}
まずオブジェクトがdateを引数として受け取るようにし(2行目)、その値を用いてプロパティを定義しています。(6行目)
これで出力するまでの材料は揃いました!
出力するメソッドをつくる
最後に、取得したタイトルと日付をこねくりまわして、文章を出力するメソッドを作成しましょう!
// オブジェクト
var logMovieTitle = function(date) {
// 映画のタイトルを受け取る
this.title = getMovieTitle();
// 日付
this.date = date;
// 出力するメソッド
this.logMovie = function() {
let txt = this.date + '、映画「' + this.title + '」絶賛上映中!';
console.log(txt);
}
}
9行目にて、作成した文章を使って文章を出力するメソッド「this.logMovie」を作成しました。
では実際にインスタンス化し、出力してみましょう!
// インスタンス化
var movie = new logMovieTitle('12月25日');
movie.logMovie();
// 出力
12月25日、映画「崖の上のポニョ」絶賛上映中!
無事、クリスマスにポニョが上映されることが出力されました。(実際の上映期間とは異なると思います笑)
インスタンス化する際に日付「12月25日」を渡しています。
ここは話を簡略化するためにクリスマス固定にしていますが、その日の日付を取得するメソッドなどを使用すればよりbotっぽくなります。
区切りが良いので、ここまでのコードをまとめて記載いたします。
// 映画のタイトルを返す関数
function getMovieTitle() {
return '崖の上のポニョ';
}
// オブジェクト
var logMovieTitle = function(date) {
// 映画のタイトルを受け取る
this.title = getMovieTitle();
// 日付
this.date = date;
// 出力するメソッド
this.logMovie = function() {
let txt = this.date + '、映画「' + this.title + '」絶賛上映中!';
console.log(txt);
}
}
// インスタンス化
var movie = new logMovieTitle('12月25日');
movie.logMovie();
ここまではカプセル化の話はなく、シンプルにオブジェクトを作成しただけです。
カプセル化を理解する
前置きが長くなりましたが、ここまでで作成したコードを用いてカプセル化について学んでいきましょう!
外部からアクセスできてしまう
現在のシステムですと、保守的な意味で一つ心配があります。
それは外部からメソッド内のプロパティにアクセスできてしまう点です。
具体的に見てみましょう!
// オブジェクト
var logMovieTitle = function(date) {
// 映画のタイトルを受け取る
this.title = getMovieTitle();
// 日付
this.date = date;
// 出力するメソッド
this.logMovie = function() {
let txt = this.date + '、映画「' + this.title + '」絶賛上映中!';
console.log(txt);
}
}
// インスタンス化
var movie = new logMovieTitle('12月25日');
// プロパティ書き換え
movie.date = '1月1日';
// プロパティ
movie.logMovie();
// 出力
1日1日、映画「崖の上のポニョ」絶賛上映中!
18行目に「movie.date = ‘1月1日’」という一文を追加しました。
その結果、movieをインスタンス化させた時には「’12月25日’」を渡しているのにも関わらず、出力では「’1月1日’」になってしまっています。
今回はシンプルなシステムかつわざとらしく上書きしていますが、大規模なシステムの場合ではこのように上書きできてしまうせいでバグ(不整合)や意図せぬ挙動を招いてしまう可能性があります。
せっかく当日の映画のタイトルを取得するシステムを作ったのに、別日扱いで出力されてしまったらショックでディスプレイ叩き割ってジェンガを始めること間違いなしです。
したがってこのように中身にアクセスできないように書き換えてあげましょう!
外部からアクセスできないようにする
では実際にどのように記述するのか見てみましょう。
// オブジェクト
var logMovieTitle = function(date) {
// 映画のタイトルを受け取る
this.title = getMovieTitle();
// 日付
var date = date; // this. -> var に変更
// 出力するメソッド
this.logMovie = function() {
let txt = date + '、映画「' + this.title + '」絶賛上映中!';
console.log(txt);
}
}
// インスタンス化
var movie = new logMovieTitle('12月25日');
// プロパティ書き換え
movie.date = '1月1日';
// プロパティ
movie.logMovie();
// 出力
12月25日、映画「崖の上のポニョ」絶賛上映中!
注目していただきたいのは6行目です。
さきほどまでは「this.date」としていた箇所を「var date」に書き換えています。
その結果、18行目にて同様に書き換えを行なっているのにも関わらず、出力は「’12月25日’」になっています。
これは変数のスコープというのを利用しています。
変数のスコープ
変数には、その変数の中身を取り出せる(参照できる)範囲が決まっています。
その範囲の広さをもとに、大きく2つの種類に分けることができます。
- グローバルスコープ
-
ページ全体どこからでも参照できる。
- ローカルスコープ
-
ページ内のある局所的な範囲(ある関数内のみなど)で参照できる変数。
おそらく初めてこのような話を聞いた方でもなんとなく想像がつくかもしれませんが、実際に見てみましょう!
// グローバル変数
const globalVar = 'どこからでもアクセスできます.';
function Func_1() {
// ローカル変数
var localVar_1 = 5;
function Func_2() {
// ローカル変数
var localVar_2 = 199;
// 出力
console.log('Func_2: ' + localVar_1);
console.log('Func_2: ' + localVar_2);
console.log('Func_2: ' + globalVar);
}
Func_2();
// 出力
console.log('Func_1: ' + localVar_1);
console.log('Func_1: ' + localVar_2);
console.log('Func_2: ' + globalVar);
}
Func_1();
// 出力
Func_2: 5
Func_2: 199
Func_2: どこからでもアクセスできます.
Func_1: 5
// エラー
ReferenceError: Can't find variable: localVar_2
1つのグローバル変数と2つのローカル変数を定義し、それぞれをいろんな箇所で出力してみました。
その結果、22行目の内容を出力する部分でReferenceError(参照エラー)が発生しています。
すなわち「Func_2()内で定義した変数に、外部からアクセスできない」ことを意味します。
自身が所属する関数内において外側の変数にはアクセスできるが、
自身内に存在する関数の中で定義された変数にはアクセスできない。
この仕組みを用いて、先ほどのカプセル化を行いました。
まとめ
カプセル化といいましても様々な概念があり、今回は「外部から情報を隠蔽し保護する」ことに焦点をあててご紹介させていただきました。
厳密にはことなる箇所もありますが、どんなものなのかイメージのしやすさを優先しました。
少しでも理解のお役にたてたら幸いです。
ご覧いただきありがとうございました!
コメント