Паттерн проектирования Мост

Паттерн Мост (Bridge) — это структурный паттерн проектирования, который позволяет нам отделить абстракции от реализаций и сделать их независимыми друг от друга. В результате абстракции и реализации могут разрабатываться как отдельные сущности. Паттерн Мост считается одним из лучших методов организации иерархии классов. Но давайте поподробнее разберем, что всё это значит.

Элементы паттерна Мост

  • Abstraction (абстракция). Это ядро ​​паттерна Мост. Она предоставляет ссылку на Implementer.
  • Refined Abstraction (расширенная абстракция) содержит различные вариации управляющей логики, наследуется от Abstraction и расширяет унаследованный интерфейс.
  • Implementer (реализатор). Определяет базовый интерфейс для конкретных реализаций. Этот интерфейс не обязательно должен напрямую соответствовать интерфейсу абстракции. Более того, он может сильно отличаться от него.
  • Concrete Implementation (конкретная реализация) наследуется от Implementer.
python logo

Английский для программистов

Наш телеграм канал с тестами по английскому языку для программистов. Английский это часть карьеры программиста. Поэтому полезно заняться им уже сейчас

×

Какую проблему решает паттерн Мост?

Давайте представим, что у нас есть класс Cuboid (параллелепипед), который имеет три атрибута: length (длина), breadth (ширина) и height (высота). Также он имеет три метода: ProducewithAPI(), ProduceWithAPI2() и expand().

Два метода зависят от реализации, поскольку у нас есть два API, а один метод — expand() — не зависит.

Пока у нас всего три метода, все в порядке. Но в масштабируемом проекте их количество может вырасти, и тогда разработчикам станет крайне сложно с ними справляться. Посмотрите, как быстро может разрастись такая структура:

 

Следующий код написан без использования паттерна Мост:

""" Code without using the bridge method
	We have a class with three attributes
	named as length, breadth, and height and
	three methods named as ProduceWithAPI1(),
	ProduceWithAPI2(), and expand(). Out of these
	producing methods are implementation-specific
	as we have two production APIs"""

class Cuboid:

	class ProducingAPI1:

		"""Implementation Specific Implementation"""

		def produceCuboid(self, length, breadth, height):

			print(f'API1 is producing Cuboid with length = {length}, '
				f' Breadth = {breadth} and Height = {height}')

	class ProducingAPI2:
		"""Implementation Specific Implementation"""

		def produceCuboid(self, length, breadth, height):
			print(f'API2 is producing Cuboid with length = {length}, '
				f' Breadth = {breadth} and Height = {height}')


	def __init__(self, length, breadth, height):

		"""Initialize the necessary attributes"""

		self._length = length
		self._breadth = breadth
		self._height = height

	def produceWithAPI1(self):

		"""Implementation specific Abstraction"""

		objectAPIone = self.ProducingAPI1()
		objectAPIone.produceCuboid(self._length, self._breadth, self._height)

	def producewithAPI2(self):

		"""Implementation specific Abstraction"""

		objectAPItwo = self.ProducingAPI2()
		objectAPItwo.produceCuboid(self._length, self._breadth, self._height)

	def expand(self, times):

		"""Implementation independent Abstraction"""

		self._length = self._length * times
		self._breadth = self._breadth * times
		self._height = self._height * times

# Instantiate a Cubiod
cuboid1 = Cuboid(1, 2, 3)

# Draw it using APIone
cuboid1.produceWithAPI1()

# Instantiate another Cuboid
cuboid2 = Cuboid(19, 20, 21)

# Draw it using APItwo
cuboid2.producewithAPI2()

Решение проблемы при помощи паттерна Мост

Теперь давайте посмотрим на решение вышеуказанной проблемы с использованием паттерна Мост. Данный паттерн является одним из лучших решений для такого рода проблем.

Наша основная задача — разделить код абстракции и реализации. В результате мы получим следующую структуру:

Следующий код написан с использованием паттерна Мост:

"""Code implemented with Bridge Method.
We have a Cuboid class having three attributes
named as length, breadth, and height and three
methods named as produceWithAPIOne(), produceWithAPItwo(),
and expand(). Our purpose is to separate out implementation
specific abstraction from implementation-independent
abstraction"""

class ProducingAPI1:

	"""Implementation specific Abstraction"""

	def produceCuboid(self, length, breadth, height):

		print(f'API1 is producing Cuboid with length = {length}, '
			f' Breadth = {breadth} and Height = {height}')

class ProducingAPI2:

	"""Implementation specific Abstraction"""

	def produceCuboid(self, length, breadth, height):

		print(f'API2 is producing Cuboid with length = {length}, '
			f' Breadth = {breadth} and Height = {height}')

class Cuboid:

	def __init__(self, length, breadth, height, producingAPI):

		"""Initialize the necessary attributes
		Implementation independent Abstraction"""

		self._length = length
		self._breadth = breadth
		self._height = height

		self._producingAPI = producingAPI

	def produce(self):

		"""Implementation specific Abstraction"""

		self._producingAPI.produceCuboid(self._length, self._breadth, self._height)

	def expand(self, times):

		"""Implementation independent Abstraction"""

		self._length = self._length * times
		self._breadth = self._breadth * times
		self._height = self._height * times


"""Instantiate a cuboid and pass to it an
object of ProducingAPIone"""

cuboid1 = Cuboid(1, 2, 3, ProducingAPI1())
cuboid1.produce()

cuboid2 = Cuboid(19, 19, 19, ProducingAPI2())
cuboid2.produce()

UML-диаграмма паттерна Мост

Вот так будет выглядеть UML-диаграмма паттерна Мост:

Преимущества применения паттерна Мост

  1. Принцип единой ответственности. Паттерн Мост четко следует принципу единой ответственности: он отделяет абстракцию от ее реализации, чтобы они могли видоизменяться независимо друг от друга.
  2. Принцип открытости/закрытости. Данный паттерн не нарушает принцип открытости/закрытости. Поскольку абстракция и реализация не зависят друг от друга, мы в любой момент можем ввести новые абстракции и реализации.
  3. Кроссплатформенность. Паттерн Мост можно легко использовать для реализации независимых от платформы функций и создания кроссплатформенных приложений.

Недостатки применения паттерна Мост

  1. Сложность. При использовании Моста наш код может усложниться, потому что мы вводим новые классы абстракций и реализаций.
  2. Снижение производительности. Применение Моста может немного ухудшить производительность.
  3. Интерфейсы только с одной реализацией. Если у нас ограниченное количество интерфейсов, то это не страшно. Но если у нас раздутый набор интерфейсов (абстракций) с минимальным количеством реализаций или вообще с одной реализацией, ими становится крайне трудно управлять.

Применение паттерна Мост

  • Динамическое связывание. Обычно паттерн Мост используется для обеспечения динамического связывания реализации. Это означает, что мы можем вызывать метод во время выполнения, а не во время компиляции.
  • Сопоставление классов. Данный паттерн также используется для сопоставления ортогональных иерархий классов.
  • UI-окружение. Мост используется при определении очертаний пользовательского интерфейса.

Заключение

Итак, мы познакомились с паттерном проектирования Мост (Bridge). Теперь вы знаете, какую проблему он решает и как применяется. Кроме того, мы разобрали преимущества и недостатки данного паттерна.

Надеемся, данная статья была вам полезна! Успехов в написании кода!

Перевод статьи «Bridge Method – Python Design Patterns».