Паттерн "Шаблонный метод"

2017-07-10 • edited 2021-02-06

Паттерн “Шаблонный метод” (Template Method1 )

Ссылки на описания остальных паттернов расположены здесь .

Содержание

Введение Необходимость паттерна “Шаблонный метод” возникает

  • Когда известен общий ход работы алгоритма, однако, реализация каждого из его этапов может различаться..

По сути, паттерн шаблонный метод является частным случаем стратегии с тем отличием, что в случае шаблонного метода подменяется не сам алгоритм, а только детали его реализации. Классический подход к реализации этого паттерна заключается в выделении базового класса с шаблонным методом, детали реализации которого описываются в наследниках базового класса. Именно эта реализация будет использоваться в следующем примере, но существуют и другие способы реализации этого паттерна, например в C# это можно сделать с помощью делегатов (подробно это описано в [2, с. 40] ).

Ниже представлена UML-диаграмма данного паттерна.

templateUml

Генерация имени в различных языках.

В данном примере воспользуемся паттерном “Шаблонный метод” для генерации имени в следующих языках:

  • Английски.
  • Русский.
  • Немецкий.

Реализации

**C#**

Базовый класс генерации имен. Шаблонный метод тут - GetName. Методы GenerateFirstName и GenerateSurname отвечают за детали реализации алгоритма генерации имён.

public enum Gender
{
    Male,
    Female
}

public abstract class BaseNameGenerator
{
    protected  Random _random = new Random();

    private Gender GetRandomGender()
    {
        switch(_random.Next(0, 2))
        {
            case 0:
                return Gender.Male;
            case 1:
                return Gender.Female;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }

    protected abstract string GenerateFirstName(Gender gender);

    protected abstract string GenerateSurname(Gender gender);

    public string GetName()
    {
        var gender = GetRandomGender();
        return $"{GenerateFirstName(gender)}  {GenerateSurname(gender)}";
    }
}

Генерация русских имён.

public class RussianNameGenerator : BaseNameGenerator
{
    private readonly string[] _firstMaleNames = new string[] { "Антон", "Олег", "Роман", "Виктор" };
    private readonly string[] _firstFemaleNames = new string[] { "Виктория", "Светлана", "Катерина", "Ольга" };
    private readonly string[] _secondNames = new string[] { "Петров", "Семенов", "Иванов", "Борисов" };

    protected override string GenerateFirstName(Gender gender)
    {
        switch (gender)
        {
            case Gender.Male:
                return _firstMaleNames[_random.Next(0, _firstMaleNames.Length)];
            case Gender.Female:
                return _firstFemaleNames[_random.Next(0, _firstFemaleNames.Length)];
            default:
                throw new ArgumentOutOfRangeException();
        }

    }

    protected override string GenerateSurname(Gender gender)
    {
        switch (gender)
        {
            case Gender.Male:
                return _secondNames[_random.Next(0, _secondNames.Length)];
            case Gender.Female:
                return $"{_secondNames[_random.Next(0, _secondNames.Length)]}а";
            default:
                throw new ArgumentOutOfRangeException();
        }

    }
}

Генерация английских имён.

public class EnglishNameGenerator: BaseNameGenerator
{
    private readonly string[] _firstMaleNames = new string[] { "Jim", "Richard", "Ted", "Robert", "Danny" };
    private readonly string[] _firstFemaleNames = new string[] { "Kate", "Susan", "Victoria", "Diana" };
    private readonly string[] _secondNames = new string[] { "Smith", "Black", "Robinson", "Rand" };

    protected override string GenerateFirstName(Gender gender)
    {
        switch (gender)
        {
            case Gender.Male:
                return _firstMaleNames[_random.Next(0, _firstMaleNames.Length)];
            case Gender.Female:
                return _firstFemaleNames[_random.Next(0, _firstFemaleNames.Length)];
            default:
                throw new ArgumentOutOfRangeException();
        }

    }

    protected override string GenerateSurname(Gender gender)
    {
        return _secondNames[_random.Next(0, _secondNames.Length)];
    }
}

Генерация немецких имён.

public class GermanNameGenerator : BaseNameGenerator
{
    private readonly string[] _firstMaleNames = new string[] { "Hans", "Heinz", "Horst", "Andreas" };
    private readonly string[] _firstFemaleNames = new string[] { "Anna", "Elke", "Birgit", "Stefanie" };
    private readonly string[] _secondNames = new string[] { "Müller", "Schröder", "Hartmann", "Schulze" };

    protected override string GenerateFirstName(Gender gender)
    {
        switch (gender)
        {
            case Gender.Male:
                return _firstMaleNames[_random.Next(0, _firstMaleNames.Length)];
            case Gender.Female:
                return _firstFemaleNames[_random.Next(0, _firstFemaleNames.Length)];
            default:
                throw new ArgumentOutOfRangeException();
        }

    }

    protected override string GenerateSurname(Gender gender)
    {
        return _secondNames[_random.Next(0, _secondNames.Length)];
    }
}

Используем реализованную функциональность.

private static void PrintRandomName(BaseNameGenerator generator)
{
    Console.WriteLine($"Generated name: {generator.GetName()}");
}

static void Main(string[] args)
{
    Console.OutputEncoding = Encoding.UTF8;

    Console.WriteLine($"Generating russian name.");
    PrintRandomName(new RussianNameGenerator());
    Console.WriteLine($"Generating english name.");
    PrintRandomName(new EnglishNameGenerator());
    Console.WriteLine($"Generating german name.");
    PrintRandomName(new GermanNameGenerator());
}

Пример работы программы

Generating russian name.
Generated name: Виктор  Иванов
Generating english name.
Generated name: Danny  Robinson
Generating german name.
Generated name: Andreas  Hartmann

**Python**

Базовый класс генерации имен. Шаблонный метод тут - get_name. Методы generate_first_name и generate_surname отвечают за детали реализации алгоритма генерации имён.

class Gender(Enum):
    MALE = 1
    FEMALE = 2


class BaseNameGenerator:
    @staticmethod
    def get_random_gender() -> Gender:
        if random.randint(1, 2) == 1:
            return Gender.MALE
        return Gender.FEMALE

    def generate_first_name(self, gender: Gender) -> str:
        pass

    def generate_surname(self, gender: Gender) -> str:
        pass

    def get_name(self)-> str:
        gender = self.get_random_gender()
        return self.generate_first_name(gender) + " " + self.generate_surname(gender)

Генерация русских имён.

class RussianNameGenerator(BaseNameGenerator):
    __first_male_names = ["Антон", "Олег", "Роман", "Виктор"]
    __first_female_names = ["Виктория", "Светлана", "Катерина", "Ольга"]
    __surnames = ["Петров", "Семенов", "Иванов", "Борисов"]

    def generate_first_name(self, gender: Gender) -> str:
        if gender == Gender.MALE:
            return self.__first_male_names[random.randint(0, len(self.__first_male_names)-1)]
        return self.__first_female_names[random.randint(0, len(self.__first_female_names)-1)]

    def generate_surname(self, gender: Gender) -> str:
        if gender == Gender.MALE:
            return self.__surnames[random.randint(0, len(self.__surnames)-1)]
        return self.__surnames[random.randint(0, len(self.__surnames)-1)]+"а"

Генерация английских имён.

class EnglishNameGenerator(BaseNameGenerator):
    __first_male_names = ["Jim", "Richard", "Ted", "Robert", "Danny"]
    __first_female_names = ["Kate", "Susan", "Victoria", "Diana"]
    __surnames = ["Smith", "Black", "Robinson", "Rand"]

    def generate_first_name(self, gender: Gender) -> str:
        if gender == Gender.MALE:
            return self.__first_male_names[random.randint(0, len(self.__first_male_names)-1)]
        return self.__first_female_names[random.randint(0, len(self.__first_female_names)-1)]

    def generate_surname(self, gender: Gender) -> str:
        return self.__surnames[random.randint(0, len(self.__surnames)-1)]

Генерация немецких имён.

class GermanNameGenerator(BaseNameGenerator):
    __first_male_names = ["Hans", "Heinz", "Horst", "Andreas"]
    __first_female_names = ["Anna", "Elke", "Birgit", "Stefanie"]
    __surnames = ["Müller", "Schröder", "Hartmann", "Schulze"]

    def generate_first_name(self, gender: Gender) -> str:
        if gender == Gender.MALE:
            return self.__first_male_names[random.randint(0, len(self.__first_male_names)-1)]
        return self.__first_female_names[random.randint(0, len(self.__first_female_names)-1)]

    def generate_surname(self, gender: Gender) -> str:
        return self.__surnames[random.randint(0, len(self.__surnames)-1)]

Используем реализованную функциональность.

def print_random_name(generator: BaseNameGenerator):
    print(generator.get_name())

print("Generating russian name.")
print_random_name(RussianNameGenerator())
print("Generating english name.")
print_random_name(EnglishNameGenerator())
print("Generating german name.")
print_random_name(GermanNameGenerator())

Пример работы программы

Generating russian name.
Светлана Петрова
Generating english name.
Robert Rand
Generating german name.
Horst Schröder

Весь код использованный в проекте доступен на GitHub .

Литература

[1]: Template Method Design Pattern [2]: Тепляков С. Паттерны проектирования на платформе .NET – СПб.: Питер, 2015. — 37-57 c. [3]: Design Patterns: Elements of Reusable Object-Oriented Software

design patterndesign patternbehavioralCsharpPythontemplate methodшаблонный методповеденческий шаблон
License: MIT

Паттерн "Стратегия"

Паттерн "Посетитель"

comments powered by Disqus