Inversion of Control (IoC) Nedir? Dependency Injection (DI) Nedir ?

Inversion of Control (IoC) Nedir?

Kabaca, kodumuz çalışacağı zaman framework’ün kodumuzu çağırması, çalıştırması ve kontrolü tekrar ele alması sürecine Inversion of Control denir. Biraz daha teknik ifadeleri verecek olursak; bir uygulamanın bir kısmının ya da objelerinin kontrolünün bir framework’e verilmesi olarak tanımlayabiliriz. Bu yapının tanımı genellikle OOP context’i içerisinde kullanılır. Kod içerisinde bir çağrım yaptığımızda framework’ün program akışını ele alması ve kodumuz üzerinde çağırımlar yapmasına imkan tanır. Bunu sağlamak için de framework’ler ek bazı davranışlara sahip abstractionları kullanır. Eğer kendi davranışımızı da eklemek istersek framework’ün ilgili yapısını kendi sınıflarımıza extend etmemiz gerekir.

Bu mimarinin faydalarını listeleyecek olursak:

  • Bir task’ın execute edilmesini kendi implementasyonundan ayırır
  • Farklı implementasyonlar arası geçişi oldukça kolaylaştırır
  • Kodun modüleritesini artırır
  • Component’leri izole ederek test etmeyi kolaylaştırır.

Spring IoC Container

Aslında IoC container ifadesi IoC konseptini implement etmiş framework’ler için genel bir ifadedir. Spring Framework’de ise bu yapının ismi ApplicationContext ile tanımlanmıştır. Spring IoC container ise Bean ismini verdiğimiz objelerin örneklenmesi, konfigüre edilmesi ve lifecycle’larının yönetilmesi gibi işlemleri yapar. En basit şekilde SpringBoot kullandığımızı düşünürsek şu şekilde IoC container’a erişmemiz mümkün:

ApplicationContext ctx = SpringApplication.run(SpringApplication.class, args);

Dependency Injection Nedir?

Dependency Injection aslında IoC’nin implement edilmesini sağlayan bir pattern’dır ve programımızın ihtiyacı olan bağımlılıkları sağlaması amaçlanır. Kısacası kod içerisinde inject ettiğimiz bağımlılıklarımız için bizim buna ihtiyacımız var git bunu getir ve bu sınıf içerisinde inject et diyeceğimiz bir konsepti ifade ediyor.

Spring Framework kullanarak Dependency Injection sağlamanın 3 farklı yolu var.

1. Constructor ile Dependency Injection

Örneğin bir servis sınıfımız olsun ve bu servis sınıfımızın bağımlı olduğu bazı repository’leri ya da farklı servisleri olsun. Eğer constructor ile Dependency Injection yapmayı planlıyorsak bunu değişkenlerimizin access modifier’larını private final olarak tanımlayarak yapabiliriz. Ek olarak şunu da belirteyim; eğer sınıfımızda tek bir constructor bulunuyorsa Spring 4.3 ve üzeri versiyona sahipseniz, Spring bu constructor’ı siz belirtmezseniz bile otomatik olarak @Autowired ile işaretliyor. Yani aslında bu örnekte @Autowired yazmasak bile bu kod sorunsuz olarak çalışacaktı. Constructor ile Dependency Injection için örnek verecek olursak:

public class UserService {

  private final UserRepository userRepository;
  private final RabbitTemplate rabbitTemplate;

  @Autowired
  public UserService(UserRepository userRepository, RabbitTemplate rabbitTemplate) {
    this.userRepository = userRepository;
    this.rabbitTemplate = rabbitTemplate;
    }
}

Lombok kullanarak constructor’ı elle oluşturmaya gerek kalmadan da aynı işi yapabiliyoruz. Buraya tıklayarak @RequiredArgsConstructor notasyonunu incelemenizi öneririm.

2. Setter ile Dependency Injection

public class ChannelServiceImpl implements ChannelService {
  private UserService userService;

  @Autowired
  public void setUserService(UserService userService) {
    this.userService = userService;
  }
}

3. Field ile Dependency Injection

Field ile inject etme örneği de bu şekilde ancak bu yöntem önerilmemekte. Çünkü debug yapmak istediğiniz zaman burada ne değişken set edilirken ne döndüğünü görebileceğiniz bir debug point atma şansınızı kaybediyorsunuz. Bu nedenle constructor kullanımı öneriliyor.

public class AuthController {
  @Autowired
  private AuthenticationManager authenticationManager;
  @Autowired
  private JwtTokenUtil jwtTokenUtil;
  @Autowired
  private JwtUserDetailsService userDetailsService;
}

Dependency Injection Uygulamanın Avantajları Nelerdir ?

  1. Daha Düzgün Bir Tasarım: Dependency Injection, yazılım bileşenlerinin birbirine daha az bağlı olmasını sağlar. Bağımlılıkların bileşen içinde sert kodlanması yerine, dışarıdan enjekte edilmesiyle, bileşenlerin daha bağımsız ve yeniden kullanılabilir olmasını sağlar. Bu, kodun daha modüler, daha test edilebilir ve daha kolay bakım yapılabilir olmasını sağlar.
  2. Test Kolaylığı: Dependency Injection, bileşenlerin bağımlılıklarını taklit eden sahte (mock) veya taklit edilmiş (stub) nesnelerle kolayca test edilebilmesini sağlar. Bu, bağımlılıkların gerçek uygulamalar yerine kontrol edilebilir ve öngörülebilir hale getirilmesini sağlar. Test sırasında gerçek nesneler yerine sahte nesneler kullanılarak, bileşenin izole bir şekilde test edilmesi mümkün hale gelir.
  3. Loose Coupling: Dependency Injection, bileşenler arasındaki bağlantıyı gevşetir. Böylece, bileşenlerin birbirleriyle doğrudan etkileşim kurmasını azaltır ve daha esnek bir sistem tasarımını destekler. Bağımlılıklar dışarıdan enjekte edildiğinde, bileşenlerin, farklı bağımlılıklarla değiştirilmesi veya güncellenmesi daha kolaydır. Bu, yeni gereksinimlere daha iyi uyum sağlamayı ve bileşenlerin yeniden kullanılabilirliğini artırmayı sağlar.
  4. Inversion of Control: Dependency Injection, bileşenler arasındaki bağımlılıkları tersine çevirir. Bileşenler, bağımlı oldukları nesneleri dışarıdan alırken, nesnelerin nasıl oluşturulduğu veya yapılandırıldığı hakkında bilgi sahibi olmazlar. Bu, bileşenlerin daha yüksek seviyeli modüllerle daha düşük seviyeli modüller arasındaki bağımlılıkları azaltarak, sistemde daha az bağımlılık oluşturulmasını sağlar.
  5. Yeniden Kullanılabilirlik: Dependency Injection, bileşenlerin daha bağımsız hale gelmesini sağlar ve bu da onların yeniden kullanılabilirliğini artırır. Bağımlılıklar dışarıdan enjekte edildiğinde, bileşenler farklı bağımlılıklarla değiştirilip güncellenebilir. Bu, bileşenlerin farklı projelerde veya farklı bağlamlarda kolayca kullanılabilmesini sağlar. Bileşenlerin yeniden kullanılabilir olması, kod tekrarını azaltır ve geliştirme süreçlerinde verimlilik sağlar.

Dependency Injection Ne Zaman Uygulanmalı?

  1. Bağımlılıkların Değiştirilebilir Olması: Bir bileşenin farklı bağımlılıklarla çalışabilmesi gerekiyorsa, Dependency Injection kullanılabilir. Örneğin, farklı veritabanı sağlayıcılarına veya farklı hizmetlere bağlanmak gerekiyorsa, bu bağımlılıklar dışarıdan enjekte edilerek bileşenin esnekliği sağlanabilir.
  2. Test Edilebilirlik: Bir bileşeni test etmek istediğinizde, gerçek bağımlılıklar yerine sahte veya taklit edilmiş nesnelerle çalışmak isteyebilirsiniz. Dependency Injection, bu sahte nesnelerin kolayca enjekte edilmesini ve bileşenin izole bir şekilde test edilebilmesini sağlar.
  3. Loose Coupling İhtiyacı: Bileşenler arasında bağımlılıkları azaltmak ve bileşenlerin daha bağımsız çalışmasını sağlamak istediğinizde, Dependency Injection kullanılabilir. Bu, bileşenlerin değişikliklere daha adapte olmasını sağlar ve sistemdeki değişikliklerin etkilerini en aza indirir.
  4. Kodun Modülerliği ve Bakımı: Dependency Injection, bileşenlerin daha modüler bir yapıda olmasını sağlar. Bileşenlerin bağımlılıkları dışarıdan enjekte edildiğinde, bileşenlerin tek başlarına kullanılabilir hale gelmesi ve daha kolay bakım yapılabilmesi mümkün olur. Bu da kodun daha sürdürülebilir ve yönetilebilir olmasını sağlar.
  5. Kullanılabilirlik ve Yeniden Kullanılabilirlik: Dependency Injection, bileşenlerin daha genel amaçlı ve tekrar kullanılabilir olmasını sağlar. Bağımlılıklar dışarıdan enjekte edildiğinde, bileşenlerin farklı projelerde veya farklı bağlamlarda kolayca kullanılabilmesi mümkün olur. Bu da yazılım geliştirme sürecini hızlandırır ve verimliliği artırır.

Dependency Injection, Spring Framework’te Neden Önemlidir?

More Reading

Post navigation

1 Comment

Comments are closed.