Dependency Inversion (DIP) Prensibi
Solid prensiplerinin sonuncusu bağımlılıkların tersine çevrilmesini de ele alarak ilk serinin sonuna geliyoruz. Bir önceki prensip Interface Segregation Prensibi incelemediyseniz bağlantıdan ulaşabilirsiniz. Dependency Inversion prensibi, Robert C. Martin aracılığıyla hayatımıza girmiş ve gevşek bağ (loosely coupled) terimini hayatımıza kazandırmıştır.
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Robert C. Martin
Bu prensibe göre;
- Yüksek seviyeli modüller, düşük seviyeli modüllere bağlı olmamalı; ikisinin de soyutlanması,
- Soyutlamanın detaylara değil; detayların soyutlamaya bağlı olması beklenmektedir.
Maddeleri basitleştirecek olursak; sınıflar arası bağımlılıkların minimal seviyeye indirgenmesi ve bağımlılıkların sınıflar ile değil arayüzler (interface) ile kurulması gerektiğine dayanır. Sistemimizi bu şekilde tasarlamazsak yüksek seviyeli bileşenler, düşük seviyeli bileşenlere bağımlı kalacak ve düşük seviyeli bir bileşen içerisinde yapılacak olan değişikliğin zincirleme olarak bağımlı olan tüm yüksek seviye bileşenleri de değişikliğe zorlayacaktır. Dependency Inversion tam olarak da bu bağımlılığın tersine çevrilmesini amaçlamaktadır.
İlkeye aykırı olarak konuyu somutlaştırmak adına bir örnek yapalım. Senaryoya göre geleneksel 3 katmanlı mimariye sahip bir uygulamada UI katmanından Business katmanına ve buradan da Data Access katmanına sahip bir uygulamamız olduğunu düşünelim.
public class EntityDal
{
public Entity Get(int id)
{
// Getting entity
}
public void Add(Entity entity)
{
// Adding a new entity
}
public void Update(Entity entity)
{
// Updating the entity
}
public void Remove(int id)
{
// Removing the entity
}
}
Düşük seviyeli bileşene bağımlılığı olan yüksek seviyeli bileşenimizi de yazalım.
public class EntityManager
{
private readonly EntityDal _entityDal;
public EntityManager()
{
_entityDal = new EntityDal();
}
public Entity Get(int id)
{
return _entityDal.Get(id);
}
public void Add(Entity entity)
{
_entityDal.Add(entity);
}
public void Update(Entity entity)
{
_entityDal.Update(entity);
}
public void Remove(int id)
{
_entityDal.Remove(id);
}
}
Mevcut kodumuzu inceleyecek olursak; ilk gözümüze çarpanın EntityManager
sınıfının doğrudan EntityDal
sınıfına bağımlı olduğunu görüyoruz. EntityDal
içerisinde yapılacak olan bir değişiklik doğrudan EntityManager
sınıfını da etkileyecektir. Şimdilik veritabanıyla yapılan işlemlerin ileride Xml olarak yapılacağını varsayarsak yine EntityManager
içerisinde değişiklik yapmak zorunda kalacağız. Bu bağımlılığın ortadan kaldırması için soyutlama yapılmalıdır.
public interface IEntityRepository
{
Entity Get(int id);
void Add(Entity entity);
void Update(Entity entity);
void Remove(int id);
}
public class EntityDal : IEntityRepository
{
public Entity Get(int id)
{
// Getting entity
}
public void Add(Entity entity)
{
// Adding a new entity
}
public void Update(Entity entity)
{
// Updating the entity
}
public void Remove(int id)
{
// Removing the entity
}
}
public class EntityManager
{
private readonly IEntityRepository _entityRepository;
public EntityManager(IEntityRepository entityRepository)
{
_entityRepository = entityRepository;
}
public Entity Get(int id)
{
return _entityDal.Get(id);
}
public void Add(Entity entity)
{
_entityDal.Add(entity);
}
public void Update(Entity entity)
{
_entityDal.Update(entity);
}
public void Remove(int id)
{
_entityDal.Remove(id);
}
}
Artık EntityManager
sınıfı doğrudan IEntityRepository
arayüzüne bağlıdır, ikinci maddede de belirtildiği gibi detaylar soyutlanmış, ileride farklı veri kaynaklarıyla çalışılmak istendiğinde yalnızca IEntityRepository
uygulanarak modüller tak/çıkar mantığıyla kolaylıkla uygulanabilir.