〰️
Programming with Java
  • Програмиране с JAVA
  • Обектно-ориентирано програмиране - 1 част
    • Лабораторно упражнение 1
      • Основи на програмирането с Java за начинаещи‎
        • Oбщи термини в Java
        • Основни характеристики на JAVA‎
        • Инсталиране на JDK
        • Инсталиране на Intellij
      • Първа програма в Java
      • Системен изход в JAVA
      • Дефиниране на променливи
      • Примитивни типове данни
      • Оператори
      • If-else
      • Switch-Case
      • For loop
      • While loop
      • Do-while loop
      • Continue
      • Break
      • Задачи
    • Лабораторно упражнение 2
      • Клас
      • Обект
      • Ключова дума new
      • Запазена дума static
      • Конвенции за именуване в Java
      • Wrapper Class
      • Сравняване на
      • Нуждата Wrapper Class
      • Упражнения за извънаудиторна заетост
    • Лабораторно упражнение 3
      • Наследяване
      • this и super
      • Модификатори за достъп
      • Капсулиране
      • Упражнения за извънаудиторна заетост
    • Лабораторно упражнение 4
      • Проследяване на грешки
      • Оценяване на изрази
      • Упражнения за извънаудиторна заетост
    • Лабораторно упражнение 5
      • Полиморфизъм
      • Абстрактен клас
      • Интерфейс
      • Упражнения за извънаудиторна заетост
    • Лабораторно упражнение 6
      • Разлика между Абстрактен клас и Интерфейс
        • Разлика 1
        • Разлика 2
        • Разлика 3
        • Разлика 4
        • Разлика 5
        • Разлика 6
      • Упражнения за извънаудиторна заетост
    • Лабораторно упражнение 7
      • String
      • Enum
      • Обработка на изключения
      • Упражнения за извънаудиторна заетост
    • Лабораторно упражнение 8
      • Генерични класове
      • Генеричен метод
      • Параметри на типа в Java Generics
      • Предимства на генериците
      • Генериците работят само с референтни типове
    • Лабораторно упражнение 9
      • Упражнения за извънаудиторна заетост
    • Лабораторно упражнение 10
      • Упражнения за извънаудиторна заетост
    • Лабораторно упражнение 11
      • Упражнения за извънаудиторна заетост
    • Лабораторно упражнение 12
      • Упражнения за извънаудиторна заетост
    • Лабораторно упражнение 13
      • Упражнения за извънаудиторна заетост
      • Упражнения за извънаудиторна заетост
  • Обектно-ориентирано програмиране - 2 част
    • Лабораторно упражнение 1
      • SRP - Single Responsibility Principle
        • Задачи
      • OCP - Open-Closed Principle
        • Задачи
    • Лабораторно упражнение 2
      • LSP - Liskov Substitution Principle
        • Задачи
      • ISP - Interface Segregation Principle
        • Задачи
      • DI - Dependency Inversion Principle
        • Задачи
    • Лабораторно упражнение 3
      • Singleton
        • Задачи
      • Builder
        • Задачи
    • Лабораторно упражнение 4
      • Factory Method
      • Abstract Factory
      • Задачи
    • Лабораторно упражнение 5
    • Лабораторно упражнение 6
      • Decorator
      • Bridge
        • Композиция
      • Задачи
    • Лабораторно упражнение 7
      • Adapter
      • Задачи
    • Лабораторно упражнение 8
      • Composite
      • Proxy
      • Задачи
    • Лабораторно упражнение 9
    • Лабораторно упражнение 10
      • Visitor
      • State
      • Задачи
    • Лабораторно упражнение 11
      • Observer
      • Задачи
    • Лабораторно упражнение 12
      • Chain of Responsibility
      • Command
      • Задачи
    • Лабораторно упражнение 13
  • Интернет технологии 2023
    • Лабораторно упражнение 1
      • HTTP протокол
        • Синтаксис на URL
        • HTTP съобщения
        • Методи за HTTP заявка
        • Кодове за състояние на HTTP отговор
      • Postman
        • Изпращане на заявка с Postman
        • Получаване на отговор
      • Задачи за работа с Postman
    • Лабораторно упражнение 2
      • Работа с Tomcat
      • Maven
      • Създаване на Maven проект в IntelliJ Ultimate Edition
      • Създаване на Maven проект в IntelliJ Community Edition
      • Създаване на Maven проект в Eclipse IDE
    • Лабораторно упражнение 3
      • Java Servlets
      • Жизнен цикъл на сървлета
      • Създаване на сървлети
      • Задачи за създаване на сървлети
    • Лабораторно упражнение 4
      • Extensible Markup Language
      • Java Architecture for XML Binding
      • JavaScript Object Notation
      • Задачи
    • Лабораторно упражнение 5
    • Лабораторно упражнение 6
      • Spring Boot
      • Създаване на Spring boot проект
      • Структура на Spring Boot проект
        • Файлът pom.xml
        • Main клас в Spring Boot
        • Управление на конфигурацията с application.properties
      • Задача
    • Лабораторно упражнение 7
      • Комуникационен поток и CRUD операции
      • ResponseEntity
      • Избрани анотации
      • Задача
    • Лабораторно упражнение 8
      • Lombok
      • Data transfer object (DTO)
      • Обработване на изключения в Spring Boot приложение
        • Клас Optional<T>
      • Валидиране на данните от заявката
      • Задачи
    • Лабораторно упражнение 9-10
      • Конфигуриране на PostgreSQL DB
      • Jakarta Persistence анотации
      • Repository слой
      • Задачи
    • Лабораторно упражнение 11
      • Проследяване на сесии
      • Филтри и FilterChain
      • Spring Security
        • Добавяне на Spring Security в Spring Boot приложение
        • Конфигуриране на филтри
        • Удостоверяване със Spring Security
        • Защита на метод със Spring Security
      • Задача
    • Лабораторно упражнение 12
      • JSON Web Token
      • Защита с помощта на JWT
      • Задача
    • Лабораторно упражнение 13
  • Програмиране за мобилни и Интернет устройства
    • Лабораторно упражнение 1
      • Задачи
    • Лабораторно упражнение 2
      • Среда за разработване
        • Създаване на проект
        • Структора на проект
        • Виртуално устройство с Android OS
      • Задача
    • Лабораторно упражнение 3
      • Activity
      • Layout
        • XML описание
      • Задача
    • Лабораторно упражнение 4
      • Activity Lifecycle
      • Intent
      • Слушатели на събитията и методи за обратно извикване
      • Задача
    • Лабораторно упражнение 5
      • Слушатели на събитията и методи за обратно извикване
      • Задача
    • Лабораторно упражнение 6
      • Фрагменти
      • DialogFragment
      • Задача
    • Лабораторно упражнение 7
      • Примери
        • BaseAdapter
        • ArrayAdapter
        • Custom ArrayAdapter
      • RecyclerView
        • Пример
      • CardView
      • Задача
    • Лабораторно упражнение 8
      • Runnable
      • HandlerThread
      • ThreadPoolExecutor
      • Задача
    • Лабораторно упражнение 9
      • Задача
      • Задача
      • Пример
  • Интернет Технологии
    • Лабораторно упражнение 1
      • Инсталиране и настройка на необходимата среда за работа в упражненията
      • Maven
      • Създаване на Maven проект в Eclipse IDE
      • Създаване на Maven проект в IntelliJ
      • Работа с Tomcat
      • Задача
    • Лабораторно упражнение 2
      • HTML форми
      • Cascading Style Sheets (CSS)
        • CSS селектори
        • Някои CSS свойства
      • Задача
    • Лабораторно упражнение 3
      • Java Servlets
      • Жизнен цикъл на сървлета
      • Създаване на сървлети
      • Задачи
    • Лабораторно упражнение 4
      • Java Server Pages технология. Същност и детайли
      • Пренасочване между Servlet и JSP
      • Задачи
    • Лабораторно упражение 5
      • Build pattern
      • JavaBean
      • Задачи
    • Лабораторно упражение 6
      • Проследяване на сесии
      • Управление на бисквитки (Cookies)
      • Задачи
    • Лабораторно упражнение 7
      • Проследяване на сесии
      • Управление на бисквитки (Cookies)
      • Servlet Filter
      • Задачи
    • Лабораторно упражнение 8
      • Servlet Filter
      • Прихващане на грешки
      • Задачи
    • Лабораторно упражнение 9
      • Extensible Markup Language
      • JavaScript Object Notation
        • Fetch
      • Задача
    • Лабораторно упражнение 10
      • Java Architecture for XML Binding
      • Задачи
Powered by GitBook
On this page
  • Използване на DIP
  • Пряко изпълнение на DIP

Was this helpful?

  1. Обектно-ориентирано програмиране - 2 част
  2. Лабораторно упражнение 2

DI - Dependency Inversion Principle

Принципът на инверсия на зависимостта (DIP) представлява част от обектно ориентираните принципи SOLID

DIP е проста – но въпреки това мощна – програмна парадигма, която можем да използва, за внедряване на добре структурирани, силно независими софтуерни компоненти за многократна употреба.

Когато не се използва DI софтуерните компоненти са плътно обвързани един с друг. Оттук те трудно се използват повторно, заменят и тестват, което води до сложни за подобрение дизайни.

Софтуера трябва да отговаря на следните правила за да изпълнява DI:

  1. Модулите на високо ниво не трябва да зависят от нископоставените модули. И двете трябва да зависят от абстракции.

  2. Абстракциите не трябва да зависят от подробности. Подробностите трябва да зависят от абстракциите

Oсновата на DIP е да обръщане на класическата зависимост между компонентите на високо ниво и ниско ниво, като се абстрахират от взаимодействието помежду си.

В традиционната разработка на софтуер компонентите на високо ниво зависят от такива от ниско ниво. По този начин е трудно да използвате повторно компонентите на високо ниво.

Използване на DIP

Следния пример се състой от клас StringProcessor, който получава стойност String с помощта на компонент на StringReader, и го записва с помощта на компонент на StringWriter:

public class StringProcessor {
    private final StringReader stringReader;
    private final StringWriter stringWriter;

    public StringProcessor(StringReader stringReader, StringWriter stringWriter) {
        this.stringReader = stringReader;
        this.stringWriter = stringWriter;
    }

    public void printString() {
        stringWriter.write(stringReader.getValue());
    }
}

Реализацията на този клас може да стане по няколко начина:

  1. StringReader и StringWriter, компонентите на ниско ниво, са твърдо зададени класове, поставени в една и същи пакет. StringProcessor, компонентът на високо ниво се поставя в различен пакет. StringProcessor зависи от StringReader и StringWriter. Няма инверсия на зависимости, оттам StringProcessor не може да се използва повторно в различен контекст.

  2. StringReader и StringWriter са интерфейси, поставени в една и същи пакет заедно с класа който ги имплементира. StringProcessor сега зависи от абстракции, но компонентите на ниско ниво не. Все още не сме постигнали инверсия на зависимостите.

  3. StringReader и StringWriter са интерфейси, поставени в един и същи пакет заедно с StringProcessor. Сега, StringProcessor има изрична собственост върху абстракциите. StringProcessor, StringReader и StringWriter всички зависят от абстракции. Постигнахме инверсия на зависимости от горе до долу, като абстрахиране на взаимодействието между компонентите. StringProcessor сега е многократна за използване в различен контекст.

  4. StringReader и StringWriter са интерфейси, поставени в отделен пакет от StringProcessor. Постигнахме инверсия на зависимостите и също така е по-лесно да заменим имплементациите на StringReader и StringWriter. StringProcessor също е много по използваем за различени контексти.

От всички горепосочени сценарии само елементи 3 и 4 са валидни внедрявания на DIP.

Точка 3 е пряко DIP изпълнение, където компонентът на високо ниво и абстракциите са поставени в един и същи пакет. От тук компонентът на високо ниво притежава абстракциите. При това изпълнение компонентът на високо равнище отговаря за определянето на абстрактния протокол, чрез който взаимодейства с компонентите на ниско ниво.

По същия начин т. 4 е по-добро DIP изпълнение. В този вариант на модела нито компонентът на високо ниво, нито нископоставените имат собствеността върху абстракциите.

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

Избор на правилното ниво на абстракция

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

В примера по-горе използвахме DI, за да инжектираме тип StringReader в класа StringProcessor. Това би било ефективно, стига нивото на абстракция на StringReader да е близо до домейна на StringProcessor.

За разлика от това, просто бихме пропуснали присъщите ползи на DIP, ако StringReader например е обект на файл, който чете стойност на String от файл. В този случай нивото на абстракция на StringReader би било много по-ниско от нивото на домейна на StringProcessor.

Казано по-просто, нивото на абстракция, което компонентите на високо ниво ще използват, за да си сътрудничат с тези от ниско ниво, трябва винаги да са близо до домейна на първите.

Пряко изпълнение на DIP

Нека разгледаме следния пример, който да предоставя услуги на клиенти:

Основното хранилище на слоя обикновено е база данни, но за да поддържаме кода прост, тук ще използваме обикновена колекция.

Нека започнем с определяне на компонента на високо ниво:

public class CustomerService {
    private final CustomerDao customerDao;
    
    // standard constructor / getter
    
    public Optional<Customer> findById(int id) {
        return customerDao.findById(id);
    }
    
    public List<Customer> findAll() {
        return customerDao.findAll();
    }
}

CustomerService класът прилага методите findById() и findAll() , които донасят клиентите от по нисък слои.

В този случай типът CustomerDao е абстракцията, която CustomerService използва за консумиране на компонента от ниско ниво.

Тъй като това е директно DIP изпълнение, нека определим абстракцията като интерфейс в същия пакет на CustomerService:

public interface CustomerDao {
    Optional<Customer> findById(int id);
    
    List<Customer> findAll();
}

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

Освен това нивото на абстракция на CustomerDao е близо до тази на CustomerService, която се изисква и за добър DIP.

Сега, нека създадем компонента на ниско ниво в различен пакет. В този случай това е просто основно внедряване на CustomerDao:

public class SimpleCustomerDao implements CustomerDao {
    // standard constructor / getter
    
    @Override
    public Optional<Customer> findById(int id) {
        return Optional.ofNullable(customers.get(id));
    }
    
    @Override
    public List<Customer> findAll() {
        return new ArrayList<>(customers.values());
    }
}

Тази реализация позволява класа SimpleCustomerDao да бъде подменян прио промяна на изискването без това да влияе на класа от високо ниво, което улеснява процеса по тестване и внедряване на софтуера.

PreviousЗадачиNextЗадачи

Last updated 3 years ago

Was this helpful?