CodeGym/Java курс/Модул 3/Инверсия на зависимостта

Инверсия на зависимостта

На разположение

9.1 Инversion на зависимостта

Не забравяйте, че веднъж казахме, че в сървърно приложение не можете просто да създавате потоци през new Thread().start()? Само контейнерът трябва да създава нишки. Сега ще доразвием тази идея още повече.

Всички обекти също трябва да бъдат създадени само от контейнера . Разбира се, не говорим за всички обекти, а по-скоро за така наречените бизнес обекти. Те също често се наричат ​​кошчета. Краката на този подход растат от петия принцип на SOLID, който изисква премахване на класовете и преминаване към интерфейси:

  • Модулите от най-високо ниво не трябва да зависят от модулите от по-ниско ниво. И тези, и другите трябва да зависят от абстракциите.
  • Абстракциите не трябва да зависят от детайлите. Изпълнението трябва да зависи от абстракцията.

Модулите не трябва да съдържат препратки към конкретни реализации и всички зависимости и взаимодействия между тях трябва да бъдат изградени единствено на базата на абстракции (тоест интерфейси). Самата същност на това правило може да бъде написана с една фраза: всички зависимости трябва да бъдат под формата на интерфейси .

Въпреки своята фундаменталност и привидна простота, това правило се нарушава най-често. А именно всеки път, когато използваме оператора new в codeа на програмата/модула и създаваме нов обект от определен тип, като по този начин instead of зависимост от интерфейса се формира зависимостта от имплементацията.

Ясно е, че това не може да се избегне и трябва да се създават обекти някъде. Но най-малкото трябва да минимизирате броя на местата, където това се прави и в които класовете са изрично посочени, Howто и да локализирате и изолирате такива места, така че да не са разпръснати из програмния code.

Много добро решение е налудничавата идея за концентриране на създаването на нови обекти в специализирани обекти и модули – фабрики, сервизни локатори, IoC контейнери.

В известен смисъл такова решение следва принципа на единния избор, който гласи: „Когато една софтуерна система трябва да поддържа много алтернативи, техният пълен списък трябва да бъде известен само на един модул на системата“ .

Следователно, ако в бъдеще е необходимо да добавите нови опции (or нови реализации, Howто в случая на създаване на нови обекти, които обмисляме), тогава ще бъде достатъчно да актуализирате само модула, който съдържа тази информация, и всички останали модули ще останат незасегнати и ще могат да продължат работата си, Howто обикновено.

Пример 1

new ArrayList Вместо да пишете нещо като , би било логично List.new()JDK да ви предостави правилната реализация на лист: ArrayList, LinkedList or дори ConcurrentList.

Например, компилаторът вижда, че има извиквания към обекта от различни нишки и поставя безопасна за нишки реализация там. Или твърде много вмъквания в средата на листа, тогава внедряването ще се базира на LinkedList.

Пример 2

Това вече се е случвало със сортовете например. Кога за последен път написахте алгоритъм за сортиране, за да сортирате колекция? Вместо това сега всеки използва метода Collections.sort()и елементите на колекцията трябва да поддържат интерфейса Comparable (сравним).

Ако sort()подадете колекция от по-малко от 10 елемента към метода, е напълно възможно да я сортирате с балонно сортиране (Bubble sort), а не Quicksort.

Пример 3

Компилаторът вече наблюдава How свързвате низове и ще замени codeа ви с StringBuilder.append().

9.2 Инversion на зависимостта на практика

Сега най-интересното: нека помислим How можем да съчетаем теорията и практиката. Как модулите могат правилно да създават и получават своите „зависимости“ и да не нарушават инversionта на зависимости?

За да направите това, когато проектирате модул, трябва да решите сами:

  • Howво прави модулът, Howва функция изпълнява;
  • тогава модулът се нуждае от своята среда, тоест с Howви обекти / модули ще трябва да работи;
  • И How ще го получи?

За да се съобразите с принципите на инversionта на зависимостите, определено трябва да решите кои външни обекти използва вашият модул и How ще получи препратки към тях.

И ето следните опции:

  • модулът сам създава обекти;
  • модулът взема обекти от контейнера;
  • модулът няма представа откъде идват обектите.

Проблемът е, че за да създадете обект, трябва да извикате конструктор от определен тип и в резултат на това модулът ще зависи не от интерфейса, а от конкретната реализация. Но ако не искаме обектите да се създават изрично в codeа на модула, тогава можем да използваме шаблона на фабричния метод .

„Долната линия е, че instead of директно инстанциране на обект чрез new, ние предоставяме на клиентския клас няHowъв интерфейс за създаване на обекти. Тъй като такъв интерфейс винаги може да бъде заменен с правилния дизайн, ние получаваме известна гъвкавост, когато използваме модули от ниско ниво в модули на високо ниво" .

В случаите, когато е необходимо да се създадат групи or семейства от свързани обекти, се използва абстрактна фабрика instead of фабричен метод .

9.3 Използване на Service Locator

Модулът взема необходимите обекти от този, който вече ги има. Предполага се, че системата има няHowво хранorще от обекти, в което модулите могат да „поставят“ своите обекти и да „вземат“ обекти от хранorщето.

Този подход се прилага от модела Service Locator , чиято основна идея е, че програмата има обект, който знае How да получи всички зависимости (услуги), които може да са необходими.

Основната разлика от фабриките е, че Service Locator не създава обекти, но всъщност вече съдържа инстанцирани обекти (or знае къде / How да ги получи и ако създава, тогава само веднъж при първото извикване). Фабриката при всяко повикване създава нов обект, върху който вие получавате пълна собственост и можете да правите Howвото си искате с него.

важно ! Локаторът на услуги създава препратки към същите вече съществуващи обекти . Затова трябва да бъдете много внимателни с обектите, издадени от Service Locator, тъй като някой друг може да ги използва едновременно с вас.

Обектите в Service Locator могат да се добавят директно през конфигурационния файл и по всяHowъв удобен за програмиста начин. Самият локатор на услуги може да бъде статичен клас с набор от статични методи, сингълтон or интерфейс и може да бъде предаден на необходимите класове чрез конструктор or метод.

Локаторът на услуги понякога се нарича анти-модел и не се препоръчва (тъй като създава имплицитни връзки и дава само вид на добър дизайн). Можете да прочетете повече от Марк Сийман:

9.4 Инжектиране на зависимост

Модулът изобщо не се интересува от зависимостите за "копаене". Той само определя Howво трябва да работи, а всички необходими зависимости се доставят (въвеждат) отвън от някой друг.

Това се нарича - Инжектиране на зависимост . Обикновено необходимите зависимости се предават or като параметри на конструктора (инжектиране на конструктор) or чрез методи на клас (инжектиране на сетер).

Този подход обръща процеса на създаване на зависимости – instead of от самия модул, създаването на зависимости се контролира от някой отвън. Модулът от активен излъчвател на обекти става пасивен - не той създава, а други създават за него.

Тази промяна в посоката се нарича Инversion на контрола or Холивудския принцип - "Не ни се обаждайте, ние ще ви се обадим."

Това е най-гъвкавото решение, даващо на модулите най-голяма автономия . Можем да кажем, че само той прилага изцяло „Принципа на единната отговорност“ – модулът трябва да е изцяло фокусиран върху това да си върши добре работата и да не се тревожи за нищо друго.

Осигуряването на модула с всичко необходимо за работа е отделна задача, която трябва да се поеме от съответния „специалист“ (обикновено определен контейнер, IoC контейнер, отговаря за управлението на зависимостите и тяхното внедряване).

Всъщност тук всичко е като в живота: в една добре организирана фирма програмистите програмират, а бюрата, компютрите и всичко необходимо за работа се купуват и осигуряват от офис мениджъра. Или, ако използвате метафората на програмата като конструктор, модулът не трябва да мисли за проводници, някой друг участва в сглобяването на конструктора, а не самите части.

Няма да е преувеличено да се каже, че използването на интерфейси за описание на зависимости между модули (инversion на зависимости) + правилното създаване и инжектиране на тези зависимости (предимно инжектиране на зависимости) са ключови техники за отделяне .

Те служат като основа, върху която хлабавото свързване на codeа, неговата гъвкавост, устойчивост на промени, повторно използване и без които всички други техники нямат смисъл. Това е основата на хлабавото свързване и добрата архитектура.

Принципът на инversion на контрола (заедно с инжектиране на зависимости и локатор на услуги) е разгледан подробно от Мартин Фаулър. Има преводи и на двете му статии: „Инversion на контролни контейнери и модел на инжектиране на зависимости“ и „Инversion на контрола“ .

Коментари
  • Популярен
  • Нов
  • Стар
Трябва да сте влезли, за да оставите коментар
Тази страница все още няма коментари