Go'da Bilgi Gizleme
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 50 / 107.
Bilgi gizleme, alanları sadece dışa aktarılmamış (unexported) yapmanın ötesine geçer. Bu, türlerinizi harici kodun sadece bilmesi gerekeni bileceği —daha fazlasını değil— şekilde tasarlamakla ilgilidir.
Amaç, iç yapıyı tamamen gizli tutarken metotlar aracılığıyla davranışı ortaya çıkarmaktır. Şu yaklaşımı düşünün:
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
}
Dış kod, Counter'ın dahili olarak bir int kullandığını bilmez. Bir float64, bir slice veya tamamen başka bir şey olabilir. Metotlar aynı şekilde davrandığı sürece, uygulama Counter'ı kullanan hiçbir kodu etkilemeden değişebilir.
Bu ilke yardımcı fonksiyonlar için de geçerlidir. Dahili mantığı dışa aktarılmamış (unexported) olarak tutun:
// Dışa aktarıldı - genel API'nin bir parçası
func (c *Counter) Reset() {
c.value = 0
}
// dışa aktarılmadı - dahili yardımcı
func (c *Counter) isAtLimit() bool {
return c.value >= c.maxLimit
}
isAtLimit() fonksiyonunu gizleyerek, onu daha sonra değiştirmekte veya kaldırmakta özgür kalırsınız. Bilgi gizleme, paketinizin dış kullanıcılara vaat ettikleri ile dahili olarak nasıl çalıştığı arasında net bir sınır oluşturur.
Görev
KolayBilgi gizlemeyi (information hiding) gösteren güvenli bir cüzdan sistemi oluşturalım. Dahili bakiye takibinin ve işlem geçmişinin harici koddan tamamen gizlendiği, yalnızca davranışların metotlar aracılığıyla dışarıya açıldığı dijital bir cüzdan oluşturacaksınız.
Kodunuzu iki dosya halinde düzenleyeceksiniz:
wallet.go: Tüm dahili durumunu gizleyen birWalletyapısı (struct) oluşturun. Cüzdan, mevcut bakiyeyi ve harcama limitini dahili olarak takip etmelidir, ancak harici kod bunların nasıl saklandığını veya temsil edildiğini asla bilmemelidir. Davranışları şu metotlar aracılığıyla dışarıya açın:NewWallet(limit float64) *Wallet- verilen harcama limiti ve sıfır bakiye ile bir cüzdan oluşturan yapıcı (constructor) fonksiyonDeposit(amount float64) bool- cüzdana para ekler, başarılıysa (miktar pozitif olmalıdır)truedönerSpend(amount float64) bool- yeterli bakiye varsa VE miktar harcama limitini aşmıyorsa parayı çeker, başarılıysatruedönerBalance() float64- mevcut bakiyeyi dönerStatus() string- şu formatta bir durum mesajı döner:Balance: $[balance] (Limit: $[limit])
main.go: Girdiden cüzdan yapılandırmasını ve işlemleri okuyun, bir cüzdan oluşturun, işlemleri gerçekleştirin ve her eylemden sonra sonuçları görüntüleyin.
Aşağıdaki girdiler sağlanacaktır:
- Satır 1: Harcama limiti
- Satır 2: Yatırılacak miktar
- Satır 3: İlk harcama miktarı
- Satır 4: İkinci harcama miktarı
Cüzdanı oluşturduktan sonra başlangıç durumunu yazdırın. Ardından her işlemi gerçekleştirin ve güncellenmiş durumu (başarılıysa) veya Transaction failed (işlem reddedildiyse) mesajını yazdırın. Tüm dolar miktarlarını iki ondalık basamakla formatlayın.
Örneğin, 50, 100, 30 ve 80 verildiğinde çıktınız şöyle olmalıdır:
Balance: $0.00 (Limit: $50.00)
Balance: $100.00 (Limit: $50.00)
Balance: $70.00 (Limit: $50.00)
Transaction failedSon işlem başarısız olur çünkü yeterli bakiye olmasına rağmen $80, $50 olan harcama limitini aşmaktadır. Cüzdan, bu kararları nasıl verdiğini ifşa etmeden kurallarını dahili olarak uygular. Harici kod sadece metotları çağırır ve sonuçları alır, dahili uygulama detaylarını asla bilmez.
Kopya kağıdı
Bilgi gizleme (information hiding), türleri harici kodun yalnızca bilmesi gerekenleri bileceği şekilde tasarlamak anlamına gelir. İç yapıyı tamamen gizli tutarken, metotlar aracılığıyla davranışı dışarı açın.
Gizli uygulamaya (implementation) sahip bir tür örneği:
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
}
Harici kod iç temsili bilmez (bu int, float64 veya başka bir şey olabilir). Uygulama, onu kullanan kodu etkilemeden değişebilir.
Yardımcı fonksiyonları dışa aktarılmamış (unexported) tutun:
// Dışa aktarılmış - genel API'nin parçası
func (c *Counter) Reset() {
c.value = 0
}
// dışa aktarılmamış - dahili yardımcı
func (c *Counter) isAtLimit() bool {
return c.value >= c.maxLimit
}
Bilgi gizleme, paketinizin dışarıya vaat ettikleri ile dahili olarak nasıl çalıştığı arasında net bir sınır oluşturur.
Kendin dene
package main
import (
"fmt"
)
func main() {
// Girdileri oku
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: Verilen harcama limiti ile yeni bir cüzdan oluşturun
// TODO: Başlangıç durumunu yazdırın
// TODO: Para yatırma işlemini gerçekleştirin ve sonucu yazdırın (durum veya "Transaction failed")
// TODO: İlk harcamayı gerçekleştirin ve sonucu yazdırın
// TODO: İkinci harcamayı gerçekleştirin ve sonucu yazdırın
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1Go OOP Temelleri
Harici DosyalarGo Çalışma Alanı ve ModüllerPaketler ve İçe AktarmalarDışa Aktarılan ve Aktarılmayan İsimlerGo'da OOP'ye GirişSınıf Olarak Struct'larStruct'larda Metot TanımlamaPointer ve Değer AlıcılarStruct BaşlatmaYapıcı FonksiyonlarÖzet - Basit Hesap Makinesi4Arayüzler
Arayüzlere GirişÖrtük UygulamaSözleşme Olarak ArayüzBoş Arayüz (any)Tür OnaylamaTür SeçimiArayüz BileşimiStringer ve Error ArayüzleriÖzet - Şekil Hesaplayıcı7Kapsülleme
Dışa Aktarılan ve Aktarılmayan AlanlarPaket Seviyesinde KapsüllemeGetter ve Setter MetotlarıGo'da Bilgi GizlemeÖzet - Öğrenci Kayıtları10Generics (Go 1.18+)
Generics'e GirişTür ParametreleriTür KısıtlamalarıGeneric Struct'larGeneric Metotlar İçin Geçici ÇözümÖzet - Generic Koleksiyon13Tasarım Kalıpları Bölüm 1
Tasarım Kalıplarına GirişSingleton KalıbıFactory KalıbıAbstract Factory KalıbıObserver KalıbıStrategy Kalıbı2Türler ve Struct'lar: Derinlemesine İnceleme
Temel ve Bileşik TürlerÖzel Tür TanımlamalarıStruct EtiketleriAnonim Struct'larİç İçe Geçmiş Struct'larSıfır Değerler ve VarsayılanlarÖzet - Rehber5Kalıtım Yerine Kompozisyon
Go'da Neden Kalıtım YokStruct Gömme TemelleriMetot YükseltmeBirden Fazla Struct GömmeGömme vs AgregasyonGömülü Metotları GölgelemeÖzet - Çalışan Hiyerarşisi8Hata Yönetimi ve OOP
error ArayüzüÖzel Hata TürleriHata Sarmalama (fmt.Errorf)Sentinel Hatalarerrors.Is() ve errors.As()Panic, Defer ve RecoverÖzet - Dosya Ayrıştırıcı3İşaretçiler ve Bellek
Go'da İşaretçi TemelleriStruct İşaretçileriDeğer ile Geçme ve Referans ile Geçmenew() FonksiyonuGo'da Garbage CollectionÖzet - Linked List Oluşturucu6Go'da Polimorfizm
Interface'ler ile PolimorfizmGo'da Duck TypingInterface Karşılama KurallarıPolimorfik KoleksiyonlarDependency InjectionÖzet - Payment Processor9Eşzamanlılık ve OOP
Goroutine TemelleriKanallar ve İletişimTamponlu ve Tamponsuz KanallarSelect İfadesisync.Mutex ve sync.RWMutexsync.WaitGroupThread-Safe Struct TasarımıÖzet - Worker Pool