Что такое паттерны проектирования?
Проблемы, решаемые паттернами
2.87M
Категория: ПрограммированиеПрограммирование

Паттерны проектирования и примеры их использования в Go

1.

«Паттерны проектирования и примеры их
использования в Go»

2.

Материалы (контент) могут использоваться исключительно в учебных и
информационных целях без цели извлечения прибыли с обязательным
указанием следующей информации:
© ООО «КНС ГРУПП» (YADRO), 2022-2024, все права защищены.
Авторы – Александрова О. В., Андрианов А.А., Денисенко Б. А.,
Дутова А. А., Ефимов М. А., Клочкова Л. И., Королев Д. О.,
Котлярова Л. П., Никифоров И. В., Ромашов В. А., Тянутов М. В.

3.

Что такое паттерны проектирования?

4. Что такое паттерны проектирования?

Паттерны проектирование – это готовые решения часто
встречающихся задач при реализации программ. Не всегда
есть потребность создавать свои варианты решений, потому
что трудозатраты могут оказаться большими, а польза от
этого маленькая. В таких ситуациях воспользоваться
паттернами – лучшее решение. Каждый паттерн создан для
решения определенной часто встречающейся задачи.
4

5. Проблемы, решаемые паттернами

Паттерн представляет собой не какой-то конкретный код, а общую
концепцию решения той или иной проблемы, которую
нужно будет подстроить под нужды вашей программы. Для
лучшего понимания можно привести реальные примеры,
которые показывают нужду паттернов:
Расширение и тестируемость
Сложные ветвления(if/else, switch)
При добавлении новой логики приходится Логика ветвления разрастается по коду и
править десятки файлов
поддерживать ее становится сложнее
5

6.

Кто такие банда четырех?

7.

История
В 1994 году Эрих Гамма, Ричард Хелм, Ральф Джонсон,Джон Влиссидес написали книгу
“Приемы объектно-ориентированного проектирования. Паттерны проектирования”, в
которую вошли 23 паттерна, решающие проблемы объектно-ориентированного дизайна.
Название книги было слишком сложное и длинное, поэтому со временем их начали
называть “book by the gang of four”, то есть книгой банды четырех.
7

8.

Категории паттернов
• Порождающие паттерны – беспокоятся о гибком создании объектов без внесения в
программу лишних зависимостей.
• Структурные паттерны – показывают различные способы построения связей между
объектами.
• Поведенческие паттерны – заботятся об эффективной коммуникации между объектами.
Важно понимать, что в оригинальной книге они описаны не на Go, а на классических
объектно-ориентированных языках. Подходы и примеры опираются на наследование,
иерархии классов и виртуальные методы.
8

9.

Примеры паттернов проектирования

10.

Порождающие паттерны
Singleton
Одиночка – это порождающий паттерн проектирования, который гарантирует, что у
структуры есть только один экземпляр, и предоставляет к нему глобальную точку доступа.
type instance struct{}
var singleInstance *instance
var once sync.Once
func GetInstance() *instance {
once.Do(func() {
log.Println("creating single instance now")
singleInstance = &instance{}
})
return singleInstance
}
singleton.go
10

11.

Порождающие паттерны
Builder
Строитель – это порождающий паттерн проектирования, который позволяет создавать
сложные объекты пошагово.
type ServerConfig struct {
Host string
Port int
Timeout int
TLS
bool
MaxConns int
}
type ServerConfigBuilder struct {
cfg ServerConfig
}
func (b *ServerConfigBuilder) WithTimeout(t int) *ServerConfigBuilder {
b.cfg.Timeout = t
return b
}
func (b *ServerConfigBuilder) WithTLS(e bool) *ServerConfigBuilder {
b.cfg.TLS = e
return b
}
func (b *ServerConfigBuilder) Build() ServerConfig {
return b.cfg
}
cfg := NewServerConfigBuilder(“localhost”, 8080).WithTimeout(10).
WithTLS(false).Build()
builder.go
11

12.

Структурные паттерны
Adapter
Адаптер – это структурный паттерн проектирования, который позволяет объектам с
несовместимыми интерфейсами работать вместе.
// client hardly dep by this logger
type Logger interface {
Info(msg string)
Error(msg string)
}
type OtherLogger struct{}
func (l *OtherLogger) LogInfo(msg string) string {
return msg
}
func (l *OtherLogger) LogError(msg string) string {
return msg
}
type OtherLoggerAdapter struct {
otherLogger *OtherLogger
}
func (a *OtherLoggerAdapter) Info(msg string) {
log.Println(a.otherLogger.LogInfo(msg))
}
adapter.go
func (a *OtherLoggerAdapter) Error(msg string) {
log.Println(a.otherLogger.LogError(msg))
}
12

13.

Структурные паттерны
Decorator
Декоратор – это структурный паттерн проектирования, который позволяет
динамически добавлять объектам новую функциональность, оборачивая их в
полезные «обёртки».
// some handler
func hw(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello world!"))
}
func loggin(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r
*http.Request) {
log.Printf("REQUEST: [%s], URL: [%s]\n", r.Method,
r.URL)
next.ServeHTTP(w, r)
})
}
func TimingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
startTime := time.Now()
next.ServeHTTP(w, r)
log.Printf("DURATION: [%d]\n", time.Since(startTime).Microseconds())
})
}
var h http.Handler
h = http.HandlerFunc(helloHandler)
h = LoggingMiddleware(h)
h = TimingMiddleware(h)
decorator.go
13

14.

Поведенческие паттерны
Strategy
Стратегия – это поведенческий паттерн проектирования, который определяет
семейство схожих алгоритмов и помещает каждый из них в собственную структуру,
после чего алгоритмы можно взаимозаменять прямо во время исполнения
программы.
type EvictionAlgo interface {
evict(c *Cache)
}
type Fifo struct{}
func (l *Fifo) evict(c *Cache) {
log.Println("Evicting by fifo strtegy")
}
type Lru struct{}
func (l *Lru) evict(c *Cache) {
log.Println("Evicting by lru strtegy")
}
type Cache struct {
storage
map[string]string
evictionAlgo EvictionAlgo
capacity int
maxCapacity int
}
func (c *Cache) setEvictionAlgo(e EvictionAlgo) {
c.evictionAlgo = e
strategy.go
}
14

15.

Поведенческие паттерны
Observer
Наблюдатель – это поведенческий паттерн проектирования, который создает
механизм подписки, позволяющий одним объектам следить и реагировать на
события, происходящие в других объектах.
type LoginEvent struct {
UserID string
}
type AuthService struct {
observers []chan LoginEvent
}
func (a *AuthService) Subscribe() <-chan LoginEvent {
ch := make(chan LoginEvent)
a.observers = append(a.observers, ch)
return ch
}
func (a *AuthService) Login(userID string) {
e := LoginEvent{UserID: userID}
for _, ch := range a.observers {
ch <- e
}
}
loggerCh := auth.Subscribe()
metricsCh := auth.Subscribe()
auth.Login("user-1")
auth.Login("user-2")
observer.go
15

16.

Примеры паттернов проектирования в
микросервисной архитектуре

17.

Распределенные системы
В микросервисной архитектуре предпосылки GoF-паттернов нарушаются.
• вызов – это сеть, а не метод. Сеть медленная, ненадежная, может упасть в любой момент.
• сервисы зависят от сети, а не локальных методов. Они падают, перегружаются и т.д
17

18.

Микросервисные паттерны
Timeout
Таймаут – это паттерн распределенных систем, который устанавливает
максимальное время ожидания ответа от другого сервиса.
type ServcieA struct{}
type ServiceB struct{}
type Order struct {
Title string json:"title"
Price float64 json:"price"
}
func (a *ServcieA) GetOrder(ctx context.Context, orderID uuid.UUID)
(*Order, error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
defer cancel()
var URL_SERVICE_B string = "https://service-b.com/api/orders/" +
orderID.String()
request, err := http.NewRequestWithContext(ctx, http.MethodGet,
URL_SERVICE_B, nil)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
timeout.go
}
return request, nil
18

19.

Микросервисные паттерны
Circuit Breaker
Автоматический выключатель – это паттерн проектирования распределенных
систем, который предотвращает каскадные отказы путем временного
блокирования запросов к неисправному сервису.
type HTTPClient struct {
client *http.Client
cb *gobreaker.CircuitBreaker
}
func NewHTTPClient() *HTTPClient {
cb := gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name:
"http-client",
MaxRequests: 2,
Timeout: 5 * time.Second,
ReadyToTrip: func(counts gobreaker.Counts) bool {
return counts.ConsecutiveFailures >= 2
},
})
return &HTTPClient{
client: &http.Client{Timeout: 3 * time.Second},
cb: cb,
}
}
func (c *HTTPClient) Get(url string) (*http.Response, error) {
result, err := c.cb.Execute(func() (interface{}, error) {
resp, err := c.client.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 500 {
return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
}
return resp, nil
})
if err != nil {
return nil, err
}
return result.(*http.Response), nil
}
breaker.go
19
English     Русский Правила