SlideShare a Scribd company logo
1
Dependency Injection
IoC Container
Лектор: Лъчезар Лечев
2
Малко за мен
● Занимавам се с Web Developing от 14 годишен.
● Имам опит като freelancer от 6 години
● PHP / MySQL / Laravel 4 framework / Symfony 2
framework и други
3
Какво ще покрием днес?
1) Какво е Inversion of Control (IoC)
2) Какво е компонент (component) и Какво е услуга (service)
3) Кога ни е необходим Di
4) Идеята. Какво е Di (Dependency Injection)
5) IoC контейнери (IoC Container)
6) Видове Di
a) Constructor injection с PicoContainer
b) Setter injection със Spring
c) Interface injection (на него няма да се спираме)
7) Предимства и недостатъци на DI
4
Какво е IoC(Inversion of Control) ?
Примера е конзолно приложение, в което питаме потребителя
първо за името му и второ за неговия куест и след въвеждане ги
обработваме с някакви наши функции:
#ruby
puts 'What is your name?'
name = gets
process_name(name)
puts 'What is your quest?'
quest = gets
process_quest(quest)
Какво всъщност се случва тук? Тук кода ни контролира приложението: кога и
кой въпрос да се покаже, кога да прочете данните и да ги обработи.
5
Но какво се случва, ако имаме нещо такова:
require 'tk'
root = TkRoot.new()
name_label = TkLabel.new() {text "What is Your Name?"}
name_label.pack
name = TkEntry.new(root).pack
name.bind("FocusOut") {process_name(name)}
quest_label = TkLabel.new() {text "What is Your Quest?"}
quest_label.pack
quest = TkEntry.new(root).pack
quest.bind("FocusOut") {process_quest(quest)}
Tk.mainloop()
Тук ситуацията е коренно различна. Имаме една система с прозорци ( tk библиотеката )
Нека разгледа набързо кода.
( IoC е още известно като Принципа на Холивуд - „Не ни звънете, ние ще ви звъннем“ )
6
To kill dragons
What is Your Name?
What is Your Quest?
Pesho
7
Компоненти и Услуги
Тук ще разгледаме обяснението на термините от гледната точка
на Martin Fowler.
Има много дебати относно „какво е компонент“ и „какво е услуга“.
8
Компоненти
Според Martin Fowler, темата свързана с свързването на елементи
заедно, е един болезнен и оплетен път. Но ето какво той описва като
Компонент:
„* Използвам Компонент за да окажа нещо за софтуера, което е
предназначено да се ползва, БЕЗ ДА СЕ ПРОМЕНЯ, от приложение
извън контрола на създателите на компонента. И чрез „без да се
променя“ имам в предвид, че който го ползва няма да променя Кода на
компонента, макар че той може да го Наследи по начин който писателите
му са позволили.“
Martin Fowler - http://martinfowler.com/articles/injection.html
9
Услуги
„* Услугата е близка по значение на Компонента, но услугата
бива използвана от Чуждо приложение. Главната разлика е, че
Аз очаквам компонента да бъде използван локално(jar файл, dll,
асемблер или импортиране на код). Услугата от друга страна ще
бъде използвана през Дистанционен интерфейс, синхронно или
асинхронно (през web service, система за съобщения, RPC
Remote Procedure Call или socket)“
Martin Fowler - http://martinfowler.com/articles/injection.html
Пример за услуги: Google Maps Api, ReCaptcha
10
Какво е Di и кога ще ни потрябва?
В този пример пишем компонент, който предоставя списък от
филми, режисирани от конкретен режисьор(конкретната
имплементация не е важена):
class MovieLister...
public Movie[] moviesDirectedBy(String arg) {
// интересува ни как ще вземем finder
List allMovies = finder.findAll();
for (Iterator it = allMovies.iterator(); it.hasNext();) {
Movie movie = (Movie) it.next();
if (!movie.getDirector().equals(arg)) it.remove();
}
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
}
…
Имаме метод в класа, който връща по даден аргумент(т.е.
името на режисьора) списък на филмите му.
11
Да преминем към същинската част – а именно как да свържем този MovieLister с някакъв
конкретен обект който търси вътре(Finder). И защо това е интересно? Защото искаме нашият
moviesDirectedBy да бъде напълно независим от нашия Finder и това как там се съхраняват
данните(файл, база данни и т.н.) и естествено от това как се взимат.
За да дефинираме такива Finder-и, нека си направим един интерфейс, който ще ни
задължава да имаме поне този findAll метод, за да сме сигурни, че какъвто и Finder да
сложим, той ще работи с нашия клас.
public interface MovieFinder {
List findAll();
}
До този момент вървим много добре – всичко е достатъчно необвързано едно с друго.
Това и желаем, но в един момент ще трябва да свържем един Finder с дадения ни Lister.
Нека разгледаме защо:
12
Тук отново се фокусираме върху нашия Lister, и в конструктора
му оказваме с new ColonDelimitedMovieFinder, кой Finder да
ползва. Представя ме си че този Finder търси във файл разделен
с двоеточие.
class MovieLister...
private MovieFinder finder;
public MovieLister() {
finder = new ColonDelimitedMovieFinder("movies1.txt");
}
И тук вече обвързахме дадена имплементация на Finder-а и
както бяхме създали независими класове, изведнъж ги
свързахме здраво.
13
Представете си, че вашите приятели харесват функционалностите му и искат
копие от програмата за да го ползват?
* Това е компонент и както казахме в началото, той не е направен за да променяме кода на му!
Ами докато приятелите ни също ползват файл с името movies1.txt с разделени
записи в него с двоеточие – всичко е тип топ.
Ако имат различно име за файла? Лесно ще сложат неговото име в някакъв
файл с свойства.
Но какво става, ако ползват коренно различно място за съхранение на техните
данни: SQL база данни, XML файл, web service, или друг формат файл?
В този случай имаме нужда от друга имплементация на Finder класа. Защото
дефинирахме Интерфейс, по нашият moviesDirectedBy метод няма да има
никакви промени. Но все още трябва да окажем правилната имплементация на
MovieFinder-а.
14
Тук показваме взаимоотношенията между класовете.
MovieLister зависи от интерфейса MovieFinder и съответната
му имплементация MovieFinderImpl. Но ние предпочитаме
нашия MovieLister да зависи единствено от интерфейса, и
как да направим това?
15
Тази ситуация Martin Fowler описва като Plugin.
MovieFinderImpl не е свързан с MovieLister-а по време на
компилацията на кода, понеже не знаем кои от всичките
имплементации нашите приятели ще използва.
Затова нашият Lister искаме да работи с която и да е
имплементация и да може тази имплементация да бъде вкарана
(plugged in) в някакъв по-късен етап и това е извън нашия
контрол.
16
Как да свържем нашия MovieLister, да не се интересува коя
имплементация ще използва, но в същото време да ползваме
инстанцията му?
Проблема е как да сглобим тези плъгини в нашето приложение?
Това е един от основните проблеми които Леките Контейнери от
ново поколекие, решават (IoC Container) и всичко което те правят
е да използват Инверсия на Контрола.
Кога ни е необходим
17
Тук трябва да отбележим, че имаме различни лица на IoC, Martin Fowler е казал:
„Инверсия на контрол е основна характеристика на frameworks, затова когато тези леки
контейнери, казват че са толкова добри, защото ползват Инверсия на контрола, е все едно
да кажа, че моята кола е специална, защото има колела.“
При тези нови контейнери, инверсията е постигната с това как те гледат на тези плъгини. В
примера ни по-горе нашият Lister намираше Finder-а ни като му правеше инстанция ( new
ColonDelimitedMovieFinder ). Това пречи на нашия Lister да бъде плъгин. Тези Контейнери
от ново поколение се уверяват, че потребителя следва някаква Конвенция, която
позволява отделни модули/компоненти да решават коя имплементация да се ползва.
И в резултат на всичко това, не може просто да наричаме този шаблон Инверсия на
контрола, защото е твърде общо. Имало е дискусии относно това как да се казва шаблона,
но евентуално са се спрели на Dependency Injection.
Забележка: Това не е единствения подход който може да предприемете за премахване на
зависимостите от класовете на приложението ви към плъгин имплементация. Другия
начин е Service Locator.
18
Идеята е да имате отделен обект, който да ви пълни полето в
Lister-а с подходящата имплементация за Finder интерфейса
Идеята
19
IoC Container
Контейнера е компонент който се грижи за класовете и
по-точно за тяхното:
● Създавате
● Унищожаване
● Живот
● Зависимости
20
● Constructor injection
● Setter injection
● Interface injection (на него няма да се спираме)
Видове Di
21
Constructor Injection с PicoContainer
В PicoContainer има също и друг начин за инжектиране(със Setter Injection), но предпочитания начин на
разработчиците е чрез Конструктор, затова е подходящ пример за тук.
За да може това да работи, нашия Lister трябва да има конструктор в който се да опишем от какво искаме да
инжектираме:
class MovieLister...
public MovieLister(MovieFinder finder) {
this.finder = finder;
}
22
Finder-а също ще бъде управляван от Pico контейнера и затова
трябва да инжектираме името на файла който ще ползваме:
class ColonMovieFinder...
public ColonMovieFinder(String filename)
{
this.filename = filename;
}
23
Нуждаем се да кажем на Pico контейнера коя имплементация да
свързва със всеки интерфейс, и кой Низ да инжектира като име
на файла във Finder-а.
private MutablePicoContainer configureContainer() {
MutablePicoContainer pico = new DefaultPicoContainer();
Parameter[] finderParams = { new ConstantParameter("movies1.txt") };
pico.registerComponentImplementation( MovieFinder.class,
ColonMovieFinder.class,
FinderParams );
pico.registerComponentImplementation(MovieLister.class);
return pico;
}
24
И за да използване контейнера ( в случая тук, е показано как ще изглежда Теста на нашия код, не конкретното му използване ), ще напишем нещо такова:
public void testWithPico() {
// зареждате конфигурацията, която написахме по-горе
MutablePicoContainer pico = configureContainer();
// взимате инстанция на компонента Lister
MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class);
// намирате филма режисиран от някого
Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
// и проверявате дали е това което очакване
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
}
25
Setter Injection със Spring
Spring e framework на Java за enterprise приложения. Както PicoContainer, той също има
възможност за Constructor и Setter инжекции, но предпочитания от разработчиците начин е
чрез Setter injection.
За да може нашия MovieLister да приеме инжекцията, си дефинираме един Setter метод:
class MovieLister...
private MovieFinder finder;
public void setFinder(MovieFinder finder) {
this.finder = finder;
}
26
По същия начин и за Finder-а:
class ColonMovieFinder...
public void setFilename(String filename)
{
this.filename = filename;
}
27
Третата стъпка е да го конфигурираме. Spring поддържа конфигурация чрез
XML файл и през кода. Но по-удобния и очакван начин е през XML.
<beans>
<bean id="MyMovieLister" class="spring.MovieLister">
<property name="finder">
<ref local="ThisMovieFinder"/>
</property>
</bean>
<bean id="ThisMovieFinder" class="spring.ColonMovieFinder">
<property name="filename">
<value>movies1.txt</value>
</property>
</bean>
</beans>
Тук framework-а сам извиква setFilename('movies1.txt') за да окаже кой файл трябва да се
отвори.
28
И не на последно място, начина на използване:
public void testWithSpring() throws Exception {
// взимаме конфигурацията от файла srping.xml
ApplicationContext ctx = new
FileSystemXmlApplicationContext("spring.xml");
// казваме да вземе Бобчето MovieLister
MovieLister lister = (MovieLister) ctx.getBean("MyMovieLister");
Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
}
29
Предимства и недостатъци на DI
Предимства:
Премахва взаимоотношенията които MovieLister има с MovieFinder имплементацията(не
интерфейса).
Недостатъци:
IoC ( Инверсията на контрола ) е често срешана част от характеристиките на framework-
ците, но идва на определена цена.
На хора им е трудно да го разберат и това довежда до проблеми, когато се опитваш да
дебъгваш. Ако има по-лесен начин и можете да избегнете IoC – направете го. Това не го
прави лошо нещо, но трябва да има за какво да го използвате, а не просто да overkill-вате
простите неща.
30
Благодаря за вниманието!
Главени източници:
http://martinfowler.com/bliki/InversionOfControl.html
http://martinfowler.com/articles/injection.html
Още интересни ресурси:
http://martinfowler.com/books/eaa.html - Книгата на Martin Fowler - Patterns of
Enterprise Application Architecture( P of EAA )
http://picocontainer.com/ - Официалният сайт на PicoContainer
http://spring.io/ - Официалният сайт на Spring

More Related Content

Similar to Dependency injection Pattern Lecture

Python choreographe NAOqi Framework
Python choreographe NAOqi FrameworkPython choreographe NAOqi Framework
Python choreographe NAOqi Framework
Atelier for robotics
 
Mozllla Labs presentation
Mozllla Labs presentationMozllla Labs presentation
Mozllla Labs presentationBogomil Shopov
 
[Dev.bg] CI from scratch with Jenkins
[Dev.bg] CI from scratch with Jenkins[Dev.bg] CI from scratch with Jenkins
[Dev.bg] CI from scratch with Jenkins
Borislav Traykov
 
Using NAOqi
Using NAOqiUsing NAOqi
Курс по уеб програмиране (2014), занятие №3 - JavaScript (част 1/2)
Курс по уеб програмиране (2014), занятие №3 - JavaScript (част 1/2)Курс по уеб програмиране (2014), занятие №3 - JavaScript (част 1/2)
Курс по уеб програмиране (2014), занятие №3 - JavaScript (част 1/2)
DAVID Academy
 
Курс по уеб програмиране (2015), занятие №1 - HTML
Курс по уеб програмиране (2015), занятие №1 - HTMLКурс по уеб програмиране (2015), занятие №1 - HTML
Курс по уеб програмиране (2015), занятие №1 - HTML
DAVID Academy
 
Курс по уеб програмиране (2015), занятие №3 - JavaScript (част 1/2)
Курс по уеб програмиране (2015), занятие №3 - JavaScript (част 1/2)Курс по уеб програмиране (2015), занятие №3 - JavaScript (част 1/2)
Курс по уеб програмиране (2015), занятие №3 - JavaScript (част 1/2)
DAVID Academy
 
Continuous integration (d.atanasov)
Continuous integration (d.atanasov)Continuous integration (d.atanasov)
Continuous integration (d.atanasov)Deyan Atanasov
 
Php security
Php securityPhp security
Php securityphristov
 
Практики в програмирането на iOS приложение - дисекция на реален мой проект)
Практики в програмирането на iOS приложение - дисекция на реален мой проект)Практики в програмирането на iOS приложение - дисекция на реален мой проект)
Практики в програмирането на iOS приложение - дисекция на реален мой проект)
Михаил Великов
 
SEO курс 2014, лекция 1 - Основи на търсенето
SEO курс 2014, лекция 1 - Основи на търсенетоSEO курс 2014, лекция 1 - Основи на търсенето
SEO курс 2014, лекция 1 - Основи на търсенето
Lily Grozeva
 
Lotus Domino Admin Blast: LCTY 2011
Lotus Domino Admin Blast: LCTY 2011Lotus Domino Admin Blast: LCTY 2011
Lotus Domino Admin Blast: LCTY 2011IBS Bulgaria
 
Монолит или microservices
Монолит или microservicesМонолит или microservices
Монолит или microservices
Zhivko Angelov
 
HTML5 приложения за Android, урок 1
HTML5 приложения за Android, урок 1HTML5 приложения за Android, урок 1
HTML5 приложения за Android, урок 1
Leon Anavi
 
Api автентификация и безопасност и защита на web-приложения
Api автентификация и безопасност и защита на web-приложенияApi автентификация и безопасност и защита на web-приложения
Api автентификация и безопасност и защита на web-приложенияPoli Petkova
 
Защита при създаването на PHP-приложения
Защита при създаването на PHP-приложенияЗащита при създаването на PHP-приложения
Защита при създаването на PHP-приложения
Nikolay Milkov
 
DrupalCamp Sofia 2015
DrupalCamp Sofia 2015DrupalCamp Sofia 2015
DrupalCamp Sofia 2015
Bozhidar Boshnakov
 
The better PHP API (BG)
The better PHP API (BG)The better PHP API (BG)
The better PHP API (BG)
boen_robot
 

Similar to Dependency injection Pattern Lecture (20)

Python choreographe NAOqi Framework
Python choreographe NAOqi FrameworkPython choreographe NAOqi Framework
Python choreographe NAOqi Framework
 
Mozllla Labs presentation
Mozllla Labs presentationMozllla Labs presentation
Mozllla Labs presentation
 
[Dev.bg] CI from scratch with Jenkins
[Dev.bg] CI from scratch with Jenkins[Dev.bg] CI from scratch with Jenkins
[Dev.bg] CI from scratch with Jenkins
 
Using NAOqi
Using NAOqiUsing NAOqi
Using NAOqi
 
Курс по уеб програмиране (2014), занятие №3 - JavaScript (част 1/2)
Курс по уеб програмиране (2014), занятие №3 - JavaScript (част 1/2)Курс по уеб програмиране (2014), занятие №3 - JavaScript (част 1/2)
Курс по уеб програмиране (2014), занятие №3 - JavaScript (част 1/2)
 
Курс по уеб програмиране (2015), занятие №1 - HTML
Курс по уеб програмиране (2015), занятие №1 - HTMLКурс по уеб програмиране (2015), занятие №1 - HTML
Курс по уеб програмиране (2015), занятие №1 - HTML
 
Курс по уеб програмиране (2015), занятие №3 - JavaScript (част 1/2)
Курс по уеб програмиране (2015), занятие №3 - JavaScript (част 1/2)Курс по уеб програмиране (2015), занятие №3 - JavaScript (част 1/2)
Курс по уеб програмиране (2015), занятие №3 - JavaScript (част 1/2)
 
Continuous integration (d.atanasov)
Continuous integration (d.atanasov)Continuous integration (d.atanasov)
Continuous integration (d.atanasov)
 
Php security
Php securityPhp security
Php security
 
Практики в програмирането на iOS приложение - дисекция на реален мой проект)
Практики в програмирането на iOS приложение - дисекция на реален мой проект)Практики в програмирането на iOS приложение - дисекция на реален мой проект)
Практики в програмирането на iOS приложение - дисекция на реален мой проект)
 
SEO курс 2014, лекция 1 - Основи на търсенето
SEO курс 2014, лекция 1 - Основи на търсенетоSEO курс 2014, лекция 1 - Основи на търсенето
SEO курс 2014, лекция 1 - Основи на търсенето
 
Lotus Domino Admin Blast: LCTY 2011
Lotus Domino Admin Blast: LCTY 2011Lotus Domino Admin Blast: LCTY 2011
Lotus Domino Admin Blast: LCTY 2011
 
Монолит или microservices
Монолит или microservicesМонолит или microservices
Монолит или microservices
 
Programirane i organizaciq
Programirane i organizaciqProgramirane i organizaciq
Programirane i organizaciq
 
HTML5 приложения за Android, урок 1
HTML5 приложения за Android, урок 1HTML5 приложения за Android, урок 1
HTML5 приложения за Android, урок 1
 
Api автентификация и безопасност и защита на web-приложения
Api автентификация и безопасност и защита на web-приложенияApi автентификация и безопасност и защита на web-приложения
Api автентификация и безопасност и защита на web-приложения
 
Защита при създаването на PHP-приложения
Защита при създаването на PHP-приложенияЗащита при създаването на PHP-приложения
Защита при създаването на PHP-приложения
 
DrupalCamp Sofia 2015
DrupalCamp Sofia 2015DrupalCamp Sofia 2015
DrupalCamp Sofia 2015
 
The better PHP API (BG)
The better PHP API (BG)The better PHP API (BG)
The better PHP API (BG)
 
Why do we need a language like go?
Why do we need a language like go?Why do we need a language like go?
Why do we need a language like go?
 

Dependency injection Pattern Lecture

  • 2. 2 Малко за мен ● Занимавам се с Web Developing от 14 годишен. ● Имам опит като freelancer от 6 години ● PHP / MySQL / Laravel 4 framework / Symfony 2 framework и други
  • 3. 3 Какво ще покрием днес? 1) Какво е Inversion of Control (IoC) 2) Какво е компонент (component) и Какво е услуга (service) 3) Кога ни е необходим Di 4) Идеята. Какво е Di (Dependency Injection) 5) IoC контейнери (IoC Container) 6) Видове Di a) Constructor injection с PicoContainer b) Setter injection със Spring c) Interface injection (на него няма да се спираме) 7) Предимства и недостатъци на DI
  • 4. 4 Какво е IoC(Inversion of Control) ? Примера е конзолно приложение, в което питаме потребителя първо за името му и второ за неговия куест и след въвеждане ги обработваме с някакви наши функции: #ruby puts 'What is your name?' name = gets process_name(name) puts 'What is your quest?' quest = gets process_quest(quest) Какво всъщност се случва тук? Тук кода ни контролира приложението: кога и кой въпрос да се покаже, кога да прочете данните и да ги обработи.
  • 5. 5 Но какво се случва, ако имаме нещо такова: require 'tk' root = TkRoot.new() name_label = TkLabel.new() {text "What is Your Name?"} name_label.pack name = TkEntry.new(root).pack name.bind("FocusOut") {process_name(name)} quest_label = TkLabel.new() {text "What is Your Quest?"} quest_label.pack quest = TkEntry.new(root).pack quest.bind("FocusOut") {process_quest(quest)} Tk.mainloop() Тук ситуацията е коренно различна. Имаме една система с прозорци ( tk библиотеката ) Нека разгледа набързо кода. ( IoC е още известно като Принципа на Холивуд - „Не ни звънете, ние ще ви звъннем“ )
  • 6. 6 To kill dragons What is Your Name? What is Your Quest? Pesho
  • 7. 7 Компоненти и Услуги Тук ще разгледаме обяснението на термините от гледната точка на Martin Fowler. Има много дебати относно „какво е компонент“ и „какво е услуга“.
  • 8. 8 Компоненти Според Martin Fowler, темата свързана с свързването на елементи заедно, е един болезнен и оплетен път. Но ето какво той описва като Компонент: „* Използвам Компонент за да окажа нещо за софтуера, което е предназначено да се ползва, БЕЗ ДА СЕ ПРОМЕНЯ, от приложение извън контрола на създателите на компонента. И чрез „без да се променя“ имам в предвид, че който го ползва няма да променя Кода на компонента, макар че той може да го Наследи по начин който писателите му са позволили.“ Martin Fowler - http://martinfowler.com/articles/injection.html
  • 9. 9 Услуги „* Услугата е близка по значение на Компонента, но услугата бива използвана от Чуждо приложение. Главната разлика е, че Аз очаквам компонента да бъде използван локално(jar файл, dll, асемблер или импортиране на код). Услугата от друга страна ще бъде използвана през Дистанционен интерфейс, синхронно или асинхронно (през web service, система за съобщения, RPC Remote Procedure Call или socket)“ Martin Fowler - http://martinfowler.com/articles/injection.html Пример за услуги: Google Maps Api, ReCaptcha
  • 10. 10 Какво е Di и кога ще ни потрябва? В този пример пишем компонент, който предоставя списък от филми, режисирани от конкретен режисьор(конкретната имплементация не е важена): class MovieLister... public Movie[] moviesDirectedBy(String arg) { // интересува ни как ще вземем finder List allMovies = finder.findAll(); for (Iterator it = allMovies.iterator(); it.hasNext();) { Movie movie = (Movie) it.next(); if (!movie.getDirector().equals(arg)) it.remove(); } return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]); } … Имаме метод в класа, който връща по даден аргумент(т.е. името на режисьора) списък на филмите му.
  • 11. 11 Да преминем към същинската част – а именно как да свържем този MovieLister с някакъв конкретен обект който търси вътре(Finder). И защо това е интересно? Защото искаме нашият moviesDirectedBy да бъде напълно независим от нашия Finder и това как там се съхраняват данните(файл, база данни и т.н.) и естествено от това как се взимат. За да дефинираме такива Finder-и, нека си направим един интерфейс, който ще ни задължава да имаме поне този findAll метод, за да сме сигурни, че какъвто и Finder да сложим, той ще работи с нашия клас. public interface MovieFinder { List findAll(); } До този момент вървим много добре – всичко е достатъчно необвързано едно с друго. Това и желаем, но в един момент ще трябва да свържем един Finder с дадения ни Lister. Нека разгледаме защо:
  • 12. 12 Тук отново се фокусираме върху нашия Lister, и в конструктора му оказваме с new ColonDelimitedMovieFinder, кой Finder да ползва. Представя ме си че този Finder търси във файл разделен с двоеточие. class MovieLister... private MovieFinder finder; public MovieLister() { finder = new ColonDelimitedMovieFinder("movies1.txt"); } И тук вече обвързахме дадена имплементация на Finder-а и както бяхме създали независими класове, изведнъж ги свързахме здраво.
  • 13. 13 Представете си, че вашите приятели харесват функционалностите му и искат копие от програмата за да го ползват? * Това е компонент и както казахме в началото, той не е направен за да променяме кода на му! Ами докато приятелите ни също ползват файл с името movies1.txt с разделени записи в него с двоеточие – всичко е тип топ. Ако имат различно име за файла? Лесно ще сложат неговото име в някакъв файл с свойства. Но какво става, ако ползват коренно различно място за съхранение на техните данни: SQL база данни, XML файл, web service, или друг формат файл? В този случай имаме нужда от друга имплементация на Finder класа. Защото дефинирахме Интерфейс, по нашият moviesDirectedBy метод няма да има никакви промени. Но все още трябва да окажем правилната имплементация на MovieFinder-а.
  • 14. 14 Тук показваме взаимоотношенията между класовете. MovieLister зависи от интерфейса MovieFinder и съответната му имплементация MovieFinderImpl. Но ние предпочитаме нашия MovieLister да зависи единствено от интерфейса, и как да направим това?
  • 15. 15 Тази ситуация Martin Fowler описва като Plugin. MovieFinderImpl не е свързан с MovieLister-а по време на компилацията на кода, понеже не знаем кои от всичките имплементации нашите приятели ще използва. Затова нашият Lister искаме да работи с която и да е имплементация и да може тази имплементация да бъде вкарана (plugged in) в някакъв по-късен етап и това е извън нашия контрол.
  • 16. 16 Как да свържем нашия MovieLister, да не се интересува коя имплементация ще използва, но в същото време да ползваме инстанцията му? Проблема е как да сглобим тези плъгини в нашето приложение? Това е един от основните проблеми които Леките Контейнери от ново поколекие, решават (IoC Container) и всичко което те правят е да използват Инверсия на Контрола. Кога ни е необходим
  • 17. 17 Тук трябва да отбележим, че имаме различни лица на IoC, Martin Fowler е казал: „Инверсия на контрол е основна характеристика на frameworks, затова когато тези леки контейнери, казват че са толкова добри, защото ползват Инверсия на контрола, е все едно да кажа, че моята кола е специална, защото има колела.“ При тези нови контейнери, инверсията е постигната с това как те гледат на тези плъгини. В примера ни по-горе нашият Lister намираше Finder-а ни като му правеше инстанция ( new ColonDelimitedMovieFinder ). Това пречи на нашия Lister да бъде плъгин. Тези Контейнери от ново поколение се уверяват, че потребителя следва някаква Конвенция, която позволява отделни модули/компоненти да решават коя имплементация да се ползва. И в резултат на всичко това, не може просто да наричаме този шаблон Инверсия на контрола, защото е твърде общо. Имало е дискусии относно това как да се казва шаблона, но евентуално са се спрели на Dependency Injection. Забележка: Това не е единствения подход който може да предприемете за премахване на зависимостите от класовете на приложението ви към плъгин имплементация. Другия начин е Service Locator.
  • 18. 18 Идеята е да имате отделен обект, който да ви пълни полето в Lister-а с подходящата имплементация за Finder интерфейса Идеята
  • 19. 19 IoC Container Контейнера е компонент който се грижи за класовете и по-точно за тяхното: ● Създавате ● Унищожаване ● Живот ● Зависимости
  • 20. 20 ● Constructor injection ● Setter injection ● Interface injection (на него няма да се спираме) Видове Di
  • 21. 21 Constructor Injection с PicoContainer В PicoContainer има също и друг начин за инжектиране(със Setter Injection), но предпочитания начин на разработчиците е чрез Конструктор, затова е подходящ пример за тук. За да може това да работи, нашия Lister трябва да има конструктор в който се да опишем от какво искаме да инжектираме: class MovieLister... public MovieLister(MovieFinder finder) { this.finder = finder; }
  • 22. 22 Finder-а също ще бъде управляван от Pico контейнера и затова трябва да инжектираме името на файла който ще ползваме: class ColonMovieFinder... public ColonMovieFinder(String filename) { this.filename = filename; }
  • 23. 23 Нуждаем се да кажем на Pico контейнера коя имплементация да свързва със всеки интерфейс, и кой Низ да инжектира като име на файла във Finder-а. private MutablePicoContainer configureContainer() { MutablePicoContainer pico = new DefaultPicoContainer(); Parameter[] finderParams = { new ConstantParameter("movies1.txt") }; pico.registerComponentImplementation( MovieFinder.class, ColonMovieFinder.class, FinderParams ); pico.registerComponentImplementation(MovieLister.class); return pico; }
  • 24. 24 И за да използване контейнера ( в случая тук, е показано как ще изглежда Теста на нашия код, не конкретното му използване ), ще напишем нещо такова: public void testWithPico() { // зареждате конфигурацията, която написахме по-горе MutablePicoContainer pico = configureContainer(); // взимате инстанция на компонента Lister MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class); // намирате филма режисиран от някого Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); // и проверявате дали е това което очакване assertEquals("Once Upon a Time in the West", movies[0].getTitle()); }
  • 25. 25 Setter Injection със Spring Spring e framework на Java за enterprise приложения. Както PicoContainer, той също има възможност за Constructor и Setter инжекции, но предпочитания от разработчиците начин е чрез Setter injection. За да може нашия MovieLister да приеме инжекцията, си дефинираме един Setter метод: class MovieLister... private MovieFinder finder; public void setFinder(MovieFinder finder) { this.finder = finder; }
  • 26. 26 По същия начин и за Finder-а: class ColonMovieFinder... public void setFilename(String filename) { this.filename = filename; }
  • 27. 27 Третата стъпка е да го конфигурираме. Spring поддържа конфигурация чрез XML файл и през кода. Но по-удобния и очакван начин е през XML. <beans> <bean id="MyMovieLister" class="spring.MovieLister"> <property name="finder"> <ref local="ThisMovieFinder"/> </property> </bean> <bean id="ThisMovieFinder" class="spring.ColonMovieFinder"> <property name="filename"> <value>movies1.txt</value> </property> </bean> </beans> Тук framework-а сам извиква setFilename('movies1.txt') за да окаже кой файл трябва да се отвори.
  • 28. 28 И не на последно място, начина на използване: public void testWithSpring() throws Exception { // взимаме конфигурацията от файла srping.xml ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml"); // казваме да вземе Бобчето MovieLister MovieLister lister = (MovieLister) ctx.getBean("MyMovieLister"); Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the West", movies[0].getTitle()); }
  • 29. 29 Предимства и недостатъци на DI Предимства: Премахва взаимоотношенията които MovieLister има с MovieFinder имплементацията(не интерфейса). Недостатъци: IoC ( Инверсията на контрола ) е често срешана част от характеристиките на framework- ците, но идва на определена цена. На хора им е трудно да го разберат и това довежда до проблеми, когато се опитваш да дебъгваш. Ако има по-лесен начин и можете да избегнете IoC – направете го. Това не го прави лошо нещо, но трябва да има за какво да го използвате, а не просто да overkill-вате простите неща.
  • 30. 30 Благодаря за вниманието! Главени източници: http://martinfowler.com/bliki/InversionOfControl.html http://martinfowler.com/articles/injection.html Още интересни ресурси: http://martinfowler.com/books/eaa.html - Книгата на Martin Fowler - Patterns of Enterprise Application Architecture( P of EAA ) http://picocontainer.com/ - Официалният сайт на PicoContainer http://spring.io/ - Официалният сайт на Spring

Editor's Notes

  1. Имаме една библиотека tk, която създава просто един прозорец, създаваме две заглавия на полета (TkLabel), създава ме си и две поле за въвеждате на текст ( TkEntry ), и закачаме(bind-ваме) един Event(събитие) listener (слушател), който при отфокусиране на даденото поле, изпълнява нашите функции. Тук контрола е обърнат. Вместо ние да извикваме рамката. Този „феномен“ се нарича Инверсия на контрола (Inversion of control – IoC ).IoC е ключова част от повечето frameworks и точно това ги отличава от библиотеките. Библиотеката съдържа в себе си дадена функционалност(обикновено се организират в класове) всяко викане върши работа, магии и връща резултат, докато при framework-а имате някакъв Абстрактен дизайн(както са абстрактните класове) и още други магии вътре в себе си, чрез които вие може да оказване влияние на дадени Места в приложението и да заредите ваш клас или разширите(наследите) клас от framework-а. И в този случай framework-а когато му дойде време, ще извика и направи това което трябва, контрола не е във вас, вие му оказвате къде трябва, но не и кога и дали изобщо ще се извика.
  2. Следващата стъпка, след като разбрахте този принцип е да разграничим два термина: Компоненти и Услуги Тоест те extends този клас/ове и променят само public и private методите/атрибутите и също ги ограничават по други начини в които няма да задълбаваме.
  3. Ако не сте чували до сега за такива дистанционни услуги, които може да ползвате, за пример мога да дам Google Maps API на JS, което в най-общи линии представлява това: Теглите си един файл/библиотека и чрез извикване на библиотеката с настройки, вие може да си създадете карта на Google Maps с маркери, да си слагате картинки, точки, да си очертавате места и т.н. Просто достъпвате техни услуги – Services.
  4. И сега нека продължим с един простичък пример, за да видим и разберем кога ни е необходим DI. В този случай самия код, който е предоставен не е от значение, той просто взима всичките филми и търси всички елементи с режисьор като аргумента и връща този списък от филми. Няма да се спираме на това, как и защо и от къде, просто си представете, че връща списъка от филми по зададения режисьор.
  5. И какво получихме тук? Вече обвързахме дадена имплементация на Finder-а, с нашия Lister. Това не е хубаво! Както имахме добре разделени, независими класове, сега изведнъж станаха твърде свързани. Не забравяйте, че това е компонент! Докато само ние ползваме този клас Lister, всичко е добре, но това както казах е Компонент.
  6. MovieFinderImpl в горният пример е ColonDelimitedMovieFinder
  7. Тук забелязвате, че слагаме един Type Hinting, за да окажем какъв трябва да е класа – наследник на MovieFinder, имплементация на MovieFinder или MovieFinder, но при положение, че той е interface, няма как да подадем MovieFinder.
  8. Тук забелязвате, че слагаме един Type Hinting, за да окажем какъв трябва да е класа – наследник на MovieFinder, имплементация на MovieFinder или MovieFinder, но при положение, че той е interface, няма как да подадем MovieFinder.
  9. Тази конфигурация ще държите в някакъв отделен клас, който трябва да извикате, преди да използвате контейнера. Естествено е много по често срещано, този код да стои в някакъв конфигурационен файл, който се зарежда при стартиране на приложението. Има възможност дори да се направи в някакъв XML файл. Pico Container не разполага с такива възможности, но Nano Container, който е близко свързан с Pico Container предлага това удобство! Идеята е цялата ви конфигурация да е на едно място. Въпроси?
  10. Въпроси?
  11. Както виждате това е някакъв вид структура, чрез която ние оказваме, че името на връзката/инжекцията която създаваме е MovieLister, която сочи към класа spring.MovieLister (от пакета spring), след това казваме, че property-то(атрибута) finder ще сочи към локална връзка MovieFinder, която е дефинирана отдолу. А в долния случай казваме, че атрибута filename ще има value(стойност) movies1.txt
  12. Въпроси?