не совсем понимаю, почему у меня есть интерфейс в классе, а не наследовать от него - PullRequest
0 голосов
/ 23 мая 2018

Я просматриваю принципы SOLID и наткнулся на некоторые фрагменты кода для SRP.

У меня есть этот код, но я не понимаю, зачем мне интерфейс, объявленный вКстати, что я делаю ниже?Что хорошего это делает для меня?Я нашел здесь , оно показывает решение, но на самом деле не объясняет, как код работает лучше или почему все так, как есть.

То, чего я не понимаю, это:

IGateUtility _gateUtility; В public class ServiceStation, а прямо под ним находится конструктор с IGateUtility в качестве параметра.Почему это написано так?Какой параметр я должен был бы передать.

public class ServiceStation
{
    IGateUtility _gateUtility;

    public ServiceStation(IGateUtility gateUtility)
    {
        this._gateUtility = gateUtility;
    }
    public void OpenForService()
    {
        _gateUtility.OpenGate();
    }

    public void DoService()
    {
        //Check if service station is opened and then
        //complete the vehicle service
    }

    public void CloseForDay()
    {
        _gateUtility.CloseGate();
    }
}

public class ServiceStationUtility : IGateUtility
{
    public void OpenGate()
    {
        //Open the shop if the time is later than 9 AM
    }

    public void CloseGate()
    {
        //Close the shop if the time has crossed 6PM
    }
}


public interface IGateUtility
{
    void OpenGate();

    void CloseGate();
}

Ответы [ 3 ]

0 голосов
/ 24 мая 2018

SRP означает Принцип единой ответственности .Итак, Класс обслуживания не несет никакой ответственности за создание объекта ServiceStationUtility .

Давайте разберемся с этим на примере.Предположим, что вы не используете какой-либо интерфейс, тогда ваш код выглядит следующим образом.

    public class ServiceStation
    {
      ServiceStationUtility  _gateUtility;

    public ServiceStation()
    {
        this._gateUtility = new ServiceStationUtility();
    }
    public void OpenForService()
    {
        _gateUtility.OpenGate();
    }

    public void DoService()
    {
        //Check if service station is opened and then
        //complete the vehicle service
    }

    public void CloseForDay()
    {
        _gateUtility.CloseGate();
    }
     }

   public class ServiceStationUtility 
   {
       public void OpenGate()
       {
        //Open the shop if the time is later than 9 AM
       }

      public void CloseGate()
      {
        //Close the shop if the time has crossed 6PM
      }
    }

Таким образом, без использования интерфейса теперь класс Service Station также отвечает за создание объектов ServiceStation , которыеявляется нарушением SRP .

Вторая проблема

С приведенным выше кодом является то, что если вы хотите предоставить другую реализацию для OpenGate () и CloseGate () .Вы должны создать другой класс.Из-за этого вам необходимо снова изменить код в ServiceStation классе.

Причина использования интерфейса

Интерфейсы позволяют вводить зависимости.это означает, что задача создания объекта делегирована третьей стороне.это означает, что здесь ServiceClass не нужно знать, кто является фактическим поставщиком объекта. ServiceClass просто следуйте здесь интерфейсу.То же самое для ServiceStationUtility класса, этот класс также не знает, кто будет его использовать.

Таким образом, интерфейсы помогают в построении Слабосвязанная архитектура

0 голосов
/ 24 мая 2018

Есть несколько шагов, прежде чем мы достигнем того класса, который у вас сейчас есть.

Шаг 1

ServiceStation тесно связан с ServiceStationUtility.В результате мы не смогли выполнить юнит-тестирование ServiceStation.

public class ServiceStation
{
    ServiceStationUtility _gateUtility;

    public ServiceStation()
    {
        this._gateUtility = new ServiceStationUtility();
    }

    public void OpenForService()
    {
        _gateUtility.OpenGate();
    }

    public void DoService() 
   {
   }

    public void CloseForDay()
    {
        _gateUtility.CloseGate();
    }
}

Шаг 2

Мы внедряем зависимости через construction injection pattern, и мы можем каким-то образом реализовать юнит-тесты, но ServiceStation все еще тесно связан с ServiceStationUtility.

public class ServiceStation
{
    ServiceStationUtility _gateUtility;

    public ServiceStation(ServiceStationUtility gateUtility)
    {
        this._gateUtility = gateUtility;
    }

    public void OpenForService()
    {
        _gateUtility.OpenGate();
    }

    public void DoService()
    {
        //Check if service station is opened and then
        //complete the vehicle service
    }

    public void CloseForDay()
    {
        _gateUtility.CloseGate();
    }
}

Шаг 3

Принцип замещения Лискова - Подкласс должен быть взаимозаменяемым с суперклассом.

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

И наконец то, что у вас есть.Это в основном удовлетворяет принципам SO_ID;Принцип замены Лискова в этом случае не требуется.

public class ServiceStation
{
    IGateUtility _gateUtility;

    public ServiceStation(IGateUtility gateUtility)
    {
        this._gateUtility = gateUtility;
    }

    public void OpenForService()
    {
        _gateUtility.OpenGate();
    }

    public void DoService()
    {
        //Check if service station is opened and then
        //complete the vehicle service
    }

    public void CloseForDay()
    {
        _gateUtility.CloseGate();
    }
}

public interface IGateUtility
{
    void OpenGate();

    void CloseGate();
}

Образцы модульных тестов

Класс, который зависит от интерфейса, больше ни от чего не зависит.Эта абстракция обеспечивает слабую связь и позволяет легко выполнять юнит-тесты.

using Moq;
using Xunit;

public class ServiceStationTests
{
    [Fact]
    public void OpenForService_should_call_OpenGate_once()
    {
        // Arrange
        var mockGateUtility = new Mock<IGateUtility>();
        mockGateUtility.Setup(x => x.OpenGate());

        // Act
        var sut = new ServiceStation(mockGateUtility.Object);
        sut.OpenForService();

        // Assert
        mockGateUtility.Verify(x => x.OpenGate(), Times.Once);
    }

    [Fact]
    public void CloseForDay_should_call_CloseGate_once()
    {
        // Arrange
        var mockGateUtility = new Mock<IGateUtility>();
        mockGateUtility.Setup(x => x.CloseGate());

        // Act
        var sut = new ServiceStation(mockGateUtility.Object);
        sut.CloseForDay();

        // Assert
        mockGateUtility.Verify(x => x.CloseGate(), Times.Once);
    }
}
0 голосов
/ 23 мая 2018

Параметр конструктора является примером внедрения зависимостей , в частности, техники, известной как "инъекция в конструктора" (которая обычно является предпочтительной методикой).

ServiceStation недолжен содержать логику IGateUtility, потому что он не имеет ничего общего с воротами (принцип единой ответственности).Однако необходимо использовать ворота, поэтому вы передаете объект, реализующий IGateUtility in.

Вообще говоря, я не думаю, что наследование имело бы смысл в этом случае независимо от этого;но есть принцип, который гласит:

Предпочитают композицию наследованию

Что в основном означает;внедрять (создавать) объекты, чтобы получить доступ к их поведению, а не наследовать от них.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...