Шаблонный метод (Template Method)
KarmanovichDevОпределение из wiki:
Поведенческий шаблон проектирования, определяющий основу алгоритма и позволяющий наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.
С определением проблем нет, всё чётко и ясно:
- Есть алгоритм действий(в базовом классе), который не должен меняться.
- Чтобы поехать на машине, сначала нужно открыть дверь, сесть в неё, закрыть дверь, завести и только потом поехать.
- Чтобы приготовить горячий напиток, нужно нагреть воду, налить её в чашку, добавить напиток(чай, кофе и т. п.) и только потом пить.
В примерах выше я попытался показать суть того, что есть некий алгоритм действий, который остаётся неизменным.
2. Наследники могут менять поведение конкретных шагов, но не алгоритм в целом.
- Представим, что в абстрактном базовом классе(Car) уже есть реализация всех/части этих шагов(открыть дверь и т. д.) для среднестатистической машины, которой для того, чтобы её завести нужен ключ. Такая реализация подойдёт, например, для классов PrioraCar или GazelCar.
Если появится иномарка Porsche911Car, то нам нужно будет переопределить метод, который должен завести машину, ведь у Porsche будет кнопка запуска, а не замок зажигания.
Также если взять LamborghiniCar, то вдобавок нужно будет переопределить методы для открытия и закрытия дверей, ведь они открываются вверх.
Заметьте, что наследники переопределяют лишь реализацию шагов алгоритма, сам алгоритм остаётся неизменным, мы всё так же должны сначала открыть двери, сесть в машину, закрыть двери, завести её, и только потом поехать, и никак иначе. - С горячим напитком всё точно так же. В абстрактном базовом классе(HotDrink) уже могут быть реализованы все шаги алгоритма, кроме добавления напитка в этом случае, так как, что делать на этом шаге будут решать конкретные классы.
CoffeHotDrink - нужно будет помешать в момент добавления,
TeeHotDrink - нужно будет подержать пакетику чая какое-то время,
или даже WatterHotDrink где шаг добавления может быть пропущен(pass).
И снова, как видите, наследники лишь меняют реализацию отдельных шагов алгоритма, сам же алгоритм остаётся неизменным.
Схема паттерна(UML):

- Клиент всегда взаимодействует лишь с открытым API класса get_price, внутри которого описан алгоритм вызова остальных методов в нужном порядке. Дочерние классы не должны переопределять get_price, иначе алгоритм будет нарушен, а в этом и есть смысл паттерна(определить базовый алгоритм действий).
Если судить по конкретной схеме, то, скорее всего, _apply_discount может быть реализован сразу в базовом абстрактном классе(скорее всего, логика применения скидки будет одинакова в большинстве случаев), в то же время каждый класс, реализующий этот интерфейс сам будет решать откуда(БД, сеть, диск) взять данные, и где получить скидку(БД, сеть, диск).
Дополнительная информация на boosty:
1. Когда можно применять паттерн
2. Недостатки паттерна
3. Примеры кода на основе схемы(UML) и реального примера с объяснениями.