Go における情報隠蔽
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 50/107。
情報隠蔽は、単にフィールドを非公開(unexported)にするだけではありません。それは、外部のコードが必要な情報のみを知り、それ以上のことは知らなくて済むように型を設計することを意味します。
目的は、メソッドを通じて振る舞いを公開する一方で、内部構造を完全に隠蔽したままにすることです。次のアプローチを検討してください:
type Counter struct {
value int
maxLimit int
}
func NewCounter(limit int) *Counter {
return &Counter{maxLimit: limit}
}
func (c *Counter) Increment() bool {
if c.value >= c.maxLimit {
return false
}
c.value++
return true
}
func (c *Counter) Value() int {
return c.value
}
外部のコードは、Counter が内部で int を使用していることを知りません。それは float64 やスライス、あるいは全く別の何かである可能性もあります。メソッドが同じように動作する限り、Counter を使用するコードに影響を与えることなく実装を変更できます。
この原則はヘルパー関数にも適用されます。内部ロジックはエクスポートしないようにしましょう:
// エクスポート済み - 公開APIの一部
func (c *Counter) Reset() {
c.value = 0
}
// エクスポートされていない - 内部ヘルパー
func (c *Counter) isAtLimit() bool {
return c.value >= c.maxLimit
}
isAtLimit() を隠蔽することで、後で自由に変更したり削除したりできるようになります。情報の隠蔽は、パッケージが外部ユーザーに対して約束するものと、内部でどのように動作するかの間に明確な境界線を作ります。
チャレンジ
簡単情報の隠蔽(情報隠蔽)を実証する、安全なウォレットシステムを構築しましょう。内部の残高管理や取引履歴が外部のコードから完全に隠され、メソッドを通じて振る舞いのみが公開されるデジタルウォレットを作成します。
コードは2つのファイルに分けて構成します:
wallet.go: すべての内部状態を隠蔽するWallet構造体を作成します。ウォレットは内部で現在の残高と支出制限を追跡する必要がありますが、外部のコードはこれらがどのように保存または表現されているかを知ることはできません。以下のメソッドを通じて振る舞いを公開してください:NewWallet(limit float64) *Wallet- 指定された支出制限と残高ゼロでウォレットを作成するコンストラクタDeposit(amount float64) bool- ウォレットにお金を追加し、成功した場合はtrueを返す(金額は正である必要があります)Spend(amount float64) bool- 十分な残高があり、かつ金額が支出制限を超えない場合にのみお金を差し引き、成功した場合はtrueを返すBalance() float64- 現在の残高を返すStatus() string-Balance: $[balance] (Limit: $[limit])の形式でステータスメッセージを返す
main.go: 入力からウォレットの設定と取引を読み取り、ウォレットを作成し、操作を実行して、各アクションの後に結果を表示します。
以下の入力が提供されます:
- 1行目: 支出制限(Spending limit)
- 2行目: 入金額(Deposit amount)
- 3行目: 1回目の支出額(First spend amount)
- 4行目: 2回目の支出額(Second spend amount)
ウォレットを作成した後、その初期ステータスを出力してください。その後、各操作を実行し、更新されたステータス(成功した場合)または Transaction failed(操作が拒否された場合)のいずれかを出力します。すべてのドル金額は小数点以下2桁でフォーマットしてください。
例えば、50、100、30、80 が与えられた場合、出力は以下のようになります:
Balance: $0.00 (Limit: $50.00)
Balance: $100.00 (Limit: $50.00)
Balance: $70.00 (Limit: $50.00)
Transaction failed最後の取引は、十分な残高があるにもかかわらず、$80 が $50 の支出制限を超えているため失敗します。ウォレットは、どのように判断を下しているかを外部に公開することなく、内部でルールを強制します。外部のコードは単にメソッドを呼び出して結果を受け取るだけで、内部の実装詳細を知ることはありません。
チートシート
情報隠蔽とは、外部のコードが必要なことだけを知るように型を設計することを意味します。内部構造を完全に隠したまま、メソッドを通じて振る舞いを公開します。
実装が隠蔽された型の例:
type Counter struct {
value int
maxLimit int
}
func NewCounter(limit int) *Counter {
return &Counter{maxLimit: limit}
}
func (c *Counter) Increment() bool {
if c.value >= c.maxLimit {
return false
}
c.value++
return true
}
func (c *Counter) Value() int {
return c.value
}
外部のコードは内部表現(int、float64、あるいはその他の何であるか)を知りません。それを使用するコードに影響を与えることなく、実装を変更することができます。
ヘルパー関数は非公開(unexported)のままにします:
// Exported - 公開APIの一部
func (c *Counter) Reset() {
c.value = 0
}
// unexported - 内部ヘルパー
func (c *Counter) isAtLimit() bool {
return c.value >= c.maxLimit
}
情報隠蔽は、パッケージが外部に対して約束することと、内部でどのように動作するかの間に明確な境界線を作ります。
自分で試してみよう
package main
import (
"fmt"
)
func main() {
// 入力を読み込む
var limit float64
var depositAmount float64
var spend1 float64
var spend2 float64
fmt.Scanln(&limit)
fmt.Scanln(&depositAmount)
fmt.Scanln(&spend1)
fmt.Scanln(&spend2)
// TODO: 指定された支出制限で新しいウォレットを作成する
// TODO: 初期ステータスを表示する
// TODO: 入金を実行し、結果(ステータスまたは "Transaction failed")を表示する
// TODO: 1回目の支出を実行し、結果を表示する
// TODO: 2回目の支出を実行し、結果を表示する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー