it-swarm-tr.com

Bağımlılık inversiyon ilkesi nedir ve neden önemlidir?

Bağımlılık inversiyon ilkesi nedir ve neden önemlidir?

163
Phillip Wells

Bu belgeyi inceleyin: Bağımlılık Tersine Çevirme İlkesi .

Temelde diyor ki:

  • Yüksek seviye modülleri, düşük seviye modüllerine bağlı olmamalıdır. Her ikisi de soyutlamalar bağlı olmalıdır.
  • Soyutlamalar asla ayrıntılara bağlı olmamalıdır. Detaylar soyutlamaya bağlı olmalıdır.

Kısaca, neden önemli olduğu konusunda: değişiklikler risklidir ve uygulama yerine bir konsepte bağlı olarak, çağrı sitelerinde değişiklik ihtiyacını azaltırsınız.

Etkili bir şekilde DIP, farklı kod parçaları arasındaki eşleşmeyi azaltır. Buradaki fikir, bir kayıt tesisini uygulamanın birçok yolu olmasına rağmen, onu kullanma şeklinizin zaman içinde nispeten istikrarlı olması gerektiğidir. Günlük tutma kavramını temsil eden bir arayüz çıkarabilirseniz, bu arayüzün uygulamasından zamana göre daha kararlı olması ve arama sitelerinin bu kayıt mekanizmasını korurken veya genişletirken yapabileceğiniz değişikliklerden daha az etkilenmesi gerekir.

Uygulamanın bir arayüze bağlı olmasını sağlayarak, çalışma zamanında kendi ortamınız için hangi uygulamanın daha uygun olacağını seçme olanağını da elde edersiniz. Davalara bağlı olarak, bu da ilginç olabilir.

102
Carl Seleborg

Çevik Yazılım Geliştirme, Prensipler, Desenler ve Uygulamalar ve C #'daki Çevik Prensipler, Desenler ve Uygulamalar kitapları, Bağımlılık İnversiyon Prensibi'nin arkasındaki orijinal hedefleri ve motivasyonları tam olarak anlamak için en iyi kaynaklardır. “Bağımlılık İnversiyon Prensibi” makalesi de iyi bir kaynaktır, ancak daha önce sözü edilen kitaplara girmiş olan bir taslağın yoğunlaştırılmış bir versiyonundan dolayı, Tasarım İlkeleri kitabında (Gamma, vd.) bulunan bu prensibi daha genel bir tavsiyeden ayırt etmenin anahtarı olan paket ve arayüz mülkiyeti “bir uygulamaya değil bir arayüze program” a tavsiye edilir.

Bir özet sağlamak için, Bağımlılık İnversiyon Prensibi temel olarak reversing "yüksek seviye" bileşenlerden "düşük seviye" bileşenlerine kadar "düşük seviye" bileşenlere olan geleneksel bağımlılık yönü ile ilgilidir. arayüzlerine bağlı belong "üst seviye" bileşenlere göre. (Not: "daha yüksek seviye" bileşeni, katmanlı bir mimari içindeki kavramsal konumunu gerektirmek zorunda olmayan, harici bağımlılıklar/hizmetler gerektiren bileşen anlamına gelir.) Bunu yaparken, eşleştirme azaltılmış olduğu gibi kaydırıldı teorik olarak daha değerli olan bileşenlere teorik olarak daha az değerli olan bileşenlerden.

Bu, dış bağımlılıkları, bir uygulamanın, bileşen tüketicisi tarafından temin edilmesi gereken bir arayüz olarak ifade edilen bileşenleri tasarlayarak elde edilir. Başka bir deyişle, tanımlanan arayüzler, bileşenin nasıl kullanıldığını değil (örneğin, "IDoSomething" değil, "INeedSomething") bileşenin ihtiyaç duyduğu şeyi ifade eder.

Bağımlılık İnversiyon Prensibi'nin ifade etmediği şey, arayüzleri kullanarak bağımlılıkları soyutlamanın basit bir uygulamasıdır (örneğin, MyService → [ILogger ⇐ Logger]). Bu, bağımlılığın spesifik uygulama detayından bir bileşeni ayırsa da, tüketici ve bağımlılık arasındaki ilişkiyi tersine çevirmez (örneğin, [MyService → IMyServiceLogger] ⇐ Logger).

Bağımlılık İnversiyon Prensibi'nin önemi, fonksiyonlarının bir kısmı için dışsal bağımlılıklara dayanan yazılım bileşenlerini tekrar kullanabilmek için tek bir amaçtan arındırılabilir (günlüğe kaydetme, doğrulama vb.).

Bu genel yeniden kullanım hedefi kapsamında, iki alt tür yeniden kullanım belirleyebiliriz:

  1. Alt bağımlılık uygulamaları olan birden fazla uygulamada bir yazılım bileşeni kullanmak (örn. Bir DI konteyner geliştirdiniz ve günlük kaydı yapmak istiyorsunuz, ancak konteynerinizi belirli bir günlükleyiciye bağlamak istemiyorum, böylece konteynerinizi kullanan herkes de seçtiğiniz günlük kütüphanesini kullanın).

  2. Yazılım bileşenlerini gelişen bir bağlamda kullanma (örneğin, uygulama ayrıntılarının değiştiği bir uygulamanın birden çok sürümünde aynı kalan işletme mantığı bileşenleri geliştirdiniz).

Bir altyapı kütüphanesinde olduğu gibi bileşenleri birden fazla uygulamada tekrar kullanmak durumunda, amaç, tüketicilerinizi kendi kütüphanenizin alt bağımlılıklarına bağlamadan tüketicilerinize temel bir altyapı ihtiyacı sağlamaktır; tüketiciler de aynı bağımlılıkları istemektedir. Kütüphanenizdeki tüketiciler aynı altyapı ihtiyaçları için farklı bir kütüphane kullanmayı seçtikleri zaman (örneğin NLog vs. log4net) ya da sürümle geriye dönük olarak uyumlu olmayan gerekli kütüphanenin daha sonraki bir sürümünü kullanmayı seçtikleri zaman sorunlu olabilir. kütüphaneniz için gerekli.

İkinci iş mantığı bileşenlerini (örneğin, "üst seviye bileşenler") yeniden kullanma durumuyla, uygulamanızın temel etki alanı uygulamasını, uygulama ayrıntılarınızın değişen gereksinimlerinden (örneğin, kalıcılık kitaplıklarını değiştirme/yükseltme, mesajlaşma kitaplıklarını) izole etmektir. şifreleme stratejileri vb. İdeal olarak, bir uygulamanın uygulama ayrıntılarını değiştirmek, uygulamanın iş mantığını içeren bileşenleri bozmamalıdır.

Not: Bazıları, bu ikinci durumu, tek bir gelişen uygulamada kullanılan iş-mantık bileşenleri gibi bileşenlerin sadece tek bir kullanımı temsil ettiğini düşünerek, gerçek yeniden kullanım olarak tanımlamaya itiraz edebilir. Bununla birlikte, buradaki fikir, uygulamanın uygulama detaylarındaki her değişikliğin yeni bir bağlam ve dolayısıyla farklı bir kullanım durumu sunmasıdır, ancak nihai hedefler izolasyon ve taşınabilirlik olarak ayırt edilebilir.

Bu ikinci durumda Bağımlılık İnversiyon Prensibi'ni takip ederken, bazı yararlar sunabilirken, Java ve C # gibi modern dillere uygulanan değerinin, belki de ilgisiz olma noktasına göre çok azaldığı belirtilmelidir. Daha önce tartışıldığı gibi DIP, uygulama detaylarını tamamen ayrı paketlere ayırmayı içerir. Bununla birlikte, gelişen bir uygulama söz konusu olduğunda, sadece iş alanı bağlamında tanımlanan arayüzlerin kullanılması, uygulama detayları aynı paket içinde yer alsa bile, uygulama detay bileşenlerinin değişen ihtiyaçlarından dolayı daha yüksek seviyeli bileşenleri değiştirme ihtiyacına karşı korunacaktır. . İlkenin bu kısmı, yeni dillerle ilgili olmayan ilkenin kodlandığı (yani C++) olduğunda diline uygun yönleri yansıtır. Bununla birlikte, Bağımlılık İnversiyon Prensibi'nin önemi, öncelikle yeniden kullanılabilir yazılım bileşenlerinin/kütüphanelerinin geliştirilmesinde yatmaktadır.

Arayüzlerin basit kullanımı, Bağımlılık Enjeksiyonu ve Ayrılmış Arayüz deseni ile ilgili olduğu için bu ilkenin daha uzun bir tartışması burada olarak bulunabilir. Ek olarak, ilkenin JavaScript gibi dinamik olarak yazılmış dillerle nasıl ilgili olduğuna dair tartışma da burada şeklinde olabilir.

137
Derek Greer

Yazılım uygulamalarını tasarlarken, düşük seviyeli sınıfları temel ve birincil işlemleri uygulayan sınıfları (disk erişimi, ağ protokolleri, ...) ve yüksek seviye sınıfları karmaşık mantığı içine alan sınıfları (iş akışları, ...) düşünebiliriz.

Sonuncusu düşük seviyeli sınıflara dayanıyor. Bu tür yapıları uygulamanın doğal bir yolu, düşük seviyeli sınıflar yazmaktır ve bir kez karmaşık üst seviye sınıfları yazmak için onlara sahip oluruz. Yüksek seviyeli sınıflar diğerleri açısından tanımlandığı için bu, bunu yapmanın mantıklı bir yoludur. Ancak bu esnek bir tasarım değil. Düşük seviyeli bir sınıfı değiştirmemiz gerekirse ne olur?

Bağımlılık İnversiyon Prensibi şunları belirtir:

  • Yüksek seviye modülleri, düşük seviye modüllerine bağlı olmamalıdır. Her ikisi de soyutlamalar bağlı olmalıdır.
  • Soyutlamalar ayrıntılara bağlı olmamalıdır. Detaylar soyutlamaya bağlı olmalıdır.

Bu ilke, yazılımdaki yüksek seviye modüllerin düşük seviye modüllere bağlı olması gerektiği konulu geleneksel görüşü "tersine çevirmeyi" amaçlıyor. Burada yüksek seviyeli modüller, düşük seviyeli modüller tarafından uygulanan soyutlamaya (örneğin, arabirimin yöntemlerine karar verme) sahiptir. Böylece daha düşük seviyeli modüllerin daha yüksek seviyeli modüllere bağımlı hale getirilmesi.

10
nikhil.singhal

İyi uygulanan bağımlılık inversiyonu, uygulamanızın tüm mimarisi düzeyinde esneklik ve istikrar sağlar. Uygulamanızın daha güvenli ve istikrarlı bir şekilde gelişmesini sağlar.

Geleneksel katmanlı mimari

Geleneksel olarak katmanlı bir mimari UI, iş katmanına ve bu da veri erişim katmanına dayanıyordu.

http://xurxodev.com/content/images/2016/02/Traditional-Layered.png

Katman, paket veya kitaplığı anlamanız gerekir. Kodun nasıl olacağını görelim.

Veri erişim katmanı için bir kütüphane veya pakete sahibiz.

// DataAccessLayer.dll
public class ProductDAO {

}

Ve veri erişim katmanına bağlı olan başka bir kütüphane veya paket katmanı iş mantığı.

// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO { 
    private ProductDAO productDAO;
}

Bağımlılık inversiyonlu katmanlı mimari

Bağımlılık inversiyonu aşağıdakileri gösterir:

Yüksek seviye modülleri, düşük seviye modüllerine bağlı olmamalıdır. Her ikisi de soyutlamalar bağlı olmalıdır.

Soyutlamalar ayrıntılara bağlı olmamalıdır. Detaylar soyutlamaya bağlı olmalıdır.

Yüksek seviye modülleri ve düşük seviye nedir? Kütüphaneler veya paketler gibi düşünme modülleri, üst seviye modül, geleneksel olarak bağımlılıkları olan ve bağımlı oldukları düşük seviyeli olanlardır.

Başka bir deyişle, modülün yüksek seviyesi, eylemin başlatıldığı yerde ve eylemin gerçekleştirildiği yerde düşük düzeyde olacaktır.

Bu ilkeden çıkarılacak makul bir sonuç, somutlıklar arasında bir bağımlılık olmaması gerektiği, ancak bir soyutlamaya bağımlı olması gerektiğidir. Ancak, aldığımız yaklaşıma göre, yatırım bağımlılığına bağımlılık yapabilir, bir soyutlama yapabiliriz.

Kurallarımızı aşağıdaki gibi uyarladığımızı hayal edin:

Soyutlamayı tanımlayan veri erişim katmanı için bir kütüphane veya pakete sahip oluruz.

// DataAccessLayer.dll
public interface IProductDAO
public class ProductDAO : IProductDAO{

}

Ve veri erişim katmanına bağlı olan başka bir kütüphane veya paket katmanı iş mantığı.

// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO { 
    private IProductDAO productDAO;
}

Bir soyutlamaya bağlı olsak da, iş ve veri erişimi arasındaki bağımlılık aynı kalıyor.

http://xurxodev.com/content/images/2016/02/Traditional-Layered.png

Bağımlılık inversiyonu elde etmek için kalıcılık arayüzü, bu yüksek seviye mantığının veya etki alanının düşük seviye modülünde olmadığı modülde veya pakette tanımlanmalıdır.

Öncelikle, etki alanı katmanının ne olduğunu ve onun iletişiminin soyutlamasını kalıcılık olarak tanımlayın.

// Domain.dll
public interface IProductRepository;

using DataAccessLayer;
public class ProductBO { 
    private IProductRepository productRepository;
}

Kalıcılık katmanı etki alanına bağlı olduktan sonra, bir bağımlılık tanımlanmışsa şimdi tersine dönme.

// Persistence.dll
public class ProductDAO : IProductRepository{

}

http://xurxodev.com/content/images/2016/02/Dependency-Inversion-Layers.png

Prensibi derinleştirmek

Kavramı iyi özümsemek, amacı ve faydaları derinleştirmek önemlidir. Mekanik olarak kalırsak ve tipik vaka havuzunu öğrenirsek, bağımlılık ilkesini nerede uygulayabileceğimizi belirleyemeyiz.

Fakat neden bir bağımlılığı tersine çeviriyoruz? Belirli örneklerin ötesindeki ana amaç nedir?

Böyle yaygın bir şekilde daha az kararlı şeylere bağımlı olmayan en kararlı şeylerin daha sık değişmesine izin verir.

Kalıcılık türünün değiştirilmesi, veritabanı veya teknolojinin etki alanı mantığından veya kalıcılık ile iletişim kurmak için tasarlanan eylemlerle aynı veritabanına erişmesi daha kolaydır. Bu nedenle, bağımlılık tersine çevrilir çünkü bu değişiklik meydana gelirse kalıcılığı değiştirmek kolaydır. Bu şekilde etki alanını değiştirmek zorunda kalmayacağız. Etki alanı katmanı hepsinin en sağlam olanıdır, bu nedenle hiçbir şeye bağlı olmamalıdır.

Ancak sadece bu depo örneği yoktur. Bu ilkenin uygulandığı birçok senaryo vardır ve bu ilkeye dayanan mimariler vardır.

Mimarileri

Bağımlılığın inversiyonunun tanımının anahtarı olduğu mimariler vardır. Tüm alanlarda en önemli olan ve etki alanı ile paketlerin veya kütüphanelerin geri kalanı arasındaki iletişim protokolünü belirten soyutlamalardır.

Temiz Mimari

Temiz mimaride etki alanı merkezde bulunur ve bağımlılığı gösteren okların yönüne bakarsanız, en önemli ve kararlı katmanların ne olduğu açıktır. Dış katmanlar dengesiz araçlar olarak kabul edilir, bu nedenle onlara bağlı olarak kaçının.

Altıgen Mimari

Etki alanının merkez kısımda bulunduğu ve limanların dominodan dışa doğru iletişimden soyutlandığı altıgen mimari ile aynı şekilde olur. Burada yine etki alanının en istikrarlı olduğu ve geleneksel bağımlılığın tersine çevrildiği açıktır.

9
xurxodev

Bana göre, resmi makalesinde bölümünde açıklandığı gibi Bağımlılık İnversiyon Prensibi, C++ dilinde bir sorunu çözmek için doğal olarak daha az tekrar kullanılabilen modüllerin yeniden kullanılabilirliğini artırmak için yanlış yönlendirilmiş bir girişimdir.

C++ 'daki sorun, başlık dosyalarının genellikle özel alanların ve yöntemlerin bildirimlerini içermesidir. Bu nedenle, yüksek seviye bir C++ modülü, düşük seviye bir modül için başlık dosyasını içeriyorsa, uygulama bu modülün detayları. Ve bu, belli ki, iyi bir şey değil. Ancak bu, günümüzde yaygın olarak kullanılan daha modern dillerde bir sorun değildir.

Yüksek seviyeli modüller düşük seviyeli modüllere göre doğal olarak daha az yeniden kullanılabilirler çünkü eski normalde ikincisine göre daha fazla uygulama/içerik. Örneğin, bir UI ekranı uygulayan bir bileşen en üst düzeydedir ve ayrıca uygulamaya özeldir (tamamen?). Böyle bir bileşeni farklı bir uygulamada tekrar kullanmaya çalışmak tersine üretkendir ve yalnızca aşırı mühendislikle sonuçlanabilir.

Dolayısıyla, bir B bileşenine bağlı olan (A'ya bağlı olmayan) bir A bileşeninin aynı seviyesinde ayrı bir soyutlamanın oluşturulması, ancak eğer A bileşeni farklı uygulamalarda veya bağlamlarda yeniden kullanım için gerçekten yararlı olacaksa yapılabilir. Durum böyle değilse, DIP uygulamak kötü bir tasarım olacaktır.

9
Rogério

Temelde diyor ki:

Sınıf, belirli detaylara (uygulamalar) değil soyutlamalara (örneğin arayüz, soyut sınıflar) dayanmalıdır.

8
martin.ra

Bağımlılık Tersine Çevirme İlkesini belirtmenin daha açık bir yolu:

Karmaşık iş mantığını kapsayan modülleriniz doğrudan iş mantığını içeren diğer modüllere bağlı olmamalıdır. Bunun yerine, sadece basit verilere olan arayüzlere bağlı olmalıdırlar.

I.e., Logic sınıfınızı, genellikle insanların yaptığı gibi uygulamak yerine:

class Dependency { ... }
class Logic {
    private Dependency dep;
    int doSomething() {
        // Business logic using dep here
    }
}

gibi bir şey yapmalısın:

class Dependency { ... }
interface Data { ... }
class DataFromDependency implements Data {
    private Dependency dep;
    ...
}
class Logic {
    int doSomething(Data data) {
        // compute something with data
    }
}

Data ve DataFromDependency, Logic ile değil, Dependency ile aynı modülde yaşamalıdır.

Bunu neden yapıyorsun?

  1. İki iş mantığı modülü şimdi ayrıştırılmıştır. Dependency değiştiğinde, Logic değiştirmenize gerek yoktur. 
  2. Logic'nin ne yaptığını anlamak çok daha basit bir iştir: yalnızca ADT'ye benzeyen şeyler üzerinde çalışır.
  3. Logic artık daha kolay test edilebilir. Artık doğrudan sahte verilerle Data örneğini başlatabilir ve iletebilirsiniz. Alaycılara veya karmaşık test iskelelerine gerek yok.
5
mattvonb

İyi cevaplar ve iyi örnekler zaten burada başkaları tarafından verilmiştir.

DIP 'nin önemli olmasının nedeni, OO prensibinin "gevşek bir şekilde bağlı tasarım" olmasını sağlamasıdır.

Yazılımınızdaki nesneler, bazı nesnelerin düşük seviyeli nesnelere bağlı olarak en üst düzeyde olduğu sıradüzene girmemelidir. Düşük seviyeli nesnelerdeki değişiklikler, yazılımı değişim için çok kırılgan hale getiren üst düzey nesnelerinize geçer.

'Üst seviye' nesnelerinizin çok kararlı olmasını ve değişim için kırılgan olmamasını istiyorsunuz, bu yüzden bağımlılıkları tersine çevirmelisiniz.

5
Hace

Kontrolün tersine çevrilmesi (IoC), bir nesneye bağımlılığı için bir çerçeve istemekten ziyade, bağımlılığını dış bir çerçeve ile ele aldığı bir tasarım desenidir.

Geleneksel aramayı kullanan sözde kod örneği:

class Service {
    Database database;
    init() {
        database = FrameworkSingleton.getService("database");
    }
}

IoC kullanarak benzer kod:

class Service {
    Database database;
    init(database) {
        this.database = database;
    }
}

IoC'nin yararları:

  • Bir merkezi Çerçeveye bağımlılığınız yoktur, bu nedenle eğer istenirse, bu değiştirilebilir.
  • Nesneler enjeksiyonla yaratıldıklarından, tercihen Arayüzleri kullanılarak, bağımlılıkları Sahte versiyonlarıyla değiştiren birim Testlerini oluşturmak kolaydır.
  • Kod ayrılıyor.
5
Staale

Bağımlılık inversiyonunun amacı, yeniden kullanılabilir yazılım yapmaktır.

Buradaki fikir, birbirlerine dayanan iki kod parçası yerine, bazı soyutlanmış arayüzlere dayanmalarıdır. O zaman herhangi bir parçayı diğeri olmadan tekrar kullanabilirsiniz.

Bunun en yaygın şekilde elde edilmesinin yolu, Spring in Java gibi bir kontrol (IoC) kabının ters çevrilmesidir. Bu modelde, nesnelerin özellikleri dışarı çıkan ve bağımlılıklarını bulmak yerine XML yapılandırmasıyla kurulur.

Bu sahte kodu düşünün ...

public class MyClass
{
  public Service myService = ServiceLocator.service;
}

Sınıfım doğrudan hem Servis sınıfına hem de ServiceLocator sınıfına bağlıdır. Başka bir uygulamada kullanmak istiyorsanız ikisine de ihtiyacı var. Şimdi bunu hayal et ...

public class MyClass
{
  public IService myService;
}

Şimdi, MyClass IService arayüzü olan tek bir arayüze güveniyor. IoC kabının gerçekte bu değişkenin değerini ayarlamasına izin verdik.

Şimdi, MyClass diğer iki sınıfın bağımlılığını da beraberinde getirmeden başka projelerde kolayca yeniden kullanılabilir.

Daha da iyisi, MyService'in bağımlılıklarını ve bu bağımlılıkların bağımlılıklarını sürüklemeniz gerekmez ve ... şey, fikir edinirsiniz.

1
Marc Hughes

Kontrol Konteynerlerinin Tersine Çevirilmesi ve Bağımlılık Enjeksiyon Kalıbı Martin Fowler tarafından da iyi okunur. Baş İlk Tasarım Desenlerini , DI ve diğer kalıpları öğrenme ilk baskım için harika bir kitap buldum.

1
Chris Canal

Sanırım çok daha iyi (daha sezgisel) bir örneğim var.

  • Çalışan ve iletişim yönetimi ile bir sistem (webapp) düşünün (iki ekran).
  • Tam olarak ilişkili değillerdir, böylece her birini kendi modülünde/klasöründe istersiniz.

Böylece, hem Çalışan yönetim modülü hem de İletişim yönetimi modülü hakkında bilmeniz gereken bazı "ana" giriş noktalarına sahip olacaksınız ve navigasyonda bağlantılar sağlamak ve api isteklerini, vb. Kabul etmek zorunda kalacaktı. ana modül bu ikişeye bağlı olacaktır - denetleyicileri, rotaları ve (paylaşılan) navigasyonda yapılması gereken bağlantıları hakkında bilgi sahibi olur.

Node.js örneği

// main.js
import express from 'express'

// two modules, each having many exports
import { api as contactsApi, navigation as cNav } from './contacts/'
import { api as employeesApi, navigation as eNav } from './employees/'

const api = express()
const navigation = {
  ...cNav,
  ...eNav
}

api.use('contacts', contactsApi)
api.use('employees', employeesApi)

// do something with navigation, possibly do some other setup

Ayrıca, lütfen dikkat: Tamamen iyi olduğunda durumlar var (basit olanlar).


Bu yüzden zamanla yeni modüller eklemek çok önemsiz olmadığı bir noktaya gelecektir. API, navigasyon, belki de permections 'i kaydetmeyi unutmamalısınız ve bu main.js daha da büyür.

Ve bu bağımlılık inversiyonunun gerçekleştiği yer. Ana modülünüzün diğer tüm modüllere bağımlı olması yerine, bazı "çekirdek" ler tanıtacak ve her modülün kendisini kaydettireceksiniz.

Bu durumda bu, kendisini birçok hizmete (güzergahlar, navigasyon, izinler) sunabilen bazı ApplicationModule kavramına sahip olmakla ilgilidir ve ana modül basit kalabilir (sadece modülü içe aktarıp kurmasına izin verir).

Başka bir deyişle, takılabilir mimari yapmakla ilgilidir. Bu fazladan bir çalışma ve kod yazmak/okumak ve sürdürmek zorunda kalacaksınız, bu yüzden önceden yapmamalısınız, aksine bu tür bir koku aldığınızda.

Özellikle ilginç olan şey, herhangi bir eklentiyi, kalıcılık katmanını bile - yani birçok kalıcılık uygulamasını desteklemeniz gerekiyorsa yapmaya değer olabilir, ancak bu genellikle böyle olmaz. Altıgen mimariye sahip görüntü için diğer cevabı görün, gösterim için harika - bir çekirdek var ve her şey aslında bir eklenti.

0
Kamil Tomšík

Bağımlılık inversiyonu: Soyutlamalara bağlı olarak, betonlara değil.

Kontrolün tersine çevrilmesi: Ana - Soyutlama ve Ana'nın sistemlerin tutkalı olması.

DIP and IoC

Bunlar, bunun hakkında konuşmak iyi yazılar:

https://coderstower.com/2019/03/26/dependency-inversion-why-you-shouldnt-avoid-it/

https://coderstower.com/2019/04/02/main-and-abstraction-the-decoupled-peers/

https://coderstower.com/2019/04/09/inversion-of-control-putting-all-together/

Genelde iyi cevapların telaşı ekleyerek, iyi ve kötü uygulamaları göstermek için kendime küçük bir örnek eklemek istiyorum. Ve evet, taş atan biri değilim!

Diyelim ki, küçük bir programın bir dizgeyi base64 formatına dönüştürmesi konsol G/Ç aracılığıyla istiyorum. İşte saf yaklaşım:

class Program
{
    static void Main(string[] args)
    {
        /*
         * BadEncoder: High-level class *contains* low-lever I/O functionality.
         * Hence, you'll have to fiddle with BadEncoder whenever you want to change
         * the I/O mode or details. Not good. A good encoder should be I/O-agnostic --
         * problems with I/O shouldn't break the encoder!
         */
        BadEncoder.Run();            
    }
}

public static class BadEncoder
{
    public static void Run()
    {
        Console.WriteLine(Convert.ToBase64String(Encoding.UTF8.GetBytes(Console.ReadLine())));
    }
}    

DIP temel olarak, yüksek kaldıraç bileşenlerinin düşük seviyeli uygulamaya bağlı olmaması gerektiğini söylüyor; burada "seviye", Robert C. Martin'e ("Temiz Mimari") göre G/Ç ile arasındaki mesafedir. Ama bu çıkmazdan nasıl kurtulursun? Basitçe merkezi Kodlayıcıyı, bunların nasıl uygulandığını zahmet etmeden sadece arayüzlere bağımlı hale getirerek:

class Program
{
    static void Main(string[] args)
    {           
        /* Demo of the Dependency Inversion Principle (= "High-level functionality
         * should not depend upon low-level implementations"): 
         * You can easily implement new I/O methods like
         * ConsoleReader, ConsoleWriter without ever touching the high-level
         * Encoder class!!!
         */            
        GoodEncoder.Run(new ConsoleReader(), new ConsoleWriter());            
    }
}

public static class GoodEncoder
{
    public static void Run(IReadable input, IWriteable output)
    {
        output.WriteOutput(Convert.ToBase64String(Encoding.ASCII.GetBytes(input.ReadInput())));            
    }
}

public interface IReadable
{
    string ReadInput();
}

public interface IWriteable
{
    void WriteOutput(string txt);
}

public class ConsoleReader : IReadable
{
    public string ReadInput()
    {
        return Console.ReadLine();
    }
}

public class ConsoleWriter : IWriteable
{
    public void WriteOutput(string txt)
    {
        Console.WriteLine(txt);
    }
}

G/Ç modunu değiştirmek için GoodEncoder öğesine dokunmanız gerekmediğini unutmayın; bu sınıf bildiği G/Ç arabirimlerinden memnun; IReadable ve IWriteable öğelerinin düşük düzeydeki uygulamaları hiç rahatsız etmeyecek.

0
John Silence

Diğer cevaplara ek olarak ....

Önce bir örnek vereyim.

Bir Jeneratör için malzemeleri için soran bir Otel olmasına izin verin. Otel, yemeğin adını (örneğin tavuk) Gıda Jeneratörü'ne verir ve Jeneratör, istenen yiyecekleri Otel'e geri gönderir. Ancak Otel aldığı ve servis ettiği yiyecek türünü önemsemiyor. Böylece Jeneratör, yiyecekleri Otele "Yiyecek" etiketi ile tedarik eder.

Bu uygulama Java’da

Fabrika Yöntemi ile FactoryClass. Gıda Üreticisi

public class FoodGenerator {
    Food food;
    public Food getFood(String name){
        if(name.equals("fish")){
            food =  new Fish();
        }else if(name.equals("chicken")){
            food =  new Chicken();
        }else food = null;

        return food;
    }
}


Bir Soyut/Arayüz Sınıfı

public abstract class Food {

    //None of the child class will override this method to ensure quality...
    public void quality(){
        String fresh = "This is a fresh " + getName();
        String tasty = "This is a tasty " + getName();
        System.out.println(fresh);
        System.out.println(tasty);
    }
    public abstract String getName();
}


Tavuk Yiyecekleri uygular (Beton Sınıfı)

public class Chicken extends Food {
    /*All the food types are required to be fresh and tasty so
     * They won't be overriding the super class method "property()"*/

    public String getName(){
        return "Chicken";
    }
}


Balık Yiyecekleri Uygular (Beton Sınıfı)

public class Fish extends Food {
    /*All the food types are required to be fresh and tasty so
     * They won't be overriding the super class method "property()"*/

    public String getName(){
        return "Fish";
    }
}


En sonunda

Otel

public class Hotel {

    public static void main(String args[]){
        //Using a Factory class....
        FoodGenerator foodGenerator = new FoodGenerator();
        //A factory method to instantiate the foods...
        Food food = foodGenerator.getFood("chicken");
        food.quality();
    }
}

Gördüğünüz gibi Otel bunun bir Tavuk Nesnesi veya Balık Nesnesi olup olmadığını bilmiyor. Sadece bunun bir Gıda Nesnesi olduğunu bilir, yani Otel, Gıda Sınıfına bağlıdır.

Ayrıca, Balık ve Tavuk Sınıfının Gıda Sınıfını uyguladığını ve doğrudan Otel ile ilgili olmadığını fark edersiniz. yani, Tavuk ve Balık da Gıda Sınıfına bağlıdır.

Bu, Yüksek seviye bileşeninin (Otel) ve Düşük seviye bileşeninin (Balık ve Tavuk) her ikisinin de bir soyutlamaya (Gıda) bağlı olduğunu gösterir.

Buna Bağımlılık İnversiyonu denir. 

0
Revolver

Bağımlılık İnversiyon Prensibi (DIP) diyor ki 

i) Yüksek seviye modülleri, düşük seviye modüllerine bağlı olmamalıdır. Her ikisi de soyutlamalar bağlı olmalıdır.

ii) Soyutlamalar asla ayrıntılara bağlı olmamalıdır. Detaylar soyutlamaya bağlı olmalıdır.

Örnek: 

    public interface ICustomer
    {
        string GetCustomerNameById(int id);
    }

    public class Customer : ICustomer
    {
        //ctor
        public Customer(){}

        public string GetCustomerNameById(int id)
        {
            return "Dummy Customer Name";
        }
    }

    public class CustomerFactory
    {
        public static ICustomer GetCustomerData()
        {
            return new Customer();
        }
    }

    public class CustomerBLL
    {
        ICustomer _customer;
        public CustomerBLL()
        {
            _customer = CustomerFactory.GetCustomerData();
        }

        public string GetCustomerNameById(int id)
        {
            return _customer.GetCustomerNameById(id);
        }
    }

    public class Program
    {
        static void Main()
        {
            CustomerBLL customerBLL = new CustomerBLL();
            int customerId = 25;
            string customerName = customerBLL.GetCustomerNameById(customerId);


            Console.WriteLine(customerName);
            Console.ReadKey();
        }
    }

Not: Sınıf, belirli ayrıntılara değil (arayüzün uygulanması) arayüz veya soyut sınıflar gibi soyutlamalara dayanmalıdır.

0
Rejwanul Reja