Что касается инверсии шаблона управления, то лучше ли вообще не иметь вызовов конструктора вне корня композиции?

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

Допустим, вы пишете приложение, которое имеет одну точку входа, как Main, который также служит корнем композиции для IoC:

  • Снаружи запустите приложение
    • Main или эквивалент
      • var container = new AwesomeContainer();
      • container.Install(new CompositionRootInstaller(startArgs));
        • container.Register( ... );
        • ApplicationMiddleware = container.Resolve<IMiddleware>();
      • ApplicationMiddleware.SignalStart();

Вот, ApplicationMiddleware может быть ControllerFactory в веб-приложении, например.

Теперь, конечно, у нас будет много других услуг, расположенных container в соответствующее время (например, по запросу).

Иногда мы сталкиваемся с ситуациями, когда мы не чувствуем себя так плохо, просто присваивая, скажем, значение по умолчанию для поля. Но, на мой взгляд, это немного нарушает IoC.

Таким образом, верно ли утверждение, что (независимо от предельного значения при этом) всегда лучше избегать вызова конструкторов или фабрик, которые вызывают конструкторы или иным образом получают компоненты, не вызывая container когда мы покинем точку входа?

Пример: программа WinForms

Вот настройка. Это надуманный пример, но я пытаюсь сосредоточиться на проблеме под рукой...

static class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        using (var root = new AppCompositionRoot())
        {

        }
    }
}

class AppCompositionRoot : IDisposable
{
    private IWindsorContainer _container;

    public AppCompositionRoot(IWindsorContainer container = null)
    {
        _container = container ?? new WindsorContainer();
    }

    public void Run()
    {
        var formFactory = _container.Resolve<DefaultFormFactory>();

        Application.Idle += delegate
        {
            formFactory.ApplicationIsIdle();
        };

        Application.Run();
    }

    public void Dispose()
    {
        _container?.Dispose();
    }
}

public interface IFormFactory
{
    System.Windows.Forms.Form Create();
}

public class DefaultFormFactory : IFormFactory
{
    private readonly IWindsorContainer _container;

    private Form _lastForm;

    public DefaultFormFactory(IWindsorContainer container)
    {
        _container = container;
    }

    public Form Create()
    {
        return _container.Resolve<AppForm>();
    }

    public void ApplicationIsIdle()
    {
        _lastForm = _lastForm ?? Create();
        _lastForm.Show();
    }
}

public class AppForm : Form
{
    private readonly string _big;               // sensible default is "Welcome!"
    private readonly string _little;            // sensible default is a string varying by form, time of day, factory
    private readonly IList<object> _watched;    // sensible default is list empty.

    public AppForm(string bigMessage, string littleMessage, IList<object> watched)
    {
        _big = bigMessage;
        _little = littleMessage;
        _watched = watched;
    }

    public void Initialize()
    {
        // do something with bigMesaage, littleMessage, etc.
    }
}

Итак, начнем с бетона AppForm, Нужно два stringс и List<object>,

Скажем, для всех них есть естественный дефолт, который имеет смысл, как в 95% случаев, как в чем-то, что было бы const string на class,

Независимо от моего вопроса - чтобы действительно сделать IoC в идеальном смысле, не правда ли, что вы всегда должны видеть конструкторы, подобные этим (чистые конструкторы), а также вводить любые значения по умолчанию?

1 ответ

Инверсия контроля просто утверждает, что контроль (что бы это ни было) инвертирован. Это не значит, что вы должны все перевернуть; Вы должны инвертировать то, что хотите изменить.

Если вы пишете приложение Hello world и не хотите ничего менять, вы можете создать string в рамках реализации:

Console.WriteLine("Hello, world!");

С другой стороны, если вы хотите изменить сообщение, вы можете передать его как примитивную зависимость:

public class Helo
{
    private readonly string message;

    public Helo(string message)
    {
        this.message = message;
    }

    public void SayHello()
    {
        Console.WriteLine(this.message);
    }
}

Если это помогает, Мишко Хевери проводит различие между новыми и инъекционными. С другой стороны, в моей книге я попытался провести различие между стабильной и изменчивой зависимостями.

Вы можете new до значений, которые вам не нужно контролировать извне. Если вам нужно контролировать их извне (помните, что этоИнверсия контроля), вам нужно ввести их.