воскресенье, 3 января 2016 г.

Dependency Inversion

Dependency Inversion-один из принципов SOLID:
  • Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции.
  • Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Для тех, кто только начинает знакомиться  с  Dependency Inversion, такое определение не понятно. Зато эта тема весьма популярна на собеседование. Попробую объяснить простым языком и легким примером "из жизни".

В проекте есть два класса, которые создают сообщение и отправляют его определенным адресатам.

    public class MessageToMainOffice
    {
        public void Send()
        {
            //создаем сообщение для главного офиса и отправляем на заданные адреса
        }
    }


    public class MessageToTechnicalOffice
    {
        public void Send()
        {
            //создаем сообщение для технического отдела и отправляем на заданные адреса
        }
    }
Также есть класс, который именно использует один из таких классов

 public class FeedbackGenerator
{  
      public void Generate(MessageToTechnicalOffice messageType){
            //какой-то код
            if(messageType!=null)
                 //вызов метода
               messageType.Send();
      }
}

 Если есть уже два класса по созданию писем, то возможно появиться и третий и четвертый, а может и больше.
 А если вам понадобиться сначала использовать класс MessageToTechnicalOffice для отправки сообщения, а потом требования изменяться и нужно будет использовать класс MessageToMainOffice. Будете ли вы создавать отдельный метод в классе FeedbackGenerator, который принимает экземпляр MessageToMainOffice? А потом еще по всему проекту будете искать везде места, где использовался один метод и менять его на другой? Если для вас это обычная практика, то советую все таки применить Dependency Inversion.

Решение.
Класс FeedbackGenerator не должен меняться относительно применяемого класса по отправке сообщений.
1. Сначала выделим интерфейс у классов:

public interface IMessageType
{
       void Send();
}

public class MessageToMainOffice:IMessageType
{
        public void Send(){
           //создаем сообщение для главного офиса и отправляем на заданные адреса
       }
}

public class MessageToTechnicalOffice:IMessageType
{
      public void Send(){
         //создаем сообщение для главного офиса и отправляем на заданные адреса
       }
}

Грубо говоря, выполнили второй пункт Dependency Inversion. 

2. Создадим конструктор у класса FeedbackGenerator

public class FeedbackGenerator
{
      private IMessageType _messageType;

      public FeedbackGenerator(IMessageType messageType){
          _messageType=messageType;
     }

      public void Generate(){
             //какой-то код
            if(_messageType!=null)
                 //вызов метода
               _messageType.Send();
      }
}

Выполнили первый пункт Dependency Inversion.

Теперь, если у вас в проекте появятся еще классы, реализующие  IMessageType интерфейс, сам класс FeedbackGenerator меняться не будет. Удобно, не так ли? Плюс ко всему теперь можно будет легко протестировать сам класс FeedbackGenerator. А если вы хотите, что бы система сама решала какую реализацию IMessageType использовать и что бы вы не пользовались строкой  if(_messageType!=null) (проверка на нулевой объект), то можете смотреть в сторону Dependency Injection и Ioc контейнеров. Об этом, простым языком, я напишу тут, но чуть позже.

Комментариев нет:

Отправить комментарий