Паттерны проектирования: Стратегия(Strategy)

Новая версия данной статьи.

Думаю, не стоит писать, что же такое паттерны. Они на слуху у всех программистов. Однако, я заметил, что в последнее время кроме MVC и MVVM я ничего не использую. По большей части это из-за специфики разрабатываемых проектов. Но, так как они не используются, то и знания о них забываются и в дальнейшем, когда будет необходимо применить тот или иной паттерн, могут возникнуть сложности.
Чтобы этого не произошло, я решил повторить самые распространённые паттерны заново. Начну я с паттерна стратегия.
Стратегия(Strategy) – это поведенческий шаблон проектирования. Он предназначен для определения семейства алгоритмов, их инкапсуляции и обеспечения взаимозаменяемости.
Задача: Задачей данного паттерна является выбор подходящего алгоритма, на основе обрабатываемых данных или типу клиента.

Простейшая схема работы паттерна:


Обычно, данный паттерн объясняется на примере уток, когда утки должны обладать определёнными свойствами (уметь летать, крякать, плавать), но каждый вид уток может обладать только определённым набором качеств. Я попробую отойти от уток и придумать что-то своё, более приближенное к реальной жизни, но при этом оставаясь учебным проектом, достаточно простым для понимания.
Проблема будет такая: на сайте есть группа пользователей, каждый из них принадлежит к определённой группе, например это администратор, редактор и гость. Каждая группа имеет свои особенности. Администраторы могут добавлять материал, удалять материал, редактировать и читать. Редакторы могут только редактировать и читать. А гости только читать. Все пользователи могут читать материал на сайте, это общая возможность для всех пользователей, остальные же возможности зависят от вида пользователя.
Для начала напишем класс User - это будет не окончательный вариант, а наброски, необходимые для дальнейшей работы.

 
public abstract class User
    {
        public void Read()
        {
            Console.Write("I read...");
        }

        public abstract void Who();
    }

При вызове функции Who будет показана информация и типе текущего пользователя, при вызове Read отобразиться сообщения “I read”. Теперь необходимо разобраться с остальными правами. Рассмотрим возможность добавления материалов на сайт. Создадим интерфейс IAdding следующего вида.

 
  public interface IAdding
    {
        void Add();
    }

Добавим клаcc NoAdd, который будет оповещать, что пользователю запрещено добавлять материалы, наследуем его от интерфейса IAdding.

 
public class NoAdd:IAdding
    {
        public void Add()
        {
            Console.WriteLine("User can not add");
        }
    }

Теперь добавляем класс Adding, который также наследуется от IAdding, но функция Add при этом позволяет пользователю добавлять материалы.

 
public class Adding:IAdding
    {
        public void Add()
        {
            Console.WriteLine("Successfully added");
        }
    }

Классы для редактирования и удаления будет аналогичными, я не буду их описывать. Теперь пора доработать класс User.

 
public abstract class User
    {
        protected IAdding addBehaviour;
        protected IEditing editBehaviour;
        protected IRemoving removeBehaviour;

        public User()
        {
            addBehaviour = new NoAdd();
            editBehaviour = new NoEdit();
            removeBehaviour = new NoRemove();
        }

        public void Read()
        {
            Console.WriteLine("I read...");
        }

        public void Add()
        {
            addBehaviour.Add();
        }

        public void Edit()
        {
            editBehaviour.Edit();
        }

        public void Remove()
        {
            removeBehaviour.Remove();
        }

        public abstract void Who();
    }

Теперь добавляем классы самих пользователей. Класс администратора:

 
public class Administrator:User
    {
        public Administrator()
        {
            addBehaviour = new Adding();
            editBehaviour = new Editing();
            removeBehaviour = new Removing();
        }

        public override void Who()
        {
            Console.WriteLine("I am administrator!");
        }
    }

Класс класса редактора:

 
public class Editor:User
    {
        public Editor()
        {
            editBehaviour = new Editing();
        }

        public override void Who()
        {
           Console.WriteLine("I am editor...");
        }
    }

Класс гостя:

 
 public class Guest:User
    {
        public override void Who()
        {
            Console.WriteLine("I am guest...hello...");
        }
    }

Осталось протестировать работу, для этого я использовал следующий код:

 
            var users = new List();
            users.Add(new Administrator());
            users.Add(new Editor());
            users.Add(new Guest());

            foreach (var user in users)
            {
                user.Who();
                user.Read();
                user.Add();
                user.Edit();
                user.Remove();

                Console.WriteLine("------------");
            }

Результат:

Это простейшая демонстрация принципов работы паттерна «Стратегия».
Как итог можно сказать следующее: при использовании стратегии нужно отделять постоянные части от изменяемых, отдавать предпочтение композиции, а не наследованию, программировать необходимо на уровне интерфейса, а не реализации.
Исходный код учебного проекта.

Комментарии (2) -

Бесспорно, хороший вариант своей интерпретации шаблона с абстрагированием от модели объяснения на утках. Так держать, мне понравилось - просто и понятно!

Отличный пример паттерна

Добавить комментарий