Программирование программного обеспечения включает в себя различные шаблоны, предназначенные для решения общих задач. Знакомство с этими шаблонами имеет решающее значение, поскольку они обеспечивают решения повторяющихся проблем в процессе разработки.
В этом уроке мы рассмотрим несколько хорошо известных шаблонов, в том числе шаблоны Decorator, Delegation и Composite, с которыми я часто сталкивался.
Шаблон декоратора
Шаблон Decorator — это структурный шаблон проектирования, который позволяет добавлять поведение к отдельному объекту статически или динамически, не влияя на поведение других объектов того же класса.
В этом шаблоне мы создаем серию классов декораторов, которые используются для обертывания конкретных компонентов. Декораторы добавляют функциональность компонентам, сохраняя при этом возможность связывать несколько декораторов вместе.
Вот пример шаблона Decorator, реализованного в Go, демонстрирующего, как он используется для персонализации заказов на кофе с различными добавками.
package main import "fmt" // Coffee defines the base component type Coffee interface { GetCost() int GetDescription() string } // SimpleCoffee is a concrete coffee type type SimpleCoffee struct{} func (c *SimpleCoffee) GetCost() int { return 100 } func (c *SimpleCoffee) GetDescription() string { return "Simple Coffee" } // MilkDecorator adds milk to the coffee type MilkDecorator struct { coffee Coffee } func (m *MilkDecorator) GetCost() int { return m.coffee.GetCost() + 20 } func (m *MilkDecorator) GetDescription() string { return m.coffee.GetDescription() + ", Milk" } // SugarDecorator adds sugar to the coffee type SugarDecorator struct { coffee Coffee } func (s *SugarDecorator) GetCost() int { return s.coffee.GetCost() + 10 } func (s *SugarDecorator) GetDescription() string { return s.coffee.GetDescription() + ", Sugar" } func main() { // Create a simple coffee coffee := &SimpleCoffee{} // Decorate the coffee with milk milkCoffee := &MilkDecorator{coffee} // Decorate the milk coffee with sugar sweetMilkCoffee := &SugarDecorator{milkCoffee} fmt.Printf("Order: %s, Cost: $%d\n", sweetMilkCoffee.GetDescription(), sweetMilkCoffee.GetCost()) }
В этом примере у нас есть интерфейс Coffee
, определяющий базовый компонент для разных видов кофе. SimpleCoffee
— это конкретный тип кофе, реализующий базовый интерфейс Coffee
.
Затем у нас есть два декоратора, MilkDecorator
и SugarDecorator
, которые добавляют в кофе молоко и сахар соответственно. Каждый декоратор встраивает Coffee
и модифицирует методы GetCost
и GetDescription
, чтобы включить дополнительные затраты и описания.
В функции main
мы создаем простой кофе, а затем украшаем его молоком и сахаром с помощью декораторов. Окончательный вывод будет:
Order: Simple Coffee, Milk, Sugar, Cost: $130
Шаблон делегирования
Шаблон делегирования, также известный как шаблон проектирования делегирования, представляет собой структурный шаблон, который позволяет объекту делегировать определенные задачи или обязанности другому объекту, известному как делегат. Этого можно достичь с помощью композиции, где объект делегирования содержит ссылку на делегата и перенаправляет вызовы делегату.
Вот пример шаблона делегирования, реализованного в Go:
package main import "fmt" // MediaPlayer defines the delegating object type MediaPlayer struct { mediaPlayerDelegate MediaPlayerDelegate } // MediaPlayerDelegate defines the delegate interface type MediaPlayerDelegate interface { PlayMedia(file string) } // AudioPlayer is a concrete delegate for playing audio files type AudioPlayer struct{} func (a *AudioPlayer) PlayMedia(file string) { fmt.Printf("Playing audio file: %s\n", file) } // VideoPlayer is another concrete delegate for playing video files type VideoPlayer struct{} func (v *VideoPlayer) PlayMedia(file string) { fmt.Printf("Playing video file: %s\n", file) } func main() { audioPlayer := &AudioPlayer{} videoPlayer := &VideoPlayer{} mediaPlayer := &MediaPlayer{mediaPlayerDelegate: audioPlayer} mediaPlayer.PlayMedia("song.mp3") mediaPlayer.mediaPlayerDelegate = videoPlayer mediaPlayer.PlayMedia("movie.mp4") }
В этом примере у нас есть интерфейс MediaPlayerDelegate
, определяющий обязанности, которые можно делегировать, в данном случае воспроизведение медиафайлов. AudioPlayer
и VideoPlayer
являются конкретными реализациями этого интерфейса, каждая из которых обрабатывает аудио- и видеофайлы соответственно.
Структура MediaPlayer
— это объект делегирования, содержащий ссылку на делегата через поле mediaPlayerDelegate
. Он делегирует задачу воспроизведения медиафайлов делегату.
В функции main
мы создаем экземпляры AudioPlayer
и VideoPlayer
. Затем мы создаем MediaPlayer
, который начинается с делегата AudioPlayer
и воспроизводит аудиофайл. Позже мы переключим делегата на VideoPlayer
и воспроизведем видеофайл.
Когда мы запустим код, мы увидим следующий вывод:
Playing audio file: song.mp3 Playing video file: movie.mp4
Композитный шаблон
Шаблон Composite — это структурный шаблон проектирования, который позволяет объединять объекты в древовидные структуры для представления иерархий часть-целое. Composite позволяет клиентам обрабатывать отдельные объекты и композиции объектов единообразно.
Вот пример шаблона Composite в Go с использованием структуры файловой системы в качестве контекста:
package main import "fmt" // Component is the common interface for both leaf and composite objects type Component interface { Display(indentation string) } // File is a leaf object that represents a file type File struct { name string } func (f *File) Display(indentation string) { fmt.Println(indentation + f.name) } // Directory is a composite object that can hold files and subdirectories type Directory struct { name string components []Component } func (d *Directory) Add(component Component) { d.components = append(d.components, component) } func (d *Directory) Display(indentation string) { fmt.Println(indentation + "Directory: " + d.name) for _, component := range d.components { component.Display(indentation + " ") } } func main() { rootDirectory := &Directory{name: "Root"} textFilesDirectory := &Directory{name: "Text Files"} imageFilesDirectory := &Directory{name: "Image Files"} file1 := &File{name: "file1.txt"} file2 := &File{name: "file2.txt"} file3 := &File{name: "file3.png"} file4 := &File{name: "file4.png"} textFilesDirectory.Add(file1) textFilesDirectory.Add(file2) imageFilesDirectory.Add(file3) imageFilesDirectory.Add(file4) rootDirectory.Add(textFilesDirectory) rootDirectory.Add(imageFilesDirectory) rootDirectory.Display("") }
В этом примере у нас есть интерфейс Component
, который определяет общие методы как для листовых объектов (File
), так и для составных объектов (Directory
).
Структура File
— это конечный объект, представляющий файл. Он реализует метод Display
для отображения своего имени.
Структура Directory
— это составной объект, который может содержать файлы и другие каталоги. Он имеет массив из Component
объектов и методов для добавления компонентов и отображения содержимого каталога.
В функции main
мы создаем иерархию каталогов и файлов, а затем вызываем метод Display
для корневого каталога, чтобы проиллюстрировать структуру.
Когда мы запустим код, мы увидим вывод:
Directory: Root Directory: Text Files file1.txt file2.txt Directory: Image Files file3.png file4.png
В этом уроке мы рассмотрели три известных шаблона: шаблон декоратора, который позволяет динамически настраивать объекты путем наложения декораторов; шаблон Delegate, позволяющий объектам делегировать задачи другим объектам; и шаблон Composite, упрощающий создание иерархических структур из отдельных и составных объектов. Понимая и эффективно используя эти шаблоны, разработчики могут улучшить модульность кода, возможность повторного использования и удобство сопровождения, что в конечном итоге приведет к созданию более надежных и эффективных программных решений.
Если вы найдете эту информацию полезной, пожалуйста, поддержите меня и следите за обновлениями.
Спасибо, что дочитали до конца. Пожалуйста, следите за автором и этой публикацией. Посетите Stackademic, чтобы узнать больше о том, как мы демократизируем бесплатное обучение программированию по всему миру.