Одним з найпоширеніших методів зменешення звязаності компонентів алпікації є заміна обєктів конкретного типу на інтерфейси, що сьогодні я постараюсь коротенько описати.
Dependency Injection
Отож наша тестова аплікація умовно працюватиме з базою даних, відповідно маємо клас DbRepository який начебто реалізує роботу з базою даних та клас WithoutDI який як понятно з назви не використовує фічу про яку я сьогодні пишу, а умовно містить в собі певну логіку яка виконується перед тим як працювати з даними збереженими в БД.
На прикладі ци виглядатиме наступним чином:
1: /// <summary>
2: /// Class without Dependency Injection
3: /// </summary>
4: class WithoutDI
5: {
6: DbRepository _repository = new DbRepository();
7:
8: public void UseRepo()
9: {
10: // Dummy logic.
11:
12: Console.WriteLine("I am " + this.GetType().Name + " and I work with " + _repository.GetType().Name);
13: }
14: }
Як видно з коду наш клас має жорстку привязку до класу DbRepository і відповідно, якщо ми захочемо замінити репозиторій на інший то нам також придеться зайти в клас WithoutDI і змінити назву класу(DbRepository ) на нову. Ще одна проблема, яка не одразу кидається в очі, це те, що відтестуватитакий клас складно так як він в теорії працює з реальною базою даних і відповідно виклик любого з методів буде мати вплив на дані в базі. Отож ми трохи змінюємо код і в результаті отримуємо щось типу:
1: /// <summary>
2: /// Class with Dependency Injection
3: /// </summary>
4: class WithDI
5: {
6: private IRepository _repository;
7:
8: public WithDI(IRepository repository)
9: {
10: _repository = repository;
11: }
12:
13: public void UseRepo()
14: {
15: // Dummy logic.
16:
17: Console.WriteLine("I am " + this.GetType().Name + " and I work with " + _repository.GetType().Name);
18: }
19: }
В даному випадку ми приймаємо репозиторій з яким ми будемо працювати в конструкторі, при цьому не конкретний а інтерфейс, що в свою чергу дозволяє передати туди любий обєкт який наслідує цей інтерфейс. Наприклад якщо в нас є декілька реалізацій репозиторіїв то ми можемо використати їх наступним чином:
1: WithDI withDbDi = new WithDI(new DbRepository());
2: WithDI withFileDi = new WithDI(new FileRepository());
3:
4: withDbDi.UseRepo();
5: withFileDi.UseRepo();
В першому випадку ми будемо працювати з репозиторієм бази даних, а в другому з репозиторієм який зберігає дані в файл.
Inversion of Control
IOC контейнер це абстракція яка виносить реєстрацію всіх залежностей в одне місце і автоматично створює необхідні обєкти там де вони потрібні. Опис в мене получився не дуже зрозумілий так що краще розглянемо на прикладі. Я створив простенький клас основною метою якого є реєстрація та повернення створених обєктів. Ось його код:
1: /// <summary>
2: /// My as simple as possible IOC
3: /// </summary>
4: class IOC
5: {
6: Dictionary<string, object> dependencies = new Dictionary<string, object>();
7:
8: public void Register(string name, object obj)
9: {
10: dependencies.Add(name, obj);
11: }
12:
13: public object Resolve(string name)
14: {
15: return dependencies[name];
16: }
17: }
Код не містить жодних перевірок і тд для того щоб була зрозуміла основна ідея.
Використати його дуже просто
1: IOC ioc = new IOC();
2:
3: ioc.Register("someRepo", new DbRepository());
4: ioc.Register("OtherRepo", new FileRepository());
В місці де нам потрібний обєкт просто викликаємо метод Resolve з іменем обєкту який ми хочемо отримати.
1: /// <summary>
2: /// Class with Dependency Injection
3: /// </summary>
4: class WithIOC
5: {
6: private IRepository _repository;
7:
8: public WithIOC(IOC ioc)
9: {
10: _repository = (IRepository)ioc.Resolve("someRepo");
11: }
12:
13: public void UseRepo()
14: {
15: // Dummy logic.
16:
17: Console.WriteLine("I am " + this.GetType().Name + " and I work with " + _repository.GetType().Name + "recieved from IOC");
18: }
19: }
Таким чином в нас для того щоб змінити один репозиторій на інший треба просто зарєеструвати в одному місці той обєкт якй нам потрібно і відповідно всюди по коду де витягується обєкт за цим іменем буде повернено інший обєкт.
Тобто в нас в коді є одне місце в якому можна керувати тим яким репозиторієм ми будемо користуватись і в випадку коли ми тестуємо код або змінюємо тип репозиторія ми можемо зробити це в одному місці, простою зміною обєкту який пхається в ІОС. Спільне використання цих технік сприєя не лише кращому тестуванню, але й має багато інших позитивних моментів Для кращого розуміння вартує перечитати ще декілька статей на тему, бо головною метою для себе поставив лише короткий опис, а сама тема досить фундаментальна і її знання обовязкове.
Стаття Мартіна Фовлера: http://martinfowler.com/articles/injection.html
No comments:
Post a Comment