Menu
Coddy logo textTech

Sözleşme Olarak Arayüz

Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 27 / 107.

Arayüzleri (interfaces) sözleşmeler olarak düşünmek, daha iyi Go programları tasarlamanıza yardımcı olur. Bir fonksiyon bir arayüz türünü kabul ettiğinde şunu demiş olur: "Bu sözleşmeyi yerine getirdiği sürece, bana hangi somut türü (concrete type) verdiğin umurumda değil."

Bu sözleşme tabanlı düşünme biçimi, belirli türlerden ziyade davranışa odaklanan fonksiyonlar yazmanıza olanak tanır. Çeşitli kaynaklardan gelen verileri işlemesi gereken bir fonksiyonu ele alalım:

type DataSource interface {
    FetchData() string
}

func ProcessData(src DataSource) {
    data := src.FetchData()
    fmt.Println("Processing:", data)
}

ProcessData fonksiyonu, bir veritabanı bağlantısı, bir API istemcisi veya bir dosya okuyucu alıp almadığını bilmez veya umursamaz. Sadece girdinin FetchData() yapabilmesini gerektirir. Bu sözleşmeye uyan her tür çalışır.

type Database struct{ ConnectionString string }
func (d Database) FetchData() string { return "data from DB" }

type APIClient struct{ Endpoint string }
func (a APIClient) FetchData() string { return "data from API" }

func main() {
    db := Database{ConnectionString: "localhost"}
    api := APIClient{Endpoint: "https://api.example.com"}
    
    ProcessData(db)   // Çalışıyor!
    ProcessData(api)  // Bu da çalışıyor!
}

Bu sözleşme yaklaşımı kodunuzu daha test edilebilir hale de getirir. Test sırasında, gerçek veritabanlarına veya ağ bağlantılarına ihtiyaç duymadan arayüzü (interface) karşılayan bir taklit (mock) tür oluşturabilirsiniz. Test edilen fonksiyon aradaki farkı anlamaz çünkü sözleşme yerine getirilmiştir.

challenge icon

Görev

Kolay

Arayüzlerin (interfaces) nasıl sözleşme olarak hareket ettiğini gösteren bir bildirim sistemi kuralım. Bir bildirimin gönderilmesinin ne anlama geldiğini tanımlayan bir Notifier arayüzü oluşturacak ve ardından bu sözleşmeye uyan birden fazla bildirim kanalı uygulayacaksınız.

Kodunuzu iki dosya halinde düzenleyeceksiniz:

  • notifiers.go: Tek bir Notify(message string) string metoduna sahip bir Notifier arayüzü tanımlayın. Ardından bu sözleşmeyi karşılayan üç farklı struct oluşturun:
    • Prefix alanına sahip ConsoleNotifier
    • Filename alanına sahip FileNotifier
    • URL alanına sahip WebhookNotifier
    Her struct, bildirimin nasıl gönderileceğini açıklayan bir dize döndürmek için Notify metodunu uygulamalıdır.
  • main.go: Bir Notifier dilimi (slice) ve bir mesaj dizesi kabul eden BroadcastAlert adında bir fonksiyon oluşturun. Bu fonksiyon, her bir bildirimci üzerinde Notify metodunu çağırmalı ve sonuçları yazdırmalıdır. Yapılandırmayı girdiden okuyun, her üç bildirimci türünü de oluşturun ve hepsinden bir uyarı yayınlayın.

Aşağıdaki girdiler sağlanacaktır:

  • Satır 1: Konsol öneki (Console prefix)
  • Satır 2: Dosya adı (Filename)
  • Satır 3: Webhook URL'si
  • Satır 4: Uyarı mesajı (Alert message)

Notify metodlarınız şu formatlarda dizeler döndürmelidir:

  • ConsoleNotifier: [Prefix] [message]
  • FileNotifier: Writing to [Filename]: [message]
  • WebhookNotifier: POST [URL]: [message]

Örneğin, ALERT:, log.txt, https://hooks.example.com ve Server down! verildiğinde, çıktınız şu şekilde olmalıdır:

ALERT: Server down!
Writing to log.txt: Server down!
POST https://hooks.example.com: Server down!

Bu tasarımın gücü, BroadcastAlert fonksiyonunuzun belirli bildirimci türlerini bilmemesi veya umursamamasıdır; yalnızca her birinin Notifier sözleşmesini yerine getirmesini gerektirir. Daha sonra yayın fonksiyonunu hiç değiştirmeden bir SlackNotifier veya EmailNotifier ekleyebilirsiniz.

Kopya kağıdı

Go'daki arayüzler (interfaces), somut türleri belirtmeden gerekli davranışı tanımlayan sözleşmeler (contracts) olarak işlev görür. Bir fonksiyon bir arayüz türünü kabul ettiğinde, arayüzün metotlarını uygulayan herhangi bir tür kullanılabilir.

Metot imzalarını belirterek bir arayüz tanımlayın:

type DataSource interface {
    FetchData() string
}

Arayüzleri kabul eden fonksiyonlar, sözleşmeyi karşılayan her türle çalışır:

func ProcessData(src DataSource) {
    data := src.FetchData()
    fmt.Println("Processing:", data)
}

Birden fazla tür aynı arayüzü uygulayabilir:

type Database struct{ ConnectionString string }
func (d Database) FetchData() string { return "data from DB" }

type APIClient struct{ Endpoint string }
func (a APIClient) FetchData() string { return "data from API" }

func main() {
    db := Database{ConnectionString: "localhost"}
    api := APIClient{Endpoint: "https://api.example.com"}
    
    ProcessData(db)   // Çalışır!
    ProcessData(api)  // Bu da çalışır!
}

Bu sözleşme tabanlı yaklaşım şunları sağlar:

  • Belirli türlerden ziyade davranışa odaklanan fonksiyonlar
  • Mock uygulamaları aracılığıyla daha iyi test edilebilirlik
  • Mevcut kodu değiştirmeden genişletilebilirlik

Kendin dene

package main

import (
	"bufio"
	"fmt"
	"os"
)

// BroadcastAlert tüm bildirimciler aracılığıyla bir mesaj gönderir
// TODO: Bu fonksiyonu şunları yapacak şekilde uygulayın:
// - Bir Notifier değerleri dilimi (slice) ve bir mesaj dizesi kabul etsin
// - Her bir bildirimci üzerinde Notify metodunu çağırsın ve sonuçları yazdırsın
func BroadcastAlert(notifiers []Notifier, message string) {
	// TODO: Bildirimciler arasında döngü kurun ve her bir bildirim sonucunu yazdırın
}

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Konsol önekini oku
	scanner.Scan()
	prefix := scanner.Text()
	
	// Dosya adını oku
	scanner.Scan()
	filename := scanner.Text()
	
	// Webhook URL'sini oku
	scanner.Scan()
	url := scanner.Text()
	
	// Uyarı mesajını oku
	scanner.Scan()
	message := scanner.Text()
	
	// TODO: Üç bildirimci türünün de örneklerini oluşturun
	// - Önek ile ConsoleNotifier
	// - Dosya adı ile FileNotifier
	// - URL ile WebhookNotifier
	
	// TODO: Üç bildirimciyi de içeren bir Notifier dilimi oluşturun
	
	// TODO: Bildirimciler ve mesaj ile BroadcastAlert fonksiyonunu çağırın
	
	_ = prefix
	_ = filename
	_ = url
	_ = message
	fmt.Println("TODO: Implement the solution")
}
quiz iconKendini test et

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