Статьи

Как я объяснил инъекцию зависимостей в мою команду


Недавно наша компания начала разработку нового веб-приложения на основе Java, и после некоторого процесса оценки мы решили использовать Spring.
 

Но многие члены команды не знают принципов Spring и Dependency Injection.
 

Поэтому меня попросили дать краткий курс о том, что такое Dependency Injection и основы Spring.
 

Вместо того, чтобы рассказывать всю теорию о IOC / DI, я подумал объяснить на примере.
 
Требование: Мы получим некоторый адрес клиента, и нам нужно подтвердить адрес. После некоторой оценки мы подумали об использовании службы проверки адресов Google.
 
Устаревший (плохой) подход: 
 

Просто создайте класс AddressVerificationService и реализуйте логику.
 

Предположим, GoogleAddressVerificationService — это сервис, предоставляемый Google, который принимает адрес в качестве строки и возвращает долготу / широту.
 
 
class AddressVerificationService 
{
	//This method validates the given address and return longitude/latitude details.	
	public String validateAddress(String address)
	{		
		GoogleAddressVerificationService gavs = new GoogleAddressVerificationService();
		String result = gavs.validateAddress(address);
		return result;
	}
}
 
 
Проблемы с этим подходом:
 
1. Если вы хотите изменить своего поставщика услуг проверки адреса, вам нужно изменить логику.
 
2. Вы не можете выполнить модульное тестирование с помощью некоторого фиктивного AddressVerificationService (используя фиктивные объекты)
 

По какой-то причине Клиент просит нас поддержать нескольких провайдеров AddressVerificationService, и мы должны определить, какую службу использовать во время выполнения.
 
 

Для этого вы можете подумать об изменении вышеуказанного класса, как показано ниже:
 
class AddressVerificationService
{
	//This method validates the given address and return longitude/latitude details.
	public String validateAddress(String address)
	{
		String result = null;
		int serviceCode = 2; // read this code value from a config file
		if(serviceCode == 1)
		{
			GoogleAddressVerificationService googleAVS = new GoogleAddressVerificationService();
			result = googleAVS.validateAddress(address);
		} else if(serviceCode == 2)
		{
			YahooAddressVerificationService yahooAVS = new YahooAddressVerificationService();
			result = yahooAVS.validateAddress(address);
		}
		return result;
	}
}
 
 
Проблемы с этим подходом:
 
1. Всякий раз, когда вам нужно поддержать нового поставщика услуг, вам нужно добавить / изменить логику, используя if-else-if.
 
2. Вы не можете выполнить модульное тестирование с помощью некоторого фиктивного AddressVerificationService (используя фиктивные объекты)
 
 
Подход IOC / DI:
 
В вышеупомянутых подходах AddressVerificationService берет на себя управление созданием своих зависимостей.
 
Таким образом, всякий раз, когда происходит изменение в его зависимостях, AddressVerificationService будет меняться.
 

Теперь давайте перепишем AddressVerificationService с использованием шаблона IOC / DI.
 
class AddressVerificationService {
	private AddressVerificationServiceProvider serviceProvider;
		
	public AddressVerificationService(AddressVerificationServiceProvider serviceProvider) {
		this.serviceProvider = serviceProvider;
	}
		
	public String validateAddress(String address)
	{
		return this.serviceProvider.validateAddress(address);
	}
}
	
interface AddressVerificationServiceProvider
{
	public String validateAddress(String address);
}

Здесь мы внедряем зависимость AddressVerificationService AddressVerificationServiceProvider.

Теперь давайте реализуем AddressVerificationServiceProvider с несколькими сервисами провайдера.
class YahooAVS implements AddressVerificationServiceProvider 
{
	@Override
	public String validateAddress(String address) {
		System.out.println("Verifying address using YAHOO AddressVerificationService");
		return yahooAVSAPI.validate(address);
	}		
}

class GoogleAVS implements AddressVerificationServiceProvider
{
	@Override
	public String validateAddress(String address) {
		System.out.println("Verifying address using Google AddressVerificationService");
		return googleAVSAPI.validate(address);
	}
}
 

Теперь Клиент может выбрать, какой сервис Поставщика услуг использовать следующим образом:
 
AddressVerificationService verificationService = null;
AddressVerificationServiceProvider provider = null;
provider = new YahooAVS();//to use YAHOO AVS
provider = new GoogleAVS();//to use Google AVS

verificationService = new AddressVerificationService(provider);
String lnl = verificationService.validateAddress("HitechCity, Hyderabad");
System.out.println(lnl);
 

Для модульного тестирования мы можем реализовать Mock AddressVerificationServiceProvider.
 
 
class MockAVS implements AddressVerificationServiceProvider
{
	@Override
	public String validateAddress(String address) {
		System.out.println("Verifying address using MOCK AddressVerificationService");
		return "<response><longitude>123</longitude><latitude>4567</latitude>";
	}
}

AddressVerificationServiceProvider provider = null;
provider = new MockAVS();//to use MOCK AVS		
AddressVerificationServiceIOC verificationService = new AddressVerificationServiceIOC(provider);
String lnl = verificationService.validateAddress("Somajiguda, Hyderabad");
System.out.println(lnl);
 
 

С этим подходом мы перечислили проблемы с вышеупомянутыми подходами, не основанными на МОК / DI.
 
 
1. Мы можем предоставить поддержку для любого количества провайдеров, сколько пожелаем. Просто внедрите AddressVerificationServiceProvider и вставьте его.
 
2. Мы можем выполнить модульное тестирование, используя фиктивные данные, используя Mock.
 
Таким образом, следуя принципу внедрения зависимостей, мы можем создавать слабосвязанные и легко тестируемые сервисы на основе интерфейса.