Menu
Coddy logo textTech

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()を隠蔽することで、後から自由に変更したり削除したりできます。情報隠蔽によって、パッケージが外部ユーザーに約束する内容と、内部での動作方法との間に明確な境界が作られます。

challenge icon

チャレンジ

簡単

情報隠蔽を示す安全なウォレットシステムを構築しましょう。内部の残高追跡と取引履歴が外部コードから完全に隠され、メソッドを通じて動作だけが公開される digital ウォレットを作成します。

コードを2つのファイルに分けて整理します。

  • wallet.go: 内部状態をすべて隠す Wallet 構造体を Create します。ウォレットは current balance と支出上限を内部的に追跡しますが、外部コードはそれらがどのように保存または表現されているかを決して知ることができません。次のメソッドを通じて動作を公開します。
    • NewWallet(limit float64) *Wallet - given spending limit とゼロの残高を持つウォレットを creates する constructor
    • Deposit(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])
    取引が有効かどうかの確認などの helper logic は、unexported functions またはメソッドとして保持します。これらは、後で変更される可能性がある internal implementation details です。
  • main.go: 入力からウォレットの設定と取引を読み取り、ウォレットを Create し、操作を実行して各 action 後に結果を表示します。

次の inputs が提供されます。

  • 1行目: 支出上限
  • 2行目: Deposit amount
  • 3行目: 最初の支出額
  • 4行目: 2回目の支出額

ウォレットを作成した後、initial status を表示します。その後、各操作を実行し、更新されたステータス(成功した場合)または Transaction failed(操作が拒否された場合)のいずれかを表示します。すべてのドル金額は小数点以下2桁で Format します。

たとえば、501003080 が 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回目の支出を実行し、結果を出力する
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Goオンラインコンパイラ