Menu
Coddy logo textTech

Паттерн Адаптер

Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 92 из 107.

Паттерн Adapter позволяет несовместимым интерфейсам работать вместе, оборачивая существующий тип новым интерфейсом. В то время как Command инкапсулирует действия, Adapter преобразует один интерфейс в другой, который ожидают клиенты.

Представьте, что у вас есть legacy-принтер, который использует другую сигнатуру метода, чем ожидает ваше application:

// Целевой интерфейс, который использует ваше приложение
type Printer interface {
    Print(text string) string
}

// Устаревший принтер с несовместимым интерфейсом
type OldPrinter struct{}

func (o OldPrinter) PrintDocument(doc string, copies int) string {
    return fmt.Sprintf("Old printer: %s (x%d)", doc, copies)
}

adapter оборачивает legacy-тип и реализует ожидаемый интерфейс:

type OldPrinterAdapter struct {
    oldPrinter OldPrinter
}

func (a OldPrinterAdapter) Print(text string) string {
    return a.oldPrinter.PrintDocument(text, 1)
}

Теперь legacy printer без проблем работает с кодом, который ожидает интерфейс Printer:

func PrintMessage(p Printer, msg string) string {
    return p.Print(msg)
}

adapter := OldPrinterAdapter{oldPrinter: OldPrinter{}}
result := PrintMessage(adapter, "Hello")  // "Old printer: Hello (x1)"

Паттерн Adapter бесценен при интеграции сторонних библиотек, работе с legacy-кодом или когда вам нужно использовать существующие классы, которые не соответствуют требованиям вашего interface. Он служит мостом, не изменяя исходный код.

challenge icon

Задание

Легко

Давайте создадим систему медиаплеера с использованием паттерна Adapter! У вас есть современный аудиоплеер, который ожидает унифицированный интерфейс, но вам необходимо интегрировать устаревшее аудиооборудование, использующее совершенно другие сигнатуры методов. Ваш адаптер устранит этот разрыв, позволяя старому оборудованию беспрепятственно работать с новой системой.

Вы организуете код в трёх файлах:

  • player.go: определите целевой интерфейс и устаревшее аудиоустройство.

    Создайте интерфейс AudioPlayer, который ожидает ваша современная система, с методом Play(track string) string, принимающим название трека и возвращающим сообщение о воспроизведении.

    Также определите устаревшее устройство, которое не соответствует этому интерфейсу:

    • структура VinylPlayer с полем SpeedRPM (int). У неё есть метод DropNeedle(record string, side string) string, который возвращает Playing [record] ([side] side) at [speed]rpm

    Интерфейс VinylPlayer несовместим с AudioPlayer. Он требует два параметра и использует совершенно другое имя метода.

  • adapter.go: создайте адаптер, который сделает устаревшее устройство совместимым.

    Создайте структуру VinylAdapter, которая оборачивает VinylPlayer и реализует интерфейс AudioPlayer. Когда вызывается Play, адаптер должен вызвать у винилового плеера метод DropNeedle, подставляя A по умолчанию в качестве параметра стороны.

    Также создайте современный плеер для сравнения:

    • структура DigitalPlayer: её метод Play возвращает Streaming: [track]
  • main.go: продемонстрируйте работу обоих плееров через один и тот же интерфейс.

    Создайте функцию PlayMusic(player AudioPlayer, track string) string, которая использует интерфейс для воспроизведения музыки. Эта функция не должна знать или учитывать, используется ли цифровой плеер или адаптированный виниловый плеер.

    Прочитайте тип плеера (digital или vinyl), а если выбран vinyl, также прочитайте скорость RPM. Затем прочитайте название трека. Создайте соответствующий плеер (используя адаптер для vinyl), воспроизведите трек с помощью функции PlayMusic и выведите результат.

Будут предоставлены следующие входные данные:

  • Строка 1: тип плеера (digital или vinyl)
  • Строка 2: скорость RPM (только если выбран vinyl)
  • Последняя строка: название воспроизводимого трека

Например, если даны:

digital
Bohemian Rhapsody

Ваш вывод должен быть таким:

Streaming: Bohemian Rhapsody

А если даны:

vinyl
33
Abbey Road

Ваш вывод должен быть таким:

Playing Abbey Road (A side) at 33rpm

А если даны:

vinyl
45
Blue Monday

Ваш вывод должен быть таким:

Playing Blue Monday (A side) at 45rpm

Обратите внимание, как функция PlayMusic работает одинаково с обоими плеерами. Она не знает, что виниловый плеер на самом деле адаптируется из совершенно другого интерфейса. Адаптер преобразует вызовы современного интерфейса в устаревший формат в фоновом режиме!

Попробуйте сами

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// PlayMusic использует интерфейс AudioPlayer для воспроизведения музыки
// Эта функция работает с любой реализацией AudioPlayer
// TODO: Реализуйте эту функцию, чтобы вызвать player.Play(track) и вернуть результат
func PlayMusic(player AudioPlayer, track string) string {
	// TODO: Реализуйте эту функцию
	return ""
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Читаем тип плеера
	playerType, _ := reader.ReadString('\n')
	playerType = strings.TrimSpace(playerType)
	
	var player AudioPlayer
	var track string
	
	if playerType == "vinyl" {
		// Читаем скорость RPM для винила
		rpmStr, _ := reader.ReadString('\n')
		rpmStr = strings.TrimSpace(rpmStr)
		rpm, _ := strconv.Atoi(rpmStr)
		
		// Читаем название трека
		track, _ = reader.ReadString('\n')
		track = strings.TrimSpace(track)
		
		// TODO: Создайте VinylPlayer с заданным RPM
		// TODO: Оберните его в VinylAdapter
		// TODO: Присвойте переменной player
		_ = rpm // Удалите эту строку, когда используете rpm
	} else {
		// Читаем название трека
		track, _ = reader.ReadString('\n')
		track = strings.TrimSpace(track)
		
		// TODO: Создайте DigitalPlayer
		// TODO: Присвойте переменной player
	}
	
	// TODO: Вызовите PlayMusic и выведите результат
	_ = player // Удалите эту строку, когда будете использовать player
	_ = track  // Удалите эту строку, когда будете использовать track
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Go