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

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

Паттерн “Посетитель” (Visitor1 )

Содержание

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

  • Когда требуется разделить данные и способы их обработки.

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

stragUml

Валидация объектов

В данном примере рассмотрим пример задачи, вероятно очень актуальной во вселенной “Кошек против собак”, а именно фейсконтроля на входе в Собачий/Кошачий бар. Вообще говоря, это пример более общей задачи - валидации данных.

Реализации

**C#**

Родительский класс для всех животных.

abstract class Animal
{
    public string Name { get; set; }

    public abstract bool Accept(ISecurity security);
}

Класс, описывающий кошек.

class Cat: Animal
{
    public override bool Accept(ISecurity security)
    {
        return security.Validate(this);
    }
}

Класс, описывающий собак.

class Dog: Animal
{
    public override bool Accept(ISecurity security)
    {
        return security.Validate(this);
    }
}

Интерфейс описывающий действия охраны. Недостатоком данного подхода является то, что при появлении третьего типа животных придётся описывать метод Validate и для них, этого можно избежать, если заменить все методы Validate на единый - bool Validate(Animal animal), однако в этом случае внутри этого метода пришлось бы делать проверку на типы, что привело бы к нагромождению кода в одной функции, вместо явного его разделения.

interface ISecurity
{
    bool Validate(Cat cat);
    bool Validate(Dog dog);
}

Охранники в кошачьем клубе пропускают котов, но не пускают собак.

class CatClubSecurity : ISecurity
{
    public bool Validate(Cat cat)
    {
        return true;
    }

    public bool Validate(Dog dog)
    {
        return false;
    }
}

Охранники в собачьем клубе пропускают собак, но не пускают котов.

class DogClubSecurity : ISecurity
{
    public bool Validate(Cat cat)
    {
        return false;
    }

    public bool Validate(Dog dog)
    {
        return true;
    }
}

Примени реализованный функционал.

class Program
{
    static void Main(string[] args)
    {
        var cat = new Cat
        {
            Name = "Kitty"
        };

        var dog = new Dog
        {
            Name = "Dogmeat"
        };

        Console.WriteLine($"{cat.Name} was {(cat.Accept(new DogClubSecurity()) ? "valid" : "not valid")} for entering the DogClub");
        Console.WriteLine($"{dog.Name} was {(dog.Accept(new DogClubSecurity()) ? "valid" : "not valid")} for entering the DogClub");
        Console.WriteLine($"{cat.Name} was {(cat.Accept(new CatClubSecurity()) ? "valid" : "not valid")} for entering the CatClub");
        Console.WriteLine($"{dog.Name} was {(dog.Accept(new CatClubSecurity()) ? "valid" : "not valid")} for entering the CatClub");
    }
}

Результат работы программы.

Kitty was not valid for entering the DogClub
Dogmeat was valid for entering the DogClub
Kitty was valid for entering the CatClub
Dogmeat was not valid for entering the CatClub

**Python**

Родительский класс для всех животных.

class Animal:
    Name = ""

    def __init__(self, name: str):
        self.Name = name

    def accept(self, security) -> bool:
        pass

Класс, описывающий кошек.

from Animals.Animal import Animal


class Cat(Animal):
    def accept(self, security) -> bool:
        return security.validate(self)

Класс, описывающий собак.

from Animals.Animal import Animal


class Dog(Animal):
    def accept(self, security) -> bool:
        return security.validate(self)

Абстрактный класс, описывающий действия охраны.

from Animals.Animal import Animal


class Security:
    def validate(self, animal: Animal) -> bool:
        pass

Охранники в кошачьем клубе пропускают котов, но не пускают собак.

from Animals.Animal import Animal
from Animals.Dog import Dog
from Security.Security import Security


class CatClubSecurity(Security):
    def validate(self, animal: Animal) -> bool:
        if isinstance(animal, Dog):
            return False
        return True

Охранники в собачьем клубе пропускают собак, но не пускают кошек.

from Animals.Cat import Cat
from Animals.Animal import Animal
from Security.Security import Security


class DogClubSecurity(Security):
    def validate(self, animal: Animal) -> bool:
        if isinstance(animal, Cat):
            return False
        return True

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

from Security.CatClubSecurity import CatClubSecurity
from Security.DogClubSecurity import DogClubSecurity
from Animals.Cat import Cat
from Animals.Dog import Dog

cat = Cat("Kitty")
dog = Dog("Dogmeat")

print(cat.Name + " was " + ("valid" if cat.accept(DogClubSecurity()) else "not valid") + " for entering the DogClub")
print(dog.Name + " was " + ("valid" if dog.accept(DogClubSecurity()) else "not valid") + " for entering the DogClub")
print(cat.Name + " was " + ("valid" if cat.accept(CatClubSecurity()) else "not valid") + " for entering the CatClub")
print(dog.Name + " was " + ("valid" if dog.accept(CatClubSecurity()) else "not valid") + " for entering the CatClub")

Пример результатов работы скрипта.

Kitty was not valid for entering the DogClub
Dogmeat was valid for entering the DogClub
Kitty was valid for entering the CatClub
Dogmeat was not valid for entering the CatClub

**JS [by xaota](https://github.com/xaota)**
class Animal {
   constructor(name) {
    this.name = name;
   }
   accept(security) {
      return security.validate(this);
   }
   static is(object) {
    return object instanceof Animal;
   }
 }
 
 class Cat extends Animal {
   static is(object) {
    return object instanceof Cat;
   }
 }
 
 class Dog extends Animal {
    static is(object) {
     return object instanceof Dog;
    }
 }
 
 class AnimalSecurity {
    validate(animal) {
     return Animal.is(animal);
    }
 }
 class CatSecurity extends AnimalSecurity {
   validate(animal) {
    return Cat.is(animal);
   }
 }
 class DogSecurity extends AnimalSecurity {
   validate(animal) {
      return Dog.is(animal);
   }
 }
 
 let cat = new Cat('Персик');
 let dog = new Dog('Спотти');
 
 let catSecurity = new CatSecurity;
 let dogSecurity = new DogSecurity;
 
 cat.accept(catSecurity);
 cat.accept(dogSecurity);
 dog.accept(catSecurity);
 dog.accept(dogSecurity);

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

Литература

[1]: Visitor pattern from Wiki [2]: Тепляков С. Паттерны проектирования на платформе .NET – СПб.: Питер, 2015. — 100-112 c. [4]: Design Patterns: Elements of Reusable Object-Oriented Software

поведенческий шаблонCsharpbehavioraldesign patternPythonvisitorпосетитель
License: MIT

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

Паттерн "Посредник"

comments powered by Disqus