Инъекция зависимости без рамки - PullRequest
9 голосов
/ 20 февраля 2009

Я пишу аналитическую программу среднего размера (5-10kloc) на MATLAB (не мое решение) и пытаюсь использовать внедрение зависимостей, чтобы сделать мой код более тестируемым. Я думаю, что понимаю базовую модель внедрения объекта / конструктора, но я не понимаю, как это масштабирует граф зависимостей.

Например, если у меня есть объект A, у которого есть - объект B, у которого - объект C, а объект C имеет зависимость, которую необходимо внедрить, мне нужно пропустить ее через всю цепочку? В конце концов, поскольку это программа анализа данных, все в основном возвращается к одному объекту / методу AnalyzeData, означает ли это, что этому объекту должны быть введены все зависимости всей программы?

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

Кроме того, исправления в моих мыслях / формулировках / основах приветствуются - я в основном изучал большую часть этого через Google / HN / SO.

Ответы [ 6 ]

5 голосов
/ 20 февраля 2009

Matlab-агностический ответ:

Если (A нужен B) и (B нужен C), то сначала вы создаете C, затем создаете B и передаете C в B. Затем вы создаете A и передаете (B имеет C) в A.

Теперь, если C требуется зависимость для внедрения, назовите ее D, вы все еще можете сделать это после этой последовательности событий, используя установку сеттера. Это может быть в том случае, если зависимость является необязательной для C. В противном случае в инициализации вашей программы, скорее всего, есть недостаток, заключающийся в том, что C не вводили D до того, как было передано B.

Если вы хотите использовать инжекцию конструктора для инъекции D в C после того, как у вас уже есть (A имеет (B имеет C)), то вам придется использовать инъекцию сеттера для передачи нового (C имеет D) в B, но эта последовательность событий обычно не является допустимым сценарием для DI.

2 голосов
/ 20 февраля 2009

Если я правильно понимаю ваш вопрос, ответ может зависеть от того, как вы создаете классы, из которых вы создаете объекты. В новейших версиях MATLAB классы могут быть определены двумя способами: класс «value» или класс «handle» (документация MATLAB здесь ). Цитирование из документации:

  • Класс значения:"Объекты классов значений постоянно связаны с переменными, которым они назначены. Когда объект значения копируется, данные объекта также копируются, и новый объект независимо от изменений исходного объекта. Экземпляры ведут себя как стандартные числовые и структурные классы MATLAB. "

  • Класс дескриптора:"Объекты классов дескрипторов используют дескриптор для ссылки на объекты класса. Дескриптор - это переменная, которая идентифицирует конкретный экземпляр класса. Когда копируется объект дескриптора дескриптор копируется, но не данные, сохраненные в свойствах объекта. Копия ссылается на те же данные, что и оригинал - если вы измените значение свойства исходного объекта, скопированный объект отразит то же изменение. "

В приведенном ниже примере кода приведены некоторые примеры взаимодействия с «вложенными» объектами, такими как описанные выше, как для вложенных объектов класса значений, так и для вложенных объектов класса handle:

% For value classes:

objC = C(...);  % Make an object of class C, where "..." stands
                %   for any input arguments
objB = B(...,objC);  % Make an object of class B, passing it objC
                     %   and placing objC in field 'objC'
objA = A(...,objB);  % Make an object of class A, passing it objB
                     %   and placing objB in field 'objB'

% If the '.' operator (field access) is defined for the objects:

objA.objB.objC.D = 1;  % Set field 'D' in objC to 1
objA.objB.objC = foo(objA.objB.objC,...);  % Apply a method that
                                           %   modifies objC and
                                           %   returns the new
                                           %   object

% For handle classes:

hC = C(...);  % Get a handle (reference) for a new object of class C
hB = B(...,hC);  % Get a handle for a new object of class B,
                 %   passing it handle hC and placing it in field 'hC'
hA = A(...,hB);  % Get a handle for a new object of class A,
                 %   passing it handle hB and placing it in field 'hB'

% If the '.' operator (field access) is defined for the objects:

hC.D = 1;  % Set field 'D' to 1 for object referenced by hC; Note
           %   that hC and hA.hB.hC both point to same object, and
           %   can thus be used interchangably
foo(hC);  % Apply a method that modifies the object referenced by hC

% If instead using get/set methods for the handle object:

set(hC,'D',1);
set(get(get(hA,'hB'),'hC'),'D',1);  % If variable hC wasn't made, get
                                    %   reference from nested objects
foo(hC);
foo(get(get(hA,'hB'),'hC'));

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

Надеюсь, это поможет с тем, что вы спрашивали.

2 голосов
/ 20 февраля 2009

Отказ от ответственности: независимый от языка ответ

Нет, вам не нужно пропускать зависимости через весь граф объектов. Не нужно создавать экземпляры конкретных типов или передавать их в качестве параметров - это точка DI. Как правило, у вас будет какая-то другая сущность, называемая, например, Assembler, которая будет внедрять эти зависимости. Ассемблер может быть ad-hock, рукописным или может быть некоторой структурой DI.

Например:

  • Класс ClsA имеет свойство типа Интерфейс IB.
  • Класс ClsB реализует IB и имеет свойство типа Интерфейс IC.
  • Класс ClsC реализует IC.

Ваш Ассемблер загрузит приложение и:

  1. создать экземпляр с именем oC класса ClsC
  2. создать экземпляр с именем oB из ClsB и введите его с помощью oC
  3. создать экземпляр с именем oA и добавить его оВ

Все ваши доменные объекты знают только интерфейсы. Ассемблер знает весь объект, но его цель - просто создать граф объектов и привести все в движение. Рукописный ассемблер совершенно уместен; некоторые люди предпочитают писать код проводки, чем использовать файлы конфигурации. Не думайте, что стоит писать ассемблер (DI framework), если вы не планируете использовать его более одного раза. Дело в том, что ваши классы написаны в стиле DI.

Посмотрите на эту статью: http://books.google.com/books?id=vRxAnRb3mb4C&pg=PP1&dq=Danijel+Arsenovski#PPA428,M1

2 голосов
/ 20 февраля 2009

Если вы вводите зависимости вручную, лучше не пропускать зависимости. Вы просто создаете C с его зависимостью, создаете B с C, создаете A с B. Нет необходимости для A знать что-либо о C или его зависимости. Он просто знает об интерфейсе B и обычно зависимости не являются частью интерфейса для объекта.

2 голосов
/ 20 февраля 2009

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

Я нашел это видео действительно полезным вступлением к этим темам - http://www.dnrtv.com/default.aspx?showNum=126

0 голосов
/ 20 февраля 2009

Не знаю ничего о Matlab, но я предполагаю (из ваших слов), что есть объекты. Если это так, перейдите на Service Locator вместо Dependency Injection. Сервисные локаторы очень просты в реализации, поэтому не требуется никаких фреймворков.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...