Menu
Coddy logo textTech

Ukrywanie informacji w Go

Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 50 z 107.

Ukrywanie informacji to coś więcej niż tylko oznaczanie pól jako nieeksportowane. Chodzi o projektowanie typów w taki sposób, aby kod zewnętrzny wiedział tylko tyle, ile musi. Nic więcej.

Celem jest udostępnienie zachowania za pomocą metod przy jednoczesnym całkowitym ukryciu wewnętrznej struktury. Rozważ takie podejście:

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
}

Zewnętrzny kod nie wie, że Counter wewnętrznie używa int. Może to być float64, wycinek lub coś zupełnie innego. Implementację można zmienić bez wpływu na jakikolwiek kod korzystający z Counter, o ile metody zachowują się tak samo.

Ta zasada dotyczy również funkcji pomocniczych. Nie eksportuj logiki wewnętrznej:

// Eksportowana - część publicznego API
func (c *Counter) Reset() {
    c.value = 0
}

// nieeksportowana - wewnętrzna funkcja pomocnicza
func (c *Counter) isAtLimit() bool {
    return c.value >= c.maxLimit
}

Ukrywając isAtLimit(), możesz później swobodnie ją zmienić lub usunąć. Ukrywanie informacji tworzy wyraźną granicę między tym, co Twój pakiet obiecuje użytkownikom zewnętrznym, a tym, jak działa wewnętrznie.

challenge icon

Wyzwanie

Łatwy

Zbudujmy bezpieczny system portfela, który demonstruje ukrywanie informacji. Utworzysz portfel cyfrowy, w którym wewnętrzne śledzenie salda i historia transakcji są całkowicie ukryte przed zewnętrznym kodem: udostępniane jest wyłącznie zachowanie poprzez metody.

Rozmieścisz kod w dwóch plikach:

  • wallet.go: Utwórz strukturę Wallet, która ukrywa cały swój wewnętrzny stan. Portfel powinien wewnętrznie śledzić bieżące saldo i limit wydatków, ale zewnętrzny kod nie powinien wiedzieć, jak są one przechowywane ani reprezentowane. Udostępnij zachowanie za pomocą tych metod:
    • NewWallet(limit float64) *Wallet — konstruktor, który tworzy portfel z podanym limitem wydatków i zerowym saldem
    • Deposit(amount float64) bool — dodaje pieniądze do portfela, zwraca true, jeśli operacja się powiedzie (kwota musi być dodatnia)
    • Spend(amount float64) bool — odejmuje pieniądze, jeśli saldo jest wystarczające ORAZ kwota nie przekracza limitu wydatków; zwraca true, jeśli operacja się powiedzie
    • Balance() float64 — zwraca bieżące saldo
    • Status() string — zwraca komunikat o stanie w formacie: Balance: $[balance] (Limit: $[limit])
    Całą logikę pomocniczą (na przykład sprawdzanie, czy transakcja jest prawidłowa) umieść w nieeksportowanych funkcjach lub metodach. Są to wewnętrzne szczegóły implementacji, które mogą się później zmienić.
  • main.go: Odczytaj konfigurację portfela i transakcje z wejścia, utwórz portfel, wykonaj operacje i wyświetl wyniki po każdej z nich.

Dane wejściowe będą następujące:

  • Wiersz 1: Limit wydatków
  • Wiersz 2: Kwota wpłaty
  • Wiersz 3: Kwota pierwszej wypłaty
  • Wiersz 4: Kwota drugiej wypłaty

Po utworzeniu portfela wypisz jego początkowy stan. Następnie wykonaj każdą operację i wypisz zaktualizowany stan (jeśli operacja się powiedzie) albo Transaction failed (jeśli operacja zostanie odrzucona). Wszystkie kwoty w dolarach formatuj z dokładnością do dwóch miejsc po przecinku.

Na przykład dla danych 50, 100, 30 i 80 wynik powinien wyglądać tak:

Balance: $0.00 (Limit: $50.00)
Balance: $100.00 (Limit: $50.00)
Balance: $70.00 (Limit: $50.00)
Transaction failed

Ostatnia transakcja nie powiedzie się, ponieważ kwota $80 przekracza limit wydatków wynoszący $50, mimo że saldo jest wystarczające. Portfel wewnętrznie egzekwuje swoje zasady, nie ujawniając, w jaki sposób podejmuje te decyzje. Zewnętrzny kod po prostu wywołuje metody i otrzymuje wyniki, nie poznając wewnętrznych szczegółów implementacji.

Spróbuj swoich sił

package main

import (
	"fmt"
)

func main() {
	// Wczytaj dane wejściowe
	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: Utwórz nowy portfel z podanym limitem wydatków

	// TODO: Wyświetl stan początkowy

	// TODO: Wpłać środki i wyświetl wynik (stan lub "Transaction failed")

	// TODO: Wykonaj pierwszy wydatek i wyświetl wynik

	// TODO: Wykonaj drugi wydatek i wyświetl wynik
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Go online