Внедрение зависимости должно произойти за счет Инкапсуляции?

Это мой трюк, чтобы сделать это с последними jquery / jqueryui (при условии, что все элементы, которые вы хотите иметь во всплывающих подсказках, имеют класс 'jqtooltip', у них есть теги заголовка, а заголовок имеет символ канала для разделителя строк:

$('.jqtooltip').tooltip({ 
  content: function(callback) { 
     callback($(this).prop('title').replace('|', '<br />')); 
  }
});
123
задан urig 28 June 2009 в 16:13
поделиться

16 ответов

Есть еще один способ взглянуть на эту проблему, который может вас заинтересовать.

Когда мы используем IoC / внедрение зависимостей, мы не используем концепции ООП. По общему признанию, мы используем объектно-ориентированный язык в качестве «хоста», но идеи, лежащие в основе IoC, исходят из компонентно-ориентированной разработки программного обеспечения, а не объектно-ориентированного программирования.

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

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

60
ответ дан 24 November 2019 в 01:18
поделиться

Это не нарушает инкапсуляцию. Вы предоставляете соавтора, но класс решает, как его использовать. Пока вы следуете Скажите, не спрашивайте , все в порядке. Я считаю, что инъекция конструктора предпочтительнее, но сеттеры тоже могут подойти, если они умны. То есть они содержат логику для поддержания инвариантов, которые представляет класс.

4
ответ дан 24 November 2019 в 01:18
поделиться

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

Теперь я предполагаю, что вы сравниваете со случаем, когда created object создает свои собственные инкапсулированные объекты, скорее всего, в своем конструкторе. В моем понимании DP мы хотим снять эту ответственность с объекта и передать ее кому-то другому. С этой целью "

5
ответ дан 24 November 2019 в 01:18
поделиться

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

Обычно этот последний случай обрабатывается, как вы говорите, с помощью аргументов конструктора или методов установки. Однако это не обязательно так - я знаю, что последние версии инфраструктуры Spring DI в Java, например, позволяют аннотировать частные поля (например, with @Autowired ), и зависимость будет установлена ​​посредством отражения без необходимости раскрывать зависимость через какие-либо общедоступные методы / конструкторы классов. Возможно, это именно то решение, которое вы искали.

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

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

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

29
ответ дан 24 November 2019 в 01:18
поделиться

I struggled with this notion as well. At first, the 'requirement' to use the DI container (like Spring) to instantiate an object felt like jumping thru hoops. But in reality, it's really not a hoop - it's just another 'published' way to create objects I need. Sure, encapsulation is 'broken' becuase someone 'outside the class' knows what it needs, but it really isn't the rest of the system that knows that - it's the DI container. Nothing magical happens differently because DI 'knows' one object needs another.

In fact it gets even better - by focusing on Factories and Repositories I don't even have to know DI is involved at all! That to me puts the lid back on encapsulation. Whew!

1
ответ дан 24 November 2019 в 01:18
поделиться

Поработав над этой проблемой еще немного, я теперь считаю, что внедрение зависимостей (в настоящее время) в некоторой степени нарушает инкапсуляцию. Не поймите меня неправильно - я думаю, что использование внедрения зависимостей в большинстве случаев стоит компромисса.

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

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

«предотвращение того, чтобы пользователи устанавливали внутренние данные компонента в недопустимое или несовместимое состояние».

В то же время нельзя сказать, что

"

Обе цитаты взяты из wikipedia .

Чтобы дать конкретный пример: мне нужно доставить клиентскую DLL, которая упрощает и скрывает связь со службой WCF (по сути, удаленный фасад). Поскольку это зависит от 3 разных прокси-классов WCF, если я использую подход DI, я вынужден открывать их через конструктор. Этим я раскрываю внутренности своего коммуникационного уровня, которые пытаюсь скрыть.

В общем, я полностью за DI. В этом конкретном (крайнем) примере это кажется мне опасным.

Обе цитаты взяты из wikipedia .

Чтобы дать конкретный пример: мне нужно доставить клиентскую DLL, которая упрощает и скрывает связь со службой WCF (по сути, удаленный фасад). Поскольку это зависит от 3 разных прокси-классов WCF, если я использую подход DI, я вынужден открывать их через конструктор. Этим я раскрываю внутренности своего коммуникационного уровня, которые пытаюсь скрыть.

В общем, я полностью за DI. В этом конкретном (крайнем) примере это кажется мне опасным.

Этим я раскрываю внутренности своего коммуникационного уровня, которые пытаюсь скрыть.

В общем, я полностью за DI. В этом конкретном (крайнем) примере это кажется мне опасным.

Этим я раскрываю внутренности своего коммуникационного уровня, которые пытаюсь скрыть.

В общем, я полностью за DI. В этом конкретном (крайнем) примере это кажется мне опасным.

2
ответ дан 24 November 2019 в 01:18
поделиться

Да, DI нарушает инкапсуляцию (также известную как «сокрытие информации»).

Но настоящая проблема возникает когда разработчики используют это как предлог для нарушения принципов KISS (Keep It Short and Simple) и YAGNI (You Ain't Gonna Need It)

Лично я предпочитаю простые и эффективные решения. Я в основном использую оператор «новый» для создания экземпляров зависимостей с отслеживанием состояния, когда и где они необходимы. Он прост, хорошо инкапсулирован, прост для понимания и тестирования. Так почему бы и нет?

13
ответ дан 24 November 2019 в 01:18
поделиться

Я верю в простоту. Применение IOC / внедрения зависимостей в доменных классах не дает никаких улучшений, за исключением того, что код становится более сложным для управления из-за наличия внешних xml-файлов, описывающих отношение. Многие технологии, такие как EJB 1.0 / 2.0 и struts 1.1, обращаются вспять, уменьшая количество материала, помещаемого в XML, и пытаются поместить их в код как аннотацию и т. Д. Таким образом, применение IOC для всех разрабатываемых вами классов сделает код бессмысленным.

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

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

2
ответ дан 24 November 2019 в 01:18
поделиться

Я использовал umbrello на ряд небольших проектов, и, похоже, работает достаточно хорошо.

-121--2839503-

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

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

di не является нарушением инкапсуляции, это способ минимизации муфты, когда инкапсуляция обязательно нарушается как вопрос, конечно, в практически в каждом проекте OOP. Включение зависимости в интерфейс внешне минимизирует побочные эффекты связи и позволяет вашим классам оставаться в агностике о реализации.

3
ответ дан 24 November 2019 в 01:18
поделиться

Это зависит от того, является ли зависимость на самом деле детализацией реализации или чем-то, о чем клиент хотел бы/должен знать так или иначе. Одна вещь, которая имеет значение, это то, на какой уровень абстракции нацелен класс. Вот несколько примеров:

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

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

Для серой области, допустим, у вас есть класс, который делает некоторую симуляцию monte carlo. Ему нужен источник случайности. С одной стороны, тот факт, что ему это нужно, является реализацией детали в том, что клиент действительно не заботится о том, откуда именно берется случайность. С другой стороны, поскольку реальные генераторы случайных чисел делают компромисс между степенью случайности, скоростью и т.д., которую клиент может захотеть контролировать, и клиент может захотеть контролировать посев, чтобы получить повторяющееся поведение, впрыскивание может иметь смысл. В этом случае я бы предложил предложить способ создания класса без указания генератора случайных чисел, и использовать по умолчанию нитевой синглл. Если/когда возникнет необходимость в более тонком управлении, предоставьте другой конструктор, позволяющий инжектировать источник случайных чисел.

3
ответ дан 24 November 2019 в 01:18
поделиться

Это похоже на ответ, за который проголосовали «за», но я хочу подумать вслух - возможно, другие тоже так видят вещи.

  • Классический объектно-ориентированный объект использует конструкторы для определения открытого контракта «инициализации» для потребителей класса (скрытие ВСЕХ деталей реализации; инкапсуляция). Этот контракт может гарантировать, что после создания экземпляра у вас будет готовый к использованию объект (то есть никаких дополнительных шагов инициализации, которые нужно запомнить (например, забыть) пользователем).

  • (конструктор) DI, несомненно, нарушает инкапсуляцию, пропуская деталь реализации через этот общедоступный интерфейс конструктора. Пока мы по-прежнему считаем открытый конструктор ответственным за определение контракта инициализации для пользователей, мы создали ужасное нарушение инкапсуляции.

Теоретический пример:

Класс Foo имеет 4 метода и требует целого числа для инициализации, поэтому его конструктор выглядит как Foo (int size) , и он сразу становится понятным пользователям class Foo , что они должны предоставить size при создании экземпляра, чтобы Foo работал.

Скажем, этой конкретной реализации Foo может также потребоваться IWidget для выполнения своей работы.Внедрение этой зависимости в конструктор заставило бы нас создать такой конструктор, как Foo (int size, IWidget widget)

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

Параметр размера НЕ является зависимостью - это просто значение инициализации для каждого экземпляра. IoC отлично подходит для внешних зависимостей (например, виджета), но не для инициализации внутреннего состояния.

Еще хуже, что, если виджет необходим только для 2 из 4 методов этого класса; Я могу понести накладные расходы на создание экземпляра Widget, даже если он не может использоваться!

Как это скомпрометировать / примирить?

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

Это касается зависимости виджетов, но как нам инициализировать «размер», не прибегая к отдельному методу инициализации в интерфейсе Foo? Используя это решение, мы потеряли возможность гарантировать, что экземпляр Foo полностью инициализирован к тому времени, когда вы его получите. Облом, потому что мне очень нравится идея и простота внедрения конструктора.

Как мне добиться гарантированной инициализации в этом мире DI, когда инициализация БОЛЬШЕ, чем ТОЛЬКО внешние зависимости?

4
ответ дан 24 November 2019 в 01:18
поделиться

PS. Предоставляя Внедрение зависимостей , вы не обязательно нарушаете инкапсуляцию . Пример:

obj.inject_dependency(  factory.get_instance_of_unknown_class(x)  );

Клиентский код еще не знает деталей реализации.

1
ответ дан 24 November 2019 в 01:18
поделиться

Как указал Джефф Стернал в комментарии к вопросу, ответ полностью зависит от того, как вы определяете инкапсуляцию .

Кажется, есть два основных лагеря того, что означает инкапсуляция:

  1. Все, что связано с объектом, является методом объекта. Итак, объект File может иметь методы для Save , Print , Display , ModifyText и т. Д.
  2. Объект - это свой собственный маленький мир, не зависящий от внешнего поведения.

Эти два определения прямо противоречат друг другу. Если объект File может напечатать сам себя, это будет сильно зависеть от поведения принтера. С другой стороны, если он просто знает о чем-то, что может печатать для него ( IFilePrinter или какой-то подобный интерфейс), то объект File не должны что-нибудь знать о печати, и поэтому работа с ней принесет меньше зависимостей в объект.

Таким образом, внедрение зависимостей нарушит инкапсуляцию, если вы воспользуетесь первым определением. Но, честно говоря, я не знаю, нравится ли мне первое определение - оно явно не масштабируется (если бы это было так, MS Word был бы одним большим классом).

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

5
ответ дан 24 November 2019 в 01:18
поделиться

Это раскрывает внедряемую зависимость и нарушает принцип инкапсуляции ООП.

Ну, честно говоря, инкапсуляцию нарушает все. :) Это своего рода нежный принцип, к которому нужно относиться хорошо.

Итак, что нарушает инкапсуляцию?

Наследование нарушает .

«Поскольку наследование раскрывает подклассу сведения о его родительской реализации, часто говорят, что« наследование нарушает инкапсуляцию »». (Gang of Four 1995: 19)

Аспектно-ориентированное программирование делает . Например, вы регистрируете обратный вызов onMethodCall (), и это дает вам прекрасную возможность внедрить код в обычную оценку метода, добавить странные побочные эффекты и т. Д.

Объявление друга в C ++ делает .

Расширение классов в Ruby соответствует . Просто переопределите строковый метод где-нибудь после того, как строковый класс был полностью определен.

Ну, многие вещи делают .

Инкапсуляция - хороший и важный принцип. Но не единственный.

switch (principle)
{
      case encapsulation:
           if (there_is_a_reason)
      break!
}
17
ответ дан 24 November 2019 в 01:18
поделиться

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

Кроме того, использование какой-либо функции автоподключения (например, Autofac для C#) делает код чрезвычайно чистым. Создав методы расширения для конструктора Autofac, я смог вырезать много кода конфигурации DI, который мне пришлось бы поддерживать с течением времени по мере роста списка зависимостей.

1
ответ дан 24 November 2019 в 01:18
поделиться

Я согласен с тем, что в крайнем случае DI может нарушать инкапсуляцию. Обычно DI раскрывает зависимости, которые никогда по-настоящему не инкапсулировались. Вот упрощенный пример, заимствованный из книги Мишко Хевери Синглтоны - патологические лжецы :

Вы начинаете с теста кредитной карты и пишете простой модульный тест.

@Test
public void creditCard_Charge()
{
    CreditCard c = new CreditCard("1234 5678 9012 3456", 5, 2008);
    c.charge(100);
}

В следующем месяце вы получите счет на 100 долларов. Почему с вас сняли обвинение? Модульный тест затронул производственную базу данных. Изнутри CreditCard вызывает Database.getInstance () . Рефакторинг CreditCard таким образом, чтобы он использовал DatabaseInterface в своем конструкторе, обнажает факт наличия зависимости. Но я бы сказал, что зависимость никогда не была инкапсулирована с самого начала, поскольку класс CreditCard вызывает внешне видимые побочные эффекты. Если вы хотите протестировать CreditCard без рефакторинга, вы наверняка заметите зависимость.

@Before
public void setUp()
{
    Database.setInstance(new MockDatabase());
}

@After
public void tearDown()
{
    Database.resetInstance();
}

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

0
ответ дан 24 November 2019 в 01:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: