2013-04-22 2 views
2

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

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

Есть два варианта, насколько мне известно. Один из них - определить класс (или интерфейс), в котором хранятся общие ссылки, контекст, если вы хотите, и передать контекст каждому созданному объекту. Другой вариант - передать каждую общую ссылку почти каждому классу в виде отдельного параметра, который увеличит количество параметров конструкторов классов.

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

+0

исследований [Dependency Injection] (https://www.google.com/search?q=dependency+injection+.net). Это слишком широкая тема для ответа на этом форуме. –

ответ

1

Обычно я использую подход контекстного объекта и передаю объект контекста либо конструктору объекта, либо методу - в зависимости от того, какой смысл имеет смысл.

Шаблон контекстного объекта может принимать несколько форм.

Вы можете определить интерфейс, который имеет именно те элементы, которые вам нужны, или вы можете создать класс классов контейнеров. Например, при написании слабосвязанных компонентов, я имею тенденцию иметь, что каждый компонент, который я реализую, имеет соответствующий интерфейс, чтобы он мог быть переопределен, если это необходимо. Затем зарегистрировать объекты на «менеджер» объект, что-то вроде этого:

public interface IServiceManager 
{ 
    public T GetService<T>(); 
    public T RequireService<T>(); 
    public void RegisterService<T>(T service); 
    public void UnregisterService<T>(T service); 
} 

За кулисы есть карта от типа объекта, который позволяет мне очень быстро собрать большой набор различных компонентов в рабочий целое. Каждый компонент запрашивает другие по интерфейсу, а объект-менеджер - это то, что склеивает их вместе. (Если вы правильно автор ваших компонентов, вы можете даже выгрузить одну услугу для другого в то время как процесс запущен!)

Можно было бы зарегистрировать сервис что-то вдоль этих линий:

class FooService : IFooService { } 

// During process start-up: 
serviceManager.RegisterService<IFooService>(new FooService()); 

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

+0

Большое спасибо за ваш ответ, это именно тот тип обратной связи, с которым я с нетерпением жду. Насколько я знаю, этот тип завода или подход поставщика услуг используется в API TFS 2012. Имеет ли смысл в этом подходе регистрировать более одной конкретной реализации для одного и того же интерфейса? Если да, то что бы это значило и как менеджер мог бы разрешить зависимость? – exc

+0

@exc Это зависит от контекста. Я написал [такую ​​реализацию] (https://github.com/cdhowie/Cdh.Toolkit/blob/master/Cdh.Toolkit.Services/ServiceManager.cs), которую я использую в нескольких проектах с успехом. Он устраняет двусмысленность, бросая исключение, если вы попросили ровно одну реализацию, но вернете все реализации, если попросите его более одного. Разумеется, возможны и другие алгоритмы - идите в зависимости от того, какой из них лучше всего решает вашу проблему. – cdhowie

+0

Это не шаблон _context_ - это шаблон_Service Locater_ –

0

Я бы предложил передать в качестве параметра конструктор. Это имеет большое преимущество как для инъекций зависимостей, так и для проверки единиц измерения (издевательства).

+1

Я думаю, что аргумент передает _one_ ссылку на конструктор с свойствами _several_ или _several_ ссылками на конструктор. –

+0

@DStanley Точно. – exc

1

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

  • Он четко определяет фактические зависимости для класса - с «контекстом» вы не знаете, какие части контекста используются без копания в коде.
  • Как правило, наличие большого количества параметров для конструктора представляет собой дизайнерский запах, поэтому использование инъекции конструктора помогает вам вынюхать недостатки дизайна.
  • При тестировании вы можете издеваться индивидуальные зависимости в зависимости от того, чтобы издеваться целый Context
+1

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

+0

Несколько способов решения этой проблемы: 1) иметь базовый класс, который содержит регистратор, 2) сделать регистратор «необязательной» зависимостью, используя Setter Injection, и 3) выполнить регистрацию на более высоком уровне и «размыть», протоколирование событий. Один большой недостаток для статических объектов/синглетов - это затрудняет модульное тестирование, потому что вы не можете заглушить/издеваться над синглом. –

+0

Я определенно согласен с большим недостатком статических объектов и синглетонов. Что касается 1), базовый класс слишком ценен, особенно в языках без множественного наследования, поэтому я не думаю, что хочу использовать наследование только для того, чтобы передать ссылку на журнал. Но это может быть полезно для естественных классов. Я думал о 2) раньше, и я думаю, что люди, которые используют код, забудут установить ссылку на журнал после создания объекта, а затем журнал будет автоматически терпеть неудачу, когда вам это нужно. 3) интересен и определенно имеет свое место, где это было бы полезно. – exc

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