- Пример применения шаблонного метода
- Улучшение шаблонного метода через введение новых подклассов
- Выводы
Позднее связывание приводит к одному следствию. Из базового класса можно вызывать методы и свойства, определенные в наследниках. Причем самих наследников может даже не существовать. Позднее связывание на то и позднее, что проверка происходит только в тот момент, когда этот код используется.
Эту особенность используют в паттерне «шаблонный метод». Он применяется, когда у подклассов есть общая логика, которая частично опирается на поведение подклассов. Такая логика реализуется в методе базового класса, а часть, которая различается для каждого подкласса, делегируется наследникам.
В этом уроке мы рассмотрим, как паттерн «шаблонный метод» может быть применен в разработке программ. Мы изучим его структуру и механизмы работы на примере, а также посмотрим, как этот паттерн помогает упростить и улучшить код.
Пример применения шаблонного метода
Возьмем для примера класс HTMLAnchorElement
. Посмотрим на метод __str__()
. Видно, что его код останется идентичным для большинства тегов. Единственное, что меняется, — название самого тега:
class HTMLAnchorElement(HTMLElement):
def __str__(self):
# Родительский метод
attr_line = self.stringify_attributes()
# Родительский метод
body = self.get_text_content()
return f'<a{attr_line}>{body}</a>'
Здесь мы видим пример класса HTMLAnchorElement
, который наследуется от HTMLElement
. Метод __str__()
возвращает строку, представляющую HTML-элемент <a>
, включающий атрибуты и текстовое содержимое.
Мы можем модифицировать код так, что метод __str__()
переместится в HTMLElement
. И единственная вещь, которая останется за подклассами, – имя тега:
class HtmlElement:
def __str__(self):
attr_line = self.stringify_attributes()
body = self.get_text_content()
tag_name = self.get_tag_name()
return f"<{tag_name} {attr_line}>{body}</{tag_name}>"
В этом примере мы переместили общую логику по созданию HTML-элемента в базовый класс HtmlElement
, а специфичные детали, например, имя тега, оставили за подклассами, которые должны реализовать метод get_tag_name()
.
Получившийся код лучше исходного варианта, так как он значительно сокращает дублирование (тегов около 100 штук).
В методе __str__
мы вызываем метод get_tag_name()
, который должен быть реализован в наследниках:
class HtmlAnchorElement(HtmlElement):
def get_tag_name(self):
return "a"
Метод get_tag_name()
в этом подклассе возвращает строку "a"
, которая является именем тега.
При этом теги бывают одиночные, значит, текущий вариант __str__
не подойдет для них. Из этой ситуации можно выйти разными способами, например, с помощью наследования.
Улучшение шаблонного метода через введение новых подклассов
Создадим у HTMLElement
два подкласса: HTMLSingleElement
и HTMLPairElement
. Теперь классы конкретных тегов должны наследоваться от одного из указанных классов. В каждом из этих классов будет своя реализация метода __str__()
:
class HtmlSingleElement(HtmlElement):
def __str__(self):
attr_line = self.stringify_attributes()
# get_tag_name – метод, который должны реализовать все подклассы
tag_name = self.get_tag_name()
# Создаётся одиночный тег
return f"<{tag_name} {attr_line} />"
class HtmlPairElement(HtmlElement):
def __str__(self):
attr_line = self.stringify_attributes()
body = self.get_text_content()
# get_tag_name – метод, который должны реализовать все подклассы
tag_name = self.get_tag_name()
return f"<{tag_name} {attr_line}>{body}</{tag_name}>"
Мы создали два подкласса HtmlSingleElement
и HtmlPairElement
, каждый из которых реализует свою версию метода __str__()
, подходящую для одиночных и парных HTML-элементов соответственно. Несмотря на различия в реализации __str__()
, оба этих подкласса требуют от своих наследников реализации одного и того же метода get_tag_name()
.
Выводы
Применение шаблонного метода позволяет избавиться от дублирования кода, делегируя специфические детали подклассам. Это позволяет сделать код более удобочитаемым, модульным и легким для поддержки. Особенно это полезно, когда у нас есть большое количество классов с похожим поведением, но различающихся в некоторых деталях, как в случае с различными HTML-элементами.
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.