Go における情報隠蔽
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 50/107。
情報隠蔽は、単にfieldsを非エクスポートにすることにとどまりません。外部コードが知る必要のあることだけを知るように、型を設計することです。それ以上は何も知る必要はありません。
メソッドを通じて振る舞いを公開しながら、内部構造を完全に隠すことが目標です。次のアプローチをConsiderしてください:
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を使用するコードに影響を与えずに実装を変更できます。
この原則は helper functions にも当てはまります。internal logic はエクスポートしないでください:
// エクスポート済み - 公開APIの一部
func (c *Counter) Reset() {
c.value = 0
}
// 未エクスポート - 内部ヘルパー
func (c *Counter) isAtLimit() bool {
return c.value >= c.maxLimit
}
isAtLimit()を隠蔽することで、後から自由に変更したり削除したりできます。情報隠蔽によって、パッケージが外部ユーザーに約束する内容と、内部での動作方法との間に明確な境界が作られます。
チャレンジ
簡単情報隠蔽を示す安全なウォレットシステムを構築しましょう。内部の残高追跡と取引履歴が外部コードから完全に隠され、メソッドを通じて動作だけが公開される digital ウォレットを作成します。
コードを2つのファイルに分けて整理します。
wallet.go: 内部状態をすべて隠すWallet構造体を Create します。ウォレットは current balance と支出上限を内部的に追跡しますが、外部コードはそれらがどのように保存または表現されているかを決して知ることができません。次のメソッドを通じて動作を公開します。NewWallet(limit float64) *Wallet- given spending limit とゼロの残高を持つウォレットを creates する constructorDeposit(amount float64) bool- ウォレットにお金を adds し、成功した場合(amount は正の値でなければなりません)にtrueを返しますSpend(amount float64) bool- sufficient balance exists かつ amount が支出上限を exceed しない場合にお金を取り除き、成功した場合はtrueを返しますBalance() float64- current balance を返しますStatus() string- 次の形式のステータスメッセージを返します:Balance: $[balance] (Limit: $[limit])
main.go: 入力からウォレットの設定と取引を読み取り、ウォレットを Create し、操作を実行して各 action 後に結果を表示します。
次の inputs が提供されます。
- 1行目: 支出上限
- 2行目: Deposit amount
- 3行目: 最初の支出額
- 4行目: 2回目の支出額
ウォレットを作成した後、initial status を表示します。その後、各操作を実行し、更新されたステータス(成功した場合)または Transaction failed(操作が拒否された場合)のいずれかを表示します。すべてのドル金額は小数点以下2桁で Format します。
たとえば、50、100、30、80 が given の場合、出力は次のようになります。
Balance: $0.00 (Limit: $50.00)
Balance: $100.00 (Limit: $50.00)
Balance: $70.00 (Limit: $50.00)
Transaction failed最後の取引は、残高が十分にあるにもかかわらず、$80 が $50 の支出上限を exceed するため failed になります。ウォレットは、これらの判断をどのように行うかを公開せずに、内部でその logic を適用します。外部コードは単にメソッドを呼び出して結果を受け取るだけで、内部の implementation details を知ることはありません。
自分で試してみよう
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: Perform deposit and print result (status or "Transaction failed")
// TODO: 最初の支出を実行し、結果を出力する
// TODO: 2回目の支出を実行し、結果を出力する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ