Является ли плохой практикой устанавливать зависимости в NULL в контейнере IoC и предоставлять зависимости во время выполнения? - PullRequest
0 голосов
/ 26 сентября 2018

У меня есть класс SocketManager, который содержит Socket и другие поля.Все поля, кроме Socket, могут быть введены во время создания графа объекта с помощью DI-фреймворка.Моя идея состояла в том, чтобы просто построить весь граф объекта заранее, оставив Socket пустым и установить его во время выполнения.Это позволило бы мне завершить создание экземпляра SocketManager в одном месте кода и использовать этот экземпляр во всей моей программе (поскольку он уже был установлен как зависимость через инфраструктуру DI)?Это стандартный способ «внедрения» зависимостей времени выполнения или это плохая практика?
Абстрактная фабрика кажется плохой идеей по двум причинам: а) она каждый раз создает новый объект; б) ей требуются параметры времени выполнения на каждомместо, где я хочу создать объект

Позвольте мне проиллюстрировать мою проблему:

Класс SocketManager:

public class SocketManager {
    //i'll only receive the socket at runtime
    Socket socket; 
    //this object is available at compile-time and can be injected through the DI container
    InjectableObject obj;
}

Где-то в моем коде [CodePosition1] я получу сокетнапример:

public class SocketCreator{
    SocketManager socketManager; //will be injected through DI container at startup
    Socket socket = this.serverSocket.accept();
    // at this point the socket manager is fully initialized
    socketManager.setSocket(socket); 
}

Во многих других местах [CodePosition2] я теперь могу использовать зависимость SocketManager

public class RandomClass {
    //injected at compile-time through DI container, but only usable after [CodePosition1]
    // was executed
    SocketManager socketManager; 
    ...
        socketManager.getSocket().doSth()
    ...
}

Проблема в том, что SocketManager не инициализируется полностью, пока [CodePosition1] во время выполнения, поэтому я не знаю другого способа, кроме как использовать init () или установщик в SocketManager для «завершения» инициализации SocketManager.Это, однако, негерметичная абстракция, как объясняется в этом посте: Существует ли шаблон для инициализации объектов, созданных с помощью контейнера DI

1 Ответ

0 голосов
/ 26 сентября 2018

Лучше всего составлять полные графы объектов с самого начала.Ввод значений null следует предотвратить, поскольку это усложняет класс потребления.

Однако в вашем случае кажется, что Socket не является "реальным" компонентом, а скорее во время выполнения.данные .Как описано здесь , вы должны запретить вставку данных времени выполнения в граф объектов во время построения.

В этой статье предлагаются два решения для решения этой проблемы, но есть и другие решения.Однако, как вы уже упоминали, абстрактные фабрики, как правило, не являются хорошим решением, а в этом посте в более общем смысле описывает проблему с абстрактными фабриками.В главе 6.2 этой книги даже более подробно рассказывается о проблеме с абстрактными фабриками с точки зрения DI.

Решением, приведенным в сообщении в блоге, является использование «контекста»абстракция.В вашем случае, например, интерфейс SocketContext, который позволяет вам получить значение времени выполнения Socket потребителем после вызова методов этого потребителя и, таким образом, после графика объекта потребителя.Например:

public interface SocketContext
{
    Socket get_CurrentSocket();
}

Другой вариант - использовать прокси-класс, который либо скрывает реальный Socket или реальный SocketManager (в зависимости от того, на каком уровне вы можете разместить прокси).Это позволяет потребителю не знать, что некоторая часть данных времени выполнения должна быть инициализирована под прикрытием и что это может быть выполнено лениво после первого вызова.Например:

public class SocketManagerLazyProxy : SocketManager
{
    private SocketManager mananger;

    public void DoSomething()
    {
        if (manager == null) manager = new RealSocketManager(new Socket());
        manager.DoSomething();
    }   
}

Другой вариант - установить значение Socket, используя свойство Injection после построения графа объектов.Это позволяет вам построить граф объектов намного раньше и установить значение времени выполнения при поступлении запроса, установив его при поступлении запроса:

void HandleRequest(RequestData data)
{
    SocketManager manager = GetSocketManagerForThisRequest();
    manager.Socket = new Socket();
    Handler handler = GetHandler(data.Name);
    handler.Handle(data);
}
...