データ隠蔽の実装
CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 33/70。
データ隠蔽は、単に private フィールドを使用することにとどまりません。それは、内部の実装の詳細が外部のコードから完全に隠され、クリーンなパブリックインターフェースを通じて必要なものだけを公開するようにクラスを設計することです。
ユーザーのパスワードを管理するクラスを考えてみましょう。生のパスワードには、読み取りであっても、誰にもアクセスさせたくありません。
public class UserAccount
{
private string passwordHash;
public void SetPassword(string password)
{
passwordHash = HashPassword(password);
}
public bool VerifyPassword(string password)
{
return HashPassword(password) == passwordHash;
}
private string HashPassword(string password)
{
return password.GetHashCode().ToString();
}
}パスワードや、そのハッシュ値さえも取得する方法がないことに注目してください。外部のコードはパスワードの設定または検証のみが可能であり、内部の保存メカニズムは完全に隠蔽されています。HashPassword メソッドも private です。なぜなら、これはクラスの使用方法に影響を与えることなく変更される可能性がある実装の詳細だからです。
このアプローチは、クラスを誤用から保護し、後で内部構造を変更する自由を与えます。別のハッシュアルゴリズムを使用することに決めたとしても、パスワードがどのように保存されているかに依存しているものはないため、外部のコードが壊れることはありません。
チャレンジ
簡単真のデータ隠蔽を実証する、安全なウォレットシステムを構築しましょう。残高が直接アクセスから完全に保護されたデジタルウォレットを作成します。外部コードは、入金、出金、および取引が可能かどうかの確認のみを行うことができ、実際の残高を直接見たり操作したりすることはできません。
コードは2つのファイルに分けて構成します:
Wallet.cs:Finance名前空間内に、残高を完全に隠蔽するWalletクラスを作成します。ウォレットには以下の要素を含める必要があります:- 残高を保存するためのプライベートフィールド (decimal)
- 残高を
"$123.45"のような通貨文字列としてフォーマットして返すプライベートメソッドFormatBalance()。これは外部に公開すべきではない内部ヘルパーです。 - 金額が正の場合のみ残高に追加し、成功した場合は
true、それ以外の場合はfalseを返すパブリックメソッドDeposit(decimal amount) - 金額が正であり、かつ現在の残高を超えない場合のみ残高から差し引き、成功した場合は
true、それ以外の場合はfalseを返すパブリックメソッドWithdraw(decimal amount) - 残高が指定された金額以上であれば
true、それ以外であればfalseを返すパブリックメソッドCanAfford(decimal amount) - 内部でプライベートな
FormatBalance()メソッドを使用し、"Current balance: {formatted balance}"を返すパブリックメソッドGetStatement()
Program.cs: メインファイルでWalletインスタンスを作成し、入力値を使用して一連の取引を実行します。パブリックインターフェースを通じて有用な機能を提供しながら、ウォレットがいかに内部状態を保護しているかを示してください。
3つの入力を受け取ります:
- 初期入金額 (decimal)
- 試行する出金額 (decimal)
- 購入可能か確認する金額 (decimal)
出力は以下の形式で表示してください:
Deposit {amount}: {True/False}
{GetStatement() result}
Withdraw {amount}: {True/False}
{GetStatement() result}
Can afford {amount}: {True/False}例えば、入力が 100.50、30.25、80.00 の場合、出力は以下のようになります:
Deposit 100.50: True
Current balance: $100.50
Withdraw 30.25: True
Current balance: $70.25
Can afford 80.00: Falseここでの重要なポイントは、外部コードが balance フィールドに直接触れることが決してないということです。フォーマットされた残高文字列でさえ、内部でプライベートヘルパーを使用するパブリックメソッドを介して提供されます。これがデータ隠蔽です。実装の詳細は完全に不可視であり、ウォレットを使用するコードを壊すことなく、残高の保存方法やフォーマット方法を自由に変更できます。
チートシート
データ隠蔽とは、内部の実装の詳細が外部のコードから完全に見えないようにクラスを設計し、クリーンなパブリックインターフェースを通じて必要なものだけを公開することを意味します。
機密データを保存するには private フィールドを使用し、public メソッドを通じて制御されたアクセスを提供します:
public class UserAccount
{
private string passwordHash;
public void SetPassword(string password)
{
passwordHash = HashPassword(password);
}
public bool VerifyPassword(string password)
{
return HashPassword(password) == passwordHash;
}
private string HashPassword(string password)
{
return password.GetHashCode().ToString();
}
}主な原則:
- 内部データへの直接アクセスを禁止する(機密フィールドのゲッターを設けない)
- 実装の詳細のためのプライベートなヘルパーメソッド
- パブリックメソッドは制御された操作のみを提供する
- 内部の保存メカニズムは完全に隠蔽される
これにより、クラスが誤用から保護され、外部のコードを壊すことなく内部の実装を変更できるようになります。
自分で試してみよう
using System;
using Finance;
class Program
{
public static void Main(string[] args)
{
// 入力値を読み込む
decimal depositAmount = Convert.ToDecimal(Console.ReadLine());
decimal withdrawAmount = Convert.ToDecimal(Console.ReadLine());
decimal checkAmount = Convert.ToDecimal(Console.ReadLine());
// TODO: Wallet インスタンスを作成する
// TODO: 入金を実行し、結果を次の形式で出力する: "Deposit {amount}: {True/False}"
// TODO: GetStatement() を使用して明細を出力する
// TODO: 出金を実行し、結果を次の形式で出力する: "Withdraw {amount}: {True/False}"
// TODO: GetStatement() を使用して明細を出力する
// TODO: 支払い能力を確認し、結果を次の形式で出力する: "Can afford {amount}: {True/False}"
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。