Как распространить изменение ссылки на объект? - PullRequest
0 голосов
/ 19 октября 2018

Я создаю приложение Windows Forms, которое время от времени отображает MessageBox, используя статический метод MessageBox.Show ().

Я поместил статический вызов в объект Repository, как показано в этом вопросе: Как использовать внедрение зависимостей со статическими методами?

В MainClass этот объект репозитория по умолчанию использует исходный статический вызов, но MainClass также предоставляет метод SetMessageBoxRepoDependency (), который устанавливает этот репозиторий.объект.Это открывает возможности для внедрения зависимости.Одним из применений этого является использование инструмента-насмешки для ссылки на интерфейс Repository и обучение методу-макету Show не создавать реального MessageBox во время тестирования.

Однако MainClass также создает ряд других объектов, которые используютэтот репозиторий экземпляр.Как я могу изменить свой код так, чтобы при вызове SetCessageBoxRepoDependency () MainClass другие объекты также использовали новый репозиторий?

(я могу изменить конструктор MainClass, чтобы он брал собственный репозиторий, но хотел бы знать, как можно установить репозиторий после создания MainClass)

MainClass

public class MainClass
{
    private IMessageBoxRepository messageBoxRepo;

    public MainClass()
    {
        messageBoxRepo = new MessageBoxRepository();

        // Just classes that reference the same Repository
        var classA = new ClassA(messageBoxRepo);
        var classB = new ClassB(messageBoxRepo);
        var classC = new ClassC(messageBoxRepo);
    }

    public void SetMessageBoxRepoDependency(IMessageBoxRepository messageBoxRepo)
    {
        this.messageBoxRepo = messageBoxRepo;
    }
}

MessageBoxRepository

public class MessageBoxRepository : IMessageBoxRepository
{
    public DialogResult Show(string text)
    {
        return MessageBox.Show(text); // The original static call.
    }
}

IMessageBoxRepository

public interface IMessageBoxRepository
{
    DialogResult Show(string text);
}

1 Ответ

0 голосов
/ 19 октября 2018

В идеале вы бы хотели, чтобы ваш DI-контейнер сортировал запуска вещей:

namespace WindowsFormsApp1
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            var container = GetContainer();

            // most likely the only resolve you need.
            var form = container.Resolve<Form1>();

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(form);
        }

        private static IContainer GetContainer()
        {
           // Register Dependencies
           // Build the Container
           // return Container;
        }
    }
}

Предполагая, что вы используете Autofac (я не знаю о других платформах, но онидолжно быть в состоянии сделать то же самое), любая форма должна иметь возможность изменить свою подпись конструктора на Func<> для разрешения новых экземпляров за вызов:

public class Form1
{
  private Func<IMessageBoxRepository> _mbr;

  public Form1(Func<IMessageBoxRepository> mbr)
  {
    _mbr = mbr;
  }

  public void OnError(string msg)
  {
    var mb = _mbr();
    mb.Show(msg);
  }
}

Это означает, что вам не нужно выставлять своиКонтейнер повсеместно (что вам не нужно, особенно если вы планируете модульное тестирование).

Модульное тестирование становится довольно простым (пример предполагает NSubstitute, но все они делают что-то подобное):

public class Form1Test
{
  public void OnError_WithAny_CallsShow()
  {
    // Arrange
    var mbr = Substitute.For<IMessageBoxRepository>();
    var mbrFunc = Substitute.For<Func<IMessageBoxRepository>>();
    mbrFunc().Returns(mbr);
    var form1 = new Form1(mbrFunc);

    // Act
    form1.OnError(null);

    // Assert
    mbr.Received().Show();
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...