情報隠蔽
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 36/104。
情報隠蔽は、単にアクセス修飾子を使用することにとどまりません。これは、データだけでなく、クラスが内部でどのように動作するかという実装の詳細も隠蔽するという設計原則です。
クラスのユーザーが知る必要のあることだけを公開し、それ以外はすべて隠しておくことが目標です。これにより、クラスを使用するコードに影響を与えることなく、内部の実装を変更できるようになります。
class Temperature {
double kelvin; // 内部表現 - 隠された詳細
public:
void setCelsius(double c) {
kelvin = c + 273.15; // ユーザーから隠された変換
}
double getCelsius() const {
return kelvin - 273.15;
}
double getFahrenheit() const {
return (kelvin - 273.15) * 9.0/5.0 + 32;
}
};このクラスのユーザーは、温度が内部的にケルビンで保存されていることを知りません(あるいは知る必要もありません)。後で代わりに摂氏で保存することに決めた場合でも、プライベートな実装を変更するだけで済み、パブリックインターフェースは変更されません。
インターフェースと実装をこのように分離することで、いくつかの利点が得られます。クラスの利用者にとっての複雑さを軽減し、内部データの予期せぬ誤用を防ぎ、コードの保守を容易にします。内部の詳細が隠蔽されていれば、そのクラスに依存する既存のコードを壊すことなく、自由に最適化やリファクタリングを行うことができます。
チャレンジ
簡単距離を内部的にセンチメートルで保存しながら、異なる単位を扱うためのクリーンなインターフェースを提供することで、情報の隠蔽を実証する Distance クラスを作成しましょう。このクラスの利用者は、距離が内部でどのように保存されているかを知る必要(あるいは気にする必要)はありません。彼らにとって便利な単位で作業するだけです。
コードを整理するために、2つのファイルを作成します。
Distance.h: 内部表現を隠しながら、柔軟なパブリックインターフェースを公開するDistanceクラスを定義します。内部的には、距離を センチメートル(double型)で保存します。これが隠された実装の詳細です。パブリックインターフェースでは、利用者が以下の操作を行えるようにする必要があります。setMeters(double m)、setCentimeters(double cm)、またはsetKilometers(double km)を使用して距離を設定するgetMeters()、getCentimeters()、またはgetKilometers()を使用して距離を取得する(これらはすべて const メソッドである必要があります)add(const Distance& other)を使用して2つの距離を加算し、新しいDistanceオブジェクトを返す
距離をゼロに初期化するデフォルトコンストラクタを含めてください。変換ロジックはメソッドの内部に隠蔽されるべきです。利用者は内部の保存形式を気にすることなく、単に
setKilometers(5)やgetMeters()を呼び出します。main.cpp: 利用者が内部表現を知らなくてもクラスを操作できることを実証します。入力から2つの距離値を読み取ります。1つ目はメートル単位、2つ目はキロメートル単位です。その後、以下の手順を実行します。Distanceオブジェクトを作成し、メートル値を使用して設定する"In meters: <value> m"と出力する"In centimeters: <value> cm"と出力する"In kilometers: <value> km"と出力する- 2つ目の
Distanceオブジェクトを作成し、キロメートル値を使用して設定する - 2つの距離を加算し、その結果を保存する
"Combined in meters: <value> m"と出力する
情報の隠蔽の素晴らしさは、後で内部の保存形式をセンチメートルからミリメートル(あるいは他のもの)に変更しても、利用者がクラスとやり取りする方法を変える必要がないことです。実装が変わっても、パブリックインターフェースは安定したまま保たれます。
すべての出力値は、<iomanip> の std::fixed と std::setprecision(2) を使用して、小数点以下2桁でフォーマットしてください。入力文字列は std::stod() を使用して double 型に変換してください。
変換の参考: 1 メートル = 100 センチメートル、1 キロメートル = 1000 メートル。
チートシート
情報隠蔽(Information hiding)とは、クラスの内部動作に関する実装の詳細を隠し、ユーザーが知る必要のある情報のみを公開するという設計原則です。
これにより、クラスを使用しているコードに影響を与えることなく、内部の実装を変更できるようになります。
class Temperature {
double kelvin; // 内部表現 - 隠された詳細
public:
void setCelsius(double c) {
kelvin = c + 273.15; // 変換処理はユーザーから隠されている
}
double getCelsius() const {
return kelvin - 273.15;
}
double getFahrenheit() const {
return (kelvin - 273.15) * 9.0/5.0 + 32;
}
};ユーザーは、温度が内部でケルビン(Kelvin)として保存されていることを知る必要はありません(また、知ることもできません)。後で保存形式を変更したとしても、プライベートな実装が変わるだけで、パブリックなインターフェースは変わらないままです。
情報隠蔽のメリット:
- クラスの利用者のための複雑さを軽減する
- 内部データの誤用を防止する
- コードの保守やリファクタリングを容易にする
- 既存のコードを壊さずに最適化を行える
自分で試してみよう
#include <iostream>
#include <string>
#include <iomanip>
#include "Distance.h"
using namespace std;
int main() {
// 入力値を読み込む
string metersInput, kmInput;
cin >> metersInput; // 最初の値(メートル単位)
cin >> kmInput; // 2番目の値(キロメートル単位)
// std::stod() を使用して文字列を double 型に変換する
double metersValue = stod(metersInput);
double kmValue = stod(kmInput);
// 出力のフォーマットを設定する
cout << fixed << setprecision(2);
// TODO: 最初の Distance オブジェクトを作成し、メートルの値を使用して設定する
// TODO: "In meters: <value> m" と出力する
// TODO: "In centimeters: <value> cm" と出力する
// TODO: "In kilometers: <value> km" と出力する
// TODO: 2番目の Distance オブジェクトを作成し、キロメートルの値を使用して設定する
// TODO: add() メソッドを使用して2つの距離を加算する
// TODO: "Combined in meters: <value> m" と出力する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス