GoDasturchi
Interface-Based Design

Interface-Based Design

Interfeysga asoslangan dizayn

"Interface Design" kursida interfeyslarning texnik tomonlarini (kichik saqlash, qayerda e'lon qilish) ko'rgan edingiz. Endi shu bilimni arxitektura darajasida qo'llaymiz: butun dasturni bir-biriga bog'liq bo'lmagan, interfeys orqali gaplashadigan qismlarga bo'lish.

Buni elektr rozetkasiga o'xshating: rozetka "standart shakl"ni taqdim etadi, va siz unga choynak ham, kompyuter ham, telefon zaryadlagichi ham ulashingiz mumkin — rozetkaning o'zi ichida qanday qurilma borligini "bilishi" shart emas. Kodda ham xuddi shunday: bir qism (masalan Service) boshqa qismning (Logger) aniq turini emas, faqat uning "interfeysi"ni biladi.

example.go
package main

import "fmt"

type Logger interface {
	Log(msg string)
}

type ConsoleLogger struct{}

func (c ConsoleLogger) Log(msg string) { fmt.Println("[LOG]", msg) }

type Service struct {
	logger Logger
}

func NewService(logger Logger) *Service {
	return &Service{logger: logger}
}

func (s *Service) DoWork() {
	s.logger.Log("ish bajarildi")
}

func main() {
	svc := NewService(ConsoleLogger{})
	svc.DoWork()
}

Service struct'i logger Logger maydoniga ega — aniq ConsoleLogger turiga emas, balki interfeysga. Bu shuni anglatadi: agar ertaga faylga yozadigan FileLogger yoki hech narsa qilmaydigan NoOpLogger yaratsangiz, Servicening kodini birorta ham qatorini o'zgartirmasdan ularni ishlata olasiz — faqat NewServicega boshqa argument berasiz.

Bu — real loyihalarda testlash uchun ham hayotiy ahamiyatga ega: testda haqiqiy ConsoleLogger o'rniga, xabarlarni shunchaki ro'yxatga yig'ib boradigan soxta logger berish mumkin ("Mocking Dependencies" darsida ko'rgan naqsh) — Servicening o'zi buni sezmaydi ham.

>_ Exercise

Ikkita implementatsiyaga ega Storage interfeysini yarating.

  • Storage interfeysini yozing: Save(key, value string)
  • MemoryStorage struct'i (map[string]string) yozing, Save metodi bilan
  • Cache struct'i (storage Storage maydoni) yozing, NewCache(s Storage) *Cache konstruktori bilan
  • Cache.Set(key, value string) metodi — ichki storage.Save'ni chaqirsin
  • NewCache(&MemoryStorage{data: map[string]string{}}) yaratib, Set("til", "go") chaqiring, so'ng MemoryStorage.data["til"]ni chop eting

Stuck? Reveal a hint to help you.

Hints (0/3)

Key Takeaway

Key Takeaway:

Interfeysga asoslangan dizaynda komponentlar bir-birining aniq turini emas, faqat kerakli xatti-harakatni (interfeysni) bilishadi — bu ularni bog'liqlikdan xoli, mustaqil almashtirish mumkin qiladi.

NEXT UP

Dependency Injection

OUTPUT

$ go run main.go
Kodingizni ishga tushiring

Interface-Based Design

Interfeysga asoslangan dizayn

"Interface Design" kursida interfeyslarning texnik tomonlarini (kichik saqlash, qayerda e'lon qilish) ko'rgan edingiz. Endi shu bilimni arxitektura darajasida qo'llaymiz: butun dasturni bir-biriga bog'liq bo'lmagan, interfeys orqali gaplashadigan qismlarga bo'lish.

Buni elektr rozetkasiga o'xshating: rozetka "standart shakl"ni taqdim etadi, va siz unga choynak ham, kompyuter ham, telefon zaryadlagichi ham ulashingiz mumkin — rozetkaning o'zi ichida qanday qurilma borligini "bilishi" shart emas. Kodda ham xuddi shunday: bir qism (masalan Service) boshqa qismning (Logger) aniq turini emas, faqat uning "interfeysi"ni biladi.

example.go
package main

import "fmt"

type Logger interface {
	Log(msg string)
}

type ConsoleLogger struct{}

func (c ConsoleLogger) Log(msg string) { fmt.Println("[LOG]", msg) }

type Service struct {
	logger Logger
}

func NewService(logger Logger) *Service {
	return &Service{logger: logger}
}

func (s *Service) DoWork() {
	s.logger.Log("ish bajarildi")
}

func main() {
	svc := NewService(ConsoleLogger{})
	svc.DoWork()
}

Service struct'i logger Logger maydoniga ega — aniq ConsoleLogger turiga emas, balki interfeysga. Bu shuni anglatadi: agar ertaga faylga yozadigan FileLogger yoki hech narsa qilmaydigan NoOpLogger yaratsangiz, Servicening kodini birorta ham qatorini o'zgartirmasdan ularni ishlata olasiz — faqat NewServicega boshqa argument berasiz.

Bu — real loyihalarda testlash uchun ham hayotiy ahamiyatga ega: testda haqiqiy ConsoleLogger o'rniga, xabarlarni shunchaki ro'yxatga yig'ib boradigan soxta logger berish mumkin ("Mocking Dependencies" darsida ko'rgan naqsh) — Servicening o'zi buni sezmaydi ham.

>_ Exercise

Ikkita implementatsiyaga ega Storage interfeysini yarating.

  • Storage interfeysini yozing: Save(key, value string)
  • MemoryStorage struct'i (map[string]string) yozing, Save metodi bilan
  • Cache struct'i (storage Storage maydoni) yozing, NewCache(s Storage) *Cache konstruktori bilan
  • Cache.Set(key, value string) metodi — ichki storage.Save'ni chaqirsin
  • NewCache(&MemoryStorage{data: map[string]string{}}) yaratib, Set("til", "go") chaqiring, so'ng MemoryStorage.data["til"]ni chop eting

Stuck? Reveal a hint to help you.

Hints (0/3)

Key Takeaway

Key Takeaway:

Interfeysga asoslangan dizaynda komponentlar bir-birining aniq turini emas, faqat kerakli xatti-harakatni (interfeysni) bilishadi — bu ularni bog'liqlikdan xoli, mustaqil almashtirish mumkin qiladi.

NEXT UP

Dependency Injection