Шаблонный метод (шаблон проектирования)
Шаблонный метод | |
---|---|
Template method | |
Тип | поведенческий |
Структура | |
Описан в Design Patterns | Да |
Шаблонный метод (англ. Template method) — поведенческий шаблон проектирования, определяющий основу алгоритма и позволяющий наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.
Применимость
- Однократное использование инвариантной части алгоритма, с оставлением изменяющейся части на усмотрение наследникам.
- Локализация и вычленение общего для нескольких классов кода для избегания дублирования.
- Разрешение расширения кода наследниками только в определенных местах.
Участники
Abstract class (абстрактный класс) - определяет абстрактные операции, замещаемые в наследниках для реализации шагов алгоритма; реализует шаблонный метод, определяющий скелет алгоритма. Шаблонный метод вызывает замещаемые и другие, определенные в Abstract class, операции.
Concrete class (конкретный класс) - реализует замещаемые операции необходимым для данной реализации способом.
Concrete class предполагает, что инвариантные шаги алгоритма будут выполнены в AbstractClass.
Примеры
В примерах шаблонный метод реализуется для игр.
C++11
Исходный текст на языке C++11
/**
* An abstract class that is common to several games in
* which players play against the оthers, but only one is
* playing at a given time.
*/
class GameObject
{
protected:
int PlayersCount;
virtual bool EndOfGame() = 0;
virtual void InitializeGame() = 0;
virtual void MakePlay(int player) = 0;
virtual void PrintWinner() = 0;
public:
/* A template method: */
void PlayOneGame(int playersCount)
{
PlayersCount = playersCount;
InitializeGame();
int j = 0;
while (!EndOfGame()) {
MakePlay(j);
j = (j + 1) % playersCount;
}
PrintWinner();
}
};
class Monopoly : public GameObject
{
protected:
/* Implementation of necessary concrete methods */
void InitializeGame() override
{
// Initialize money
}
void MakePlay(int player) override
{
// Process one turn of player
}
bool EndOfGame() override
{
return true;
}
void PrintWinner() override
{
// Display who won
}
};
class Chess : public GameObject
{
protected:
/* Implementation of necessary concrete methods */
void InitializeGame() override
{
// Put the pieces on the board
}
void MakePlay(int player) override
{
// Process a turn for the player
}
bool EndOfGame() override
{
// Return true if in Checkmate or Stalemate has been reached
return true;
}
void PrintWinner() override
{
// Display the winning player
}
};
int main()
{
GameObject* game = new Monopoly();
game->PlayOneGame(2);
return 0;
}
Java
Исходный текст на языке Java
package com.designpatterns.templatemethod;
/* Коды разновидностей игр.
*
* Файл GameCode.java
* */
public enum GameCode {
CHESS,
MONOPOLY
}
/* Абстрактный класс, реализация абстрактных методов которого будет специфичной для каждого вида игры.
*
* Файл Game.java
* */
public abstract class Game {
private int playersAmount;
protected abstract void initializeGame();
protected abstract void playGame();
protected abstract void endGame();
protected abstract void printWinner();
public final void playOneGame(int playersAmount){
setPlayersAmount(playersAmount);
initializeGame();
playGame();
endGame();
printWinner();
}
public void setPlayersAmount(int playersAmount){
this.playersAmount = playersAmount;
}
}
package com.designpatterns.templatemethod;
/* Игра "Шахматы". Специфически только для шахмат реализует методы класса Game.
*
* Файл Chess.java
* */
public class Chess extends Game {
@Override
protected void initializeGame() {
// chess specific initialization actions
}
@Override
protected void playGame() {
// chess specific play actions
}
@Override
protected void endGame() {
// chess specific actions to end a game
}
@Override
protected void printWinner() {
// chess specific actions to print winner
}
}
package com.designpatterns.templatemethod;
/* Игра "Монополия". Специфически только для монополии реализует методы класса Game.
*
* Файл Monopoly.java
* */
public class Monopoly extends Game{
@Override
protected void initializeGame() {
// monopoly specific initialization actions
}
@Override
protected void playGame() {
// monopoly specific play actions
}
@Override
protected void endGame() {
// monopoly specific actions to end a game
}
@Override
protected void printWinner() {
// monopoly specific actions to print winner
}
}
package com.designpatterns.templatemethod;
/* Класс, показывающий работу шаблона проектирования "Шаблонный метод".
*
* Файл GamesManager.java
* */
public class GamesManager {
public static void main (String [] args){
final GameCode gameCode = GameCode.CHESS;
Game game;
switch (gameCode){
case CHESS :
game = new Chess();
break;
case MONOPOLY :
game = new Monopoly();
break;
default :
throw new IllegalStateException();
}
game.playOneGame(2);
}
}
C#
Исходный текст на языке C#
/**
* An abstract class that is common to several games in
* which players play against the others, but only one is
* playing at a given time.
*/
namespace Design_Patterns
{
class TemplateMethodPattern
{
internal abstract class GameObject
{
protected int PlayersCount;
abstract protected bool EndOfGame();
abstract protected void InitializeGame();
abstract protected void MakePlay(int player);
abstract protected void PrintWinner();
/* A template method : */
public void PlayOneGame(int playersCount)
{
PlayersCount = playersCount;
InitializeGame();
var j = 0;
while (!EndOfGame())
{
MakePlay(j);
j = (j + 1) % playersCount;
}
PrintWinner();
}
}
//Now we can extend this class in order to implement actual games:
public class Monopoly : GameObject
{
/* Implementation of necessary concrete methods */
protected override void InitializeGame()
{
// Initialize money
}
protected override void MakePlay(int player)
{
// Process one turn of player
}
protected override bool EndOfGame()
{
return true;
}
protected override void PrintWinner()
{
// Display who won
}
/* Specific declarations for the Monopoly game. */
// ...
}
public class Chess : GameObject
{
/* Implementation of necessary concrete methods */
protected override void InitializeGame()
{
// Put the pieces on the board
}
protected override void MakePlay(int player)
{
// Process a turn for the player
}
protected override bool EndOfGame()
{
return true;
// Return true if in Checkmate or Stalemate has been reached
}
protected override void PrintWinner()
{
// Display the winning player
}
/* Specific declarations for the chess game. */
// ...
}
public static void Test()
{
GameObject game = new Monopoly();
game.PlayOneGame(2);
}
}
}
Python
Исходный текст на языке Python
from abc import ABCMeta, abstractmethod
class Unit(metaclass=ABCMeta):
"""
Абстрактный отряд. Атрибуты класса, начинающиеся с подчеркивания в python
являются protected
"""
def __init__(self, speed: int) -> None:
"""
Constructor.
:param speed: скорость отряда
"""
self._speed = speed
def hit_and_run(self) -> None:
"""
Шаблонный метод
"""
self._move('вперед')
self._stop()
self._attack()
self._move('назад')
@abstractmethod
def _attack(self) -> None:
pass
@abstractmethod
def _stop(self) -> None:
pass
def _move(self, direction: str) -> None:
"""
Передвижение - у всех отрядов одинаковое, в шаблон не входит
:param direction: направление движения
"""
self._output('движется {} со скоростью {}'.format(direction, self._speed))
def _output(self, message: str) -> None:
"""
Вспомогательный метод вывода сообщений, в шаблон не входит
:param message: выводимое сообщение
"""
print('Отряд типа {} {}'.format(self.__class__.__name__, message))
class Archers(Unit):
"""
Лучники
"""
def _attack(self) -> None:
self._output('обстреливает врага')
def _stop(self) -> None:
self._output('останавливается в 100 шагах от врага')
class Cavalrymen(Unit):
"""
Кавалеристы
"""
def _attack(self) -> None:
self._output('на полном скаку врезается во вражеский строй')
def _stop(self) -> None:
self._output('летит вперед, не останавливаясь')
if __name__ == '__main__':
print('OUTPUT:')
archers = Archers(4)
archers.hit_and_run()
cavalrymen = Cavalrymen(8)
cavalrymen.hit_and_run()
'''
OUTPUT:
Отряд типа Archers движется вперед со скоростью 4
Отряд типа Archers останавливается в 100 шагах от врага
Отряд типа Archers обстреливает врага
Отряд типа Archers движется назад со скоростью 4
Отряд типа Cavalrymen движется вперед со скоростью 8
Отряд типа Cavalrymen летит вперед, не останавливаясь
Отряд типа Cavalrymen на полном скаку врезается во вражеский строй
Отряд типа Cavalrymen движется назад со скоростью 8
'''
Литература
- Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес. Приемы объектно-ориентированного проектирования. Паттерны проектирования = Design Patterns: Elements of Reusable Object-Oriented Software. — СПб.: «Питер», 2007. — С. 366. — ISBN 978-5-469-01136-1. (также ISBN 5-272-00355-1)
Ссылки
- Паттерн Template Method (шаблонный метод) — назначение, описание, особенности и реализация на C++.