2

Я создал приложение WinForms MVC, используя инъекции зависимостей (DI) и Ninject в качестве контейнера DI. Базовая архитектура выглядит следующим образомИнъекция зависимостей Seemann, «Three Calls Pattern» vs Service Locator Anit-Pattern

Program.cs (главную точку входа приложения WinForms):

static class Program 
{ 
    [STAThread] 
    static void Main() 
    { 
     ... 

     CompositionRoot.Initialize(new DependencyModule()); 
     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 
     Application.Run(CompositionRoot.Resolve<ApplicationShellView>()); 
    } 
} 

DependencyModule.cs

public class DependencyModule : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<IApplicationShellView>().To<ApplicationShellView>(); 
     Bind<IDocumentController>().To<SpreadsheetController>(); 
     Bind<ISpreadsheetView>().To<SpreadsheetView>(); 
    } 
} 

CompositionRoot.cs

public class CompositionRoot 
{ 
    private static IKernel ninjectKernel; 

    public static void Initialize(INinjectModule module) 
    { 
     ninjectKernel = new StandardKernel(module); 
    } 

    public static T Resolve<T>() 
    { 
     return ninjectKernel.Get<T>(); 
    } 

    public static IEnumerable<T> ResolveAll<T>() 
    { 
     return ninjectKernel.GetAll<T>(); 
    } 
} 

ApplicationShellView.cs (основная форма заявки)

public partial class ApplicationShellView : C1RibbonForm, IApplicationShellView 
{ 
    private ApplicationShellController controller; 

    public ApplicationShellView() 
    { 
     this.controller = new ApplicationShellController(this); 
     InitializeComponent(); 
    } 

    public void InitializeView() 
    { 
     dockPanel.Extender.FloatWindowFactory = new CustomFloatWindowFactory(); 
     dockPanel.Theme = vS2012LightTheme; 
    } 

    private void ribbonButtonTest_Click(object sender, EventArgs e) 
    { 
     controller.OpenNewSpreadsheet(); 
    } 

    public DockPanel DockPanel 
    { 
     get { return dockPanel; } 
    } 
} 

где

public interface IApplicationShellView 
{ 
    void InitializeView(); 
    DockPanel DockPanel { get; } 
} 

ApplicationShellController.cs

public class ApplicationShellController 
{ 
    private IApplicationShellView shellView; 

    public ApplicationShellController(IApplicationShellView view) 
    { 
     this.shellView = view; 
    } 

    public void OpenNewSpreadsheet(DockState dockState = DockState.Document) 
    { 
     SpreadsheetController controller = (SpreadsheetController)GetDocumentController("new.xlsx"); 
     SpreadsheetView view = (SpreadsheetView)controller.New("new.xlsx"); 
     view.Show(shellView.DockPanel, dockState); 
    } 

    private IDocumentController GetDocumentController(string path) 
    { 
     return CompositionRoot.ResolveAll<IDocumentController>() 
      .SingleOrDefault(provider => provider.Handles(path)); 
    } 

    public IApplicationShellView ShellView { get { return shellView; } } 
} 

SpreadsheetController.cs

public class SpreadsheetController : IDocumentController 
{ 
    private ISpreadsheetView view; 

    public SpreadsheetController(ISpreadsheetView view) 
    { 
     this.view = view; 
     this.view.SetController(this); 
    } 

    public bool Handles(string path) 
    { 
     string extension = Path.GetExtension(path); 
     if (!String.IsNullOrEmpty(extension)) 
     { 
      if (FileTypes.Any(ft => ft.FileExtension.CompareNoCase(extension))) 
       return true; 
     } 
     return false; 
    } 

    public void SetViewActive(bool isActive) 
    { 
     ((SpreadsheetView)view).ShowIcon = isActive; 
    } 

    public IDocumentView New(string fileName) 
    { 
     // Opens a new file correctly. 
    } 

    public IDocumentView Open(string path) 
    { 
     // Opens an Excel file correctly. 
    } 

    public IEnumerable<DocumentFileType> FileTypes 
    { 
     get 
     { 
      return new List<DocumentFileType>() 
      { 
       new DocumentFileType("CSV", ".csv"), 
       new DocumentFileType("Excel", ".xls"), 
       new DocumentFileType("Excel10", ".xlsx") 
      }; 
     } 
    } 
} 

, где реализован интерфейс

public interface IDocumentController 
{ 
    bool Handles(string path); 

    void SetViewActive(bool isActive); 

    IDocumentView New(string fileName); 

    IDocumentView Open(string path); 

    IEnumerable<DocumentFileType> FileTypes { get; } 
} 

Теперь вид ascociated с этим контроллером является

public partial class SpreadsheetView : DockContent, ISpreadsheetView 
{ 
    private IDocumentController controller; 

    public SpreadsheetView() 
    { 
     InitializeComponent(); 
    } 

    private void SpreadsheetView_Activated(object sender, EventArgs e) 
    { 
     controller.SetViewActive(true); 
    } 

    private void SpreadsheetView_Deactivate(object sender, EventArgs e) 
    { 
     controller.SetViewActive(false); 
    } 

    public void SetController(IDocumentController controller) 
    { 
     this.controller = controller; 
     Log.Trace("SpreadsheetView.SetController(): Controller set successfully"); 
    } 

    public string DisplayName 
    { 
     get { return Text; } 
     set { Text = value; } 
    } 

    public WorkbookView WorkbookView 
    { 
     get { return workbookView; } 
     set { workbookView = value; } 
    } 
    ... 
} 

Наконец вид интерфейсов

public interface ISpreadsheetView : IDocumentView 
{ 
    WorkbookView WorkbookView { get; set; } 
} 

и

public interface IDocumentView 
{ 
    void SetController(IDocumentController controller); 

    string DisplayName { get; set; } 

    bool StatusBarVisible { get; set; } 
} 

Теперь по моим вопросам. В книге Seemann «Injection Dependency in .NET» он рассказывает о «Трехзоновом шаблоне», и это то, что я попытался реализовать в приведенном выше. Код работает, отображается представление оболочки и через шаблон MVC мои контроллеры правильно открывают представления и т. Д. Однако я смущен, так как вышеупомянутый определенно обладает вкусом «Anti-Pattern Locator». В главе 3 книги Seemann он излагает:

В таблице СОСТАВНОЙ КОРТ описывается, где вы должны использовать ДИ-КОНТЕЙНЕР. Однако не указывает, как его использовать. Образец РЕГИСТРАЦИИ RESOLVE RELEASE адресует этот вопрос [...] ДИ-КОНТЕЙНЕР следует использовать в трех последовательных фазах , называемых Регистром, Решением и Освобождением.

В чистом виде шаблон РЕГИСТРАЦИОННОГО РЕШЕНИЯ RELEASE указывает, что вы должны только сделать один вызов метода в каждой фазе. Кшиштоф Козимич называет это Тройным Образом.

Конфигурирование ДИ-КОНТЕЙНЕРА в одном вызове метода требует дополнительных пояснений. причиной того, что регистрация компонентов должно происходить в одном вызове метода является , потому что вы должны рассматривать конфигурацию DI КОНТЕЙНЕР как единого, атомарного действия. После завершения конфигурации контейнер следует рассматривать как только для чтения.

Это звучит как разминированный «сервисный локатор», почему это не считается сервисом?


Для того, чтобы настроить свой код, чтобы использовать вместо Contstructor инъекции, я изменил код входа в

[STAThread] 
static void Main() 
{ 
    var kernel = new StandardKernel(); 
    kernel.Bind(t => t.FromThisAssembly() 
         .SelectAllClasses() 
         .BindAllInterfaces()); 

    FileLogHandler fileLogHandler = new FileLogHandler(Utils.GetLogFilePath()); 
    Log.LogHandler = fileLogHandler; 
    Log.Trace("Program.Main(): Logging initialized"); 

    Application.EnableVisualStyles(); 
    Application.SetCompatibleTextRenderingDefault(false); 
    Application.Run(kernel.Get<ApplicationShellView>()); 
} 

с помощью Ninject.Extensions.Conventions, я тогда изменил ApplicationShellController для того, чтобы исправить мой код, чтобы впрыснуть IDocumentController сек с помощью инъекции CTOR:

public class ApplicationShellController 
{ 
    private IApplicationShellView shellView; 
    private IEnumerable<IDocumentController> controllers; 

    public ApplicationShellController(IApplicationShellView shellView, IEnumerable<IDocumentController> controllers) 
    { 
     this.shellView = shellView; 
     this.controllers = controllers; 
     Log.Trace("ApplicationShellController.Ctor(): Shell initialized successfully"); 
    } 
    ... 
} 

где

public class SpreadsheetController : IDocumentController 
{ 
    private ISpreadsheetView view; 

    public SpreadsheetController(ISpreadsheetView view) 
    { 
     this.view = view; 
     this.view.SetController(this); 
    } 
    ... 
} 

но это приводит к циклической зависимости, как мне это справиться?

Вопрос Резюме:

  1. Почему мое первоначальное использование Ninject с помощью "Thee вызовов Pattern" и CompositionRoot.Resolve<T>() плохо или отличается от Service Locator антишаблоном?
  2. Как я могу решить проблему круговой зависимости выше, если я хочу, чтобы перейти к чистой инъекции CTOR?

Большое спасибо за ваше время.

ответ

2

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

Вы должны создать инфраструктуру, которая отправляет ваши команды и использует какую-либо фабрику для поиска зависимостей, используемых созданными объектами.

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

+0

Не могли бы Вы и пример или ссылку на пример «контроллера завода»? Большое спасибо за ваше время ... – MoonKnight

+0

@Killercam - Я не видел ни WinForms кода, который успешно использует DI, но вы можете посмотреть на asp.net MVC рамки в качестве примера (хотя многим из них не нравятся «, соответствующая емкость «подход, который использует MVC6). –

+0

Хорошо, спасибо, я посмотрю на это ... – MoonKnight

Смежные вопросы