Внедрение зависимости

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск

Внедрение зависимости (англ. Dependency injection, DI) — процесс предоставления внешней зависимости программному компоненту. Является специфичной формой «инверсии управления» (англ. Inversion of control), где изменение порядка связи осуществляется путём получения необходимой зависимости.

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

Принцип работы[править | править исходный текст]

Работа фреймворка, обеспечивающая внедрение зависимости, описывается следующим образом. Приложение, независимо от оформления, исполняется внутри контейнера IoC, предоставляемого фреймворком. Часть объектов в программе по-прежнему создается обычным способом языка программирования, часть создается контейнером на основе предоставленной ему конфигурации. Управляемые контейнером объекты называются бины[источник не указан 147 дней] (bean). Зависимости от других бинов (но не обычных объектов!) внедряются в бин автоматически контейнером на основании конфигурации. Внедренная зависимость может быть как внутри того же приложения (другой бин), так и внешней. Например, можно внедрить ссылку на источник JDBC. Меняя конфигурацию, мы получаем разные реализации бинов при том, что приложение остается тем же самым. Например, отладочная конфигурация может включать заглушки вместо реальных бинов, что облегчает тестирование.

Примеры кода[править | править исходный текст]

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

Пример кода на Java[править | править исходный текст]

public interface ICar {
    public float getSpeed();
    public void setPedalPressure(final float PEDAL_PRESSURE);
}
 
public interface IEngine {
    public float getEngineRotation();
    public void setFuelConsumptionRate(final float FUEL_FLOW);
}

Без использования dependency injection[править | править исходный текст]

public class DefaultEngineImpl implements IEngine {
    private float engineRotation = 0;
 
    public float getEngineRotation() {
        return engineRotation;
    }
 
    public void setFuelConsumptionRate(final float FUEL_FLOW) {
        engineRotation =;
    }
}
 
public class DefaultCarImpl implements ICar {
    private IEngine engine = new DefaultEngineImpl();
 
    public float getSpeed() {
        return engine.getEngineRotation()*;
    }
 
    public void setPedalPressure(final float PEDAL_PRESSURE) {
        engine.setFuelConsumptionRate();
    }
}
 
public class MyApplication {
    public static void main(String[] args) {
        ICar car = new DefaultCarImpl();
        car.setPedalPressure(5);
        float speed = car.getSpeed();
        System.out.println("Speed of the car is " + speed);
    }
}


Внедрение зависимости вручную[править | править исходный текст]

public class DefaultCarImpl implements ICar {
    private IEngine engine;
 
    public DefaultCarImpl(final IEngine engineImpl) {
        engine = engineImpl;
    }
 
    public float getSpeed() {
        return engine.getEngineRotation()*;
    }
 
    public void setPedalPressure(final float PEDAL_PRESSURE) {
        engine.setFuelConsumptionRate();
    }
}
 
public class CarFactory {
    public static ICar buildCar() {
        return new DefaultCarImpl(new DefaultEngineImpl());
    }
}
 
public class MyApplication {
    public static void main(String[] args) {
        ICar car = CarFactory.buildCar();
        car.setPedalPressure(5);
        float speed = car.getSpeed();
        System.out.println("Speed of the car is " + speed);
    }
}

Внедрение зависимости при помощи фреймворка[править | править исходный текст]

    <service-point id="CarBuilderService">
        <invoke-factory>
            <construct class="Car">
                <service>DefaultCarImpl</service>
                <service>DefaultEngineImpl</service>
            </construct>
        </invoke-factory>
    </service-point>
/** Реализация не показана **/
 
public class MyApplication {
    public static void main(String[] args) {
        Service service = (Service)DependencyManager.get("CarBuilderService");
        ICar car = (ICar)service.getService(Car.class);
        car.setPedalPressure(5);
        float speed = car.getSpeed();
    }
}

Существующие фреймворки[править | править исходный текст]

Реализация внедрения зависимостей существует для различных платформ и языков программирования.

См. также[править | править исходный текст]

Литература[править | править исходный текст]

Ссылки[править | править исходный текст]