io.Reader ve io.Writer
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 73 / 107.
Go'nun standart kütüphanesi, iki temel arayüz aracılığıyla arayüz tabanlı tasarımı sergiler: io.Reader ve io.Writer. Bu basit arayüzler, dosya işlemlerinden ağ iletişimine kadar her şeye güç verir.
io.Reader arayüzü sadece bir metot gerektirir:
type Reader interface {
Read(p []byte) (n int, err error)
}Dilime len(p) bayta kadar veri okur ve kaç bayt okunduğunu döndürür. Benzer şekilde, io.Writer şunu tanımlar:
type Writer interface {
Write(p []byte) (n int, err error)
}Bu metotları uygulayan herhangi bir tür, tüm I/O ekosistemiyle çalışır.
İşte io.Reader arayüzünü uygulayan özel bir tür:
type RepeatReader struct {
Char byte
Count int
pos int
}
func (r *RepeatReader) Read(p []byte) (int, error) {
if r.pos >= r.Count {
return 0, io.EOF
}
n := 0
for n < len(p) && r.pos < r.Count {
p[n] = r.Char
n++
r.pos++
}
return n, nil
}Artık bu özel okuyucu, io.Reader bekleyen herhangi bir fonksiyonla çalışır:
reader := &RepeatReader{Char: 'A', Count: 5}
data, _ := io.ReadAll(reader)
fmt.Println(string(data)) // AAAAABu tasarım, io.Reader veya io.Writer kabul eden fonksiyonlar yazmanıza olanak tanıyarak; bunların dosyalar, ağ bağlantıları, tamponlar veya herhangi bir özel uygulama ile çalışmasını sağlar.
Görev
Kolayio.Reader ve io.Writer arayüzlerini uygulayan özel bir metin akışı işlemcisi oluşturalım! Metin içinden akarken onu dönüştürebilen türler oluşturacak ve bu temel arayüzlerin güçlü G/Ç (I/O) kompozisyonunu nasıl mümkün kıldığını göstereceksiniz.
Kodunuzu iki dosya halinde düzenleyeceksiniz:
streams.go: Özel okuyucu (reader) ve yazıcı (writer) türlerinizi tanımlayın.Bir dizeyi (string) saran ve kaç baytın okunduğunu takip eden bir
CountingReaderyapısı (struct) oluşturun. Kaynak dize ve bir konum izleyici için alanları olmalıdır. Kaynaktan gelen baytları sağlanan dilime (slice) okuyan, konumu güncelleyen ve kaynak tükendiğindeio.EOFdöndürenRead(p []byte) (int, error)metodunu uygulayın.Yazılan verileri toplayan ve bunları büyük harfe dönüştüren bir
UppercaseWriteryapısı oluşturun. Biriken sonucu dahili olarak saklamalıdır. Gelen baytları büyük harfe dönüştüren ve saklayanWrite(p []byte) (int, error)metodunu uygulayın. Biriken büyük harfli metni almak için birResult() stringmetodu ekleyin.Türlerinizi düzgün bir şekilde başlatmak için
NewCountingReader(source string) *CountingReaderveNewUppercaseWriter() *UppercaseWriteryapıcı (constructor) fonksiyonlarını oluşturun.main.go: Standart kütüphane ile çalışan özel G/Ç türlerinizi gösterin.Bir dize girişi okuyun, ardından tüm verileri okumak için
io.ReadAllile birlikteCountingReaderyapınızı kullanın. Bu veriyiWritemetodunu kullanarakUppercaseWriteryapınızdan geçirin.Sonuçları şu formatta yazdırın:
Original: [input] Uppercase: [result from writer] Bytes read: [total bytes]
Aşağıdaki girdi sağlanacaktır:
- Tek bir satır metin
Örneğin, şu verildiğinde:
Hello WorldÇıktınız şöyle olmalıdır:
Original: Hello World
Uppercase: HELLO WORLD
Bytes read: 11Ve şu verildiğinde:
Go interfaces are powerfulÇıktınız şöyle olmalıdır:
Original: Go interfaces are powerful
Uppercase: GO INTERFACES ARE POWERFUL
Bytes read: 26Buradaki temel nokta, CountingReader yapınızın io.Reader arayüzünü uyguladığı için io.ReadAll ile sorunsuz çalışmasıdır. Go ekosisteminde io.Reader kabul eden herhangi bir fonksiyon, özel türünüzle çalışacaktır; arayüz tabanlı tasarımın gücü budur.
Kopya kağıdı
Go'nun io.Reader ve io.Writer arayüzleri, G/Ç (I/O) işlemleri için temeldir:
type Reader interface {
Read(p []byte) (n int, err error)
}type Writer interface {
Write(p []byte) (n int, err error)
}Read metodu, dilim (slice) içine len(p) bayta kadar okuma yapar ve okunan bayt sayısını döndürür. Write metodu, dilimdeki baytları yazar ve yazılan bayt sayısını döndürür.
Bu metodları uygulayan herhangi bir tür, Go'nun tüm G/Ç ekosistemiyle çalışabilir. Örnek özel okuyucu:
type RepeatReader struct {
Char byte
Count int
pos int
}
func (r *RepeatReader) Read(p []byte) (int, error) {
if r.pos >= r.Count {
return 0, io.EOF
}
n := 0
for n < len(p) && r.pos < r.Count {
p[n] = r.Char
n++
r.pos++
}
return n, nil
}Özel okuyucunun standart kütüphane fonksiyonlarıyla kullanımı:
reader := &RepeatReader{Char: 'A', Count: 5}
data, _ := io.ReadAll(reader)
fmt.Println(string(data)) // AAAAABu tasarım, io.Reader veya io.Writer kabul eden fonksiyonların dosyalar, ağ bağlantıları, arabellekler veya herhangi bir özel uygulama ile çalışmasına olanak tanır.
Kendin dene
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// Girdiyi oku
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
// Varsa sondaki yeni satır karakterini kaldır
if len(input) > 0 && input[len(input)-1] == '\n' {
input = input[:len(input)-1]
}
// TODO: Giriş dizesiyle bir CountingReader oluşturun
// countingReader := NewCountingReader(input)
// TODO: CountingReader'ınızdaki tüm verileri okumak için io.ReadAll kullanın
// data, err := io.ReadAll(countingReader)
// TODO: Bir UppercaseWriter oluşturun
// upperWriter := NewUppercaseWriter()
// TODO: Verileri UppercaseWriter'ınıza yazın
// upperWriter.Write(data)
// TODO: Sonuçları istenen formatta yazdırın:
// Original: [input]
// Uppercase: [result from writer]
// Bytes read: [total bytes]
// Kullanılmayan içe aktarma hatasını önlemek için yer tutucu - uygularken kaldırın
_ = io.EOF
fmt.Println("Original:", input)
// fmt.Println("Uppercase:", upperWriter.Result())
// fmt.Println("Bytes read:", countingReader.Position)
}
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ı11Standart Kütüphane ve OOP
io.Reader ve io.Writersort.Interface Arayüzüfmt.Stringer ArayüzüStruct'lar ile encoding/jsonhttp.Handler ArayüzüÖzet - REST API Modelleri3İş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