Многие архитекторы и инженеры рекомендуют Dependency Injection и другие Inversion of Control узоры как способ до improve the testability of your code. Нельзя отрицать, что Dependency Injection делает код более подверженным тестированию, однако, разве это не завершающая цель Abstraction в целом?Где баланс между впрыском зависимостей и абстракцией?
Чувствую противоречие! Я написал пример, чтобы проиллюстрировать это; это не суперреалистично, и я бы не проектировал его таким образом, но мне нужен был быстрый и простой пример структуры класса с несколькими зависимостями. Первый пример - без Injection Dependency, а второй - Injected Dependencies.
Non-DI Пример
package com.stackoverflow.di;
public class EmployeeInventoryAnswerer()
{
/* In reality, at least the store name and product name would be
* passed in, but this example can't be 8 pages long or the point
* may be lost.
*/
public void myEntryPoint()
{
Store oaklandStore = new Store('Oakland, CA');
StoreInventoryManager inventoryManager = new StoreInventoryManager(oaklandStore);
Product fancyNewProduct = new Product('My Awesome Product');
if (inventoryManager.isProductInStock(fancyNewProduct))
{
System.out.println("Product is in stock.");
}
}
}
public class StoreInventoryManager
{
protected Store store;
protected InventoryCatalog catalog;
public StoreInventoryManager(Store store)
{
this.store = store;
this.catalog = new InventoryCatalog();
}
public void addProduct(Product product, int quantity)
{
this.catalog.addProduct(this.store, product, quantity);
}
public boolean isProductInStock(Product product)
{
return this.catalog.isInStock(this.store, this.product);
}
}
public class InventoryCatalog
{
protected Database db;
public InventoryCatalog()
{
this.db = new Database('productReadWrite');
}
public void addProduct(Store store, Product product, int initialQuantity)
{
this.db.query(
'INSERT INTO store_inventory SET store_id = %d, product_id = %d, quantity = %d'
).format(
store.id, product.id, initialQuantity
);
}
public boolean isInStock(Store store, Product product)
{
QueryResult qr;
qr = this.db.query(
'SELECT quantity FROM store_inventory WHERE store_id = %d AND product_id = %d'
).format(
store.id, product.id
);
if (qr.quantity.toInt() > 0)
{
return true;
}
return false;
}
}
Dependency-Введенный Пример
package com.stackoverflow.di;
public class EmployeeInventoryAnswerer()
{
public void myEntryPoint()
{
Database db = new Database('productReadWrite');
InventoryCatalog catalog = new InventoryCatalog(db);
Store oaklandStore = new Store('Oakland, CA');
StoreInventoryManager inventoryManager = new StoreInventoryManager(oaklandStore, catalog);
Product fancyNewProduct = new Product('My Awesome Product');
if (inventoryManager.isProductInStock(fancyNewProduct))
{
System.out.println("Product is in stock.");
}
}
}
public class StoreInventoryManager
{
protected Store store;
protected InventoryCatalog catalog;
public StoreInventoryManager(Store store, InventoryCatalog catalog)
{
this.store = store;
this.catalog = catalog;
}
public void addProduct(Product product, int quantity)
{
this.catalog.addProduct(this.store, product, quantity);
}
public boolean isProductInStock(Product product)
{
return this.catalog.isInStock(this.store, this.product);
}
}
public class InventoryCatalog
{
protected Database db;
public InventoryCatalog(Database db)
{
this.db = db;
}
public void addProduct(Store store, Product product, int initialQuantity)
{
this.db.query(
'INSERT INTO store_inventory SET store_id = %d, product_id = %d, quantity = %d'
).format(
store.id, product.id, initialQuantity
);
}
public boolean isInStock(Store store, Product product)
{
QueryResult qr;
qr = this.db.query(
'SELECT quantity FROM store_inventory WHERE store_id = %d AND product_id = %d'
).format(
store.id, product.id
);
if (qr.quantity.toInt() > 0)
{
return true;
}
return false;
}
}
(Пожалуйста, чтобы мой пример лучше, если у вас есть какие-либо идеи! Это не может это лучший пример.)
В моем примере, я чувствую, что Абстракция полностью нарушена EmployeeInventoryAnswerer
, обладающей знаниями об основных деталях реализации StoreInventoryManager
.
не EmployeeInventoryAnswerer
должны иметь перспективу, «Хорошо, я просто захватить StoreInventoryManager
, дать ему название продукта клиент ищет, и какой магазин я хочу, чтобы проверить, и он расскажет если товар находится на складе ».? Разве он не должен знать ничего о Database
s или InventoryCatalog
, так как с его точки зрения, это часть реализации, о которой она не должна касаться?
Итак, где баланс между проверяемым кодом с инъецированными зависимостями и скрытие информации в качестве принципа абстракции? Даже если средние классы являются просто сквозными зависимостями, одна только подпись конструктора обнаруживает нерелевантные детали, верно?
Более реалистично, скажем, это долговременное фоновое приложение, обрабатывающее данные из СУБД; на каком «слое» графа вызовов целесообразно создавать и обходить коннектор базы данных, сохраняя при этом свой код без использования СУБД?
Мне очень интересно узнать о теории и практичности ООП здесь, а также прояснить, что кажется парадоксальным между ДИ и Скрытием/Абстракцией информации.
Может ли кто-нибудь проголосовать или закрыть голосование, прокомментируйте, как улучшить этот вопрос? – Will