Arabirim Ayrışma İlkesinin çelişkili iki tanımı - hangisi doğrudur?


14

ISS ile ilgili makaleleri okurken, ISS'nin birbiriyle çelişen iki tanımı var gibi görünüyor:

İlk tanıma göre (bkz. 1 , 2 , 3 ), ISS, arabirimi uygulayan sınıfların ihtiyaç duymadıkları işlevleri uygulamaya zorlanmaması gerektiğini belirtir. Böylece, yağ arayüzüIFat

interface IFat
{
     void A();
     void B();
     void C();
     void D();
}

class MyClass: IFat
{ ... }

daha küçük arayüzlere bölünmeli ISmall_1veISmall_2

interface ISmall_1
{
     void A();
     void B();
}

interface ISmall_2
{
     void C();
     void D();
}

class MyClass:ISmall_2
{ ... }

Bu yolla beri benim MyClasstek yöntemlerini ihtiyacı (uygulamak mümkün olan D()ve C()aynı zamanda kukla uygulamaları sağlamak zorunda olmadan,) A(), B()ve C():

Ancak ikinci tanıma göre (bkz. 1 , 2 , Nazar Merza'nın cevabı ), ISS MyClientçağırma yöntemlerinin ihtiyaç duymadığı MyServiceyöntemlerin farkında MyServiceolmaması gerektiğini belirtir. Diğer bir deyişle, eğer MyClientyalnızca işlevselliğini ihtiyacı C()ve D()daha sonra yerine

class MyService 
{
    public void A();
    public void B();
    public void C();
    public void D();
}

/*client code*/      
MyService service = ...;
service.C(); 
service.D();

MyService'syöntemleri istemciye özgü arabirimlere ayırmalıyız :

public interface ISmall_1
{
     void A();
     void B();
}

public interface ISmall_2
{
     void C();
     void D();
}

class MyService:ISmall_1, ISmall_2 
{ ... }

/*client code*/
ISmall_2 service = ...;
service.C(); 
service.D();

Böylece eski tanımla, ISP'nin amacı " IFat arayüzünü uygulayan sınıfların yaşamını kolaylaştırmak " iken, ISP'nin amacı " MyService yöntemlerini çağıran müşterilerin hayatını kolaylaştırmak " tır .

İSS'nin iki farklı tanımından hangisi gerçekten doğrudur?

@MARJAN VENEMA

1.

Bu yüzden IFat'ı daha küçük bir arayüze ayıracağınız zaman, hangi yöntemlerin üyelerin ne kadar uyumlu olduğuna bağlı olarak ISmallinterface'e karar vermesi gerekir.

Birbirine bağlı yöntemleri aynı arayüz içine koymak mantıklı olsa da, ISS modeli ile müşterinin ihtiyaçları bir arabirimin "yapışabilirliği" ne göre öncelik kazanıyor diye düşündüm. Başka bir deyişle, ISP ile belirli müşteriler için ihtiyaç duyulan yöntemleri aynı arayüzde toplamalıyız diye düşündüm.

Böylece, sadece hiç çağrısına ihtiyaç olacak istemcilerin bir sürü olsaydı CutGreens, ama aynı zamanda değil GrillMeat, o zaman ISS desen biz sadece koymalıyız uymaları CutGreensiçeride ICookdeğil, aynı zamanda GrillMeat, iki yöntem son derece yapışkan olmasına rağmen ?!

2.

Karışıklıklarınızın ilk tanımdaki gizli bir varsayımdan kaynaklandığını düşünüyorum: uygulayıcı sınıflar zaten tek sorumluluk ilkesini izliyor.

"SRP takip etmiyor uygulayan sınıflar" yle, uygulamak bu sınıflara kastediyoruz IFatveya uygulamak sınıflara ISmall_1/ ISmall_2? Uygulayan sınıflara atıfta bulunduğunuzu varsayıyorum IFat? Öyleyse, neden SRP'yi takip etmediklerini varsayıyorsunuz?

Teşekkürler


4
Neden her ikisi de aynı ilke tarafından sunulan birden fazla tanım olamaz?
Bobson

5
Bu tanımlar birbiriyle çelişmez.
Mike Partridge

1
Tabii ki müşterinin ihtiyacı bir arayüzün tutarlılığına göre öncelikli değildir. Bu "kural" yolunu çok ileriye götürebilir ve her yerde kesinlikle hiçbir anlam ifade etmeyen tek bir yöntem arabirimi ile sonuçlanabilir. Kuralları izlemeyi bırakın ve bu kuralların oluşturulduğu hedefleri düşünmeye başlayın. "SRP'yi takip etmeyen sınıflar" ile ben sizin örneğinizdeki belirli sınıflardan bahsetmiyordum ya da SRP'yi takip etmiyorlardı. Yeniden oku. İlk tanım yalnızca arabirim ISP'yi takip etmiyorsa ve sınıf SRP'yi takip ediyorsa bir arabirimin bölünmesine yol açar.
Marjan Venema

2
İkinci tanım uygulayıcıları umursamıyor. Arabirimleri arayanların bakış açısından tanımlar ve uygulayıcıların zaten var olup olmadıkları konusunda herhangi bir varsayımda bulunmaz. Muhtemelen ISS'yi takip ettiğinizde ve bu arayüzleri uygulamaya geldiğinizde, elbette, bunları oluştururken SRP'yi takip edeceğinizi varsayar.
Marjan Venema

2
Hangi müşterilerin var olacağını ve hangi yöntemlere ihtiyaç duyacaklarını önceden nasıl biliyorsunuz? Yapamazsın. Elden önce bildiğiniz şey arayüzünüzün ne kadar uyumlu olduğudur.
Tulains Córdova

Yanıtlar:


6

İkisi de doğru

Okuduğum şekilde, ISS'nin (Arayüz Segregasyon İlkesi) amacı arayüzleri küçük ve odaklanmış tutmaktır: tüm arayüz üyeleri çok yüksek bir kaynağa sahip olmalıdır. Her iki tanım da "tüm-jack-of-trades-master-of-none" arayüzlerinden kaçınmak içindir.

Arabirim ayrımı ve SRP (Tek Sorumluluk İlkesi) aynı amaca sahiptir: küçük, yüksek düzeyde uyumlu yazılım bileşenleri sağlamak. Birbirlerini tamamlarlar. Arabirim ayrımı, arabirimlerin küçük, odaklanmış ve yüksek düzeyde uyumlu olmasını sağlar. Tek sorumluluk ilkesine uymak, sınıfların küçük, odaklanmış ve yüksek düzeyde uyumlu olmasını sağlar.

Bahsettiğiniz ilk tanım uygulayıcılara, ikinci tanım istemcilere odaklanır. Hangi @ @ user61852 aksine, ben uygulayıcıları değil arayüzün kullanıcılar / arayanlar almak.

Karışıklıklarınızın ilk tanımdaki gizli bir varsayımdan kaynaklandığını düşünüyorum: uygulayıcı sınıflar zaten tek sorumluluk ilkesini izliyor.

Bana göre ikinci tanım, müşterilerle arayüzün arayanları olarak, amaçlanan hedefe ulaşmanın daha iyi bir yoludur.

ayrılan

Sorunuzda şunları belirtiyorsunuz:

bu şekilde MyClass'ım A (), B () ve C () için kukla uygulamalar yapmaya zorlanmadan yalnızca (D () ve C ()) yöntemlerini uygulayabildiğinden:

Ama bu dünyayı altüst ediyor.

  • Bir arabirimi uygulayan bir sınıf, uyguladığı arabirimde ihtiyaç duyduğu şeyi dikte etmez.
  • Arabirimler, bir uygulayıcı sınıfın hangi yöntemleri sağlaması gerektiğini belirler.
  • Bir arayüzün arayanları, arayüz için hangi işlevselliğe ihtiyaç duyduklarını ve dolayısıyla bir uygulayıcının ne sağlaması gerektiğini belirleyen kişilerdir.

Bu yüzden, IFatdaha küçük bir arayüze ayrılacağınız zaman , hangi yöntemlerin ISmallüyelerin ne kadar uyumlu olduğuna bağlı olarak hangi arayüzde karar verilmesi gerektiği ortaya çıkar.

Bu arayüzü düşünün:

interface IEverythingButTheKitchenSink
{
     void DoDishes();
     void CleanSink();
     void CutGreens();
     void GrillMeat();
}

Hangi yöntemleri uygularsınız ICookve neden? Eğer koymak istiyorsunuz CleanSinkile birlikte GrillMeatsadece bunu yapar bir sınıf ve diğer yöntemlerden herhangi gibi bir başka şeylerin çift başka bir şey var ne sırf? Veya aşağıdaki gibi iki daha uyumlu arayüze böler misiniz:

interface IClean
{
     void DoDishes();
     void CleanSink();
}

interface ICook
{
     void CutGreens();
     void GrillMeat();
}

Arayüz bildirim notu

Bir arayüz tanımı tercihen ayrı bir ünitede kendi başına olmalıdır, ancak mutlaka arayan veya uygulayıcı ile yaşaması gerekiyorsa, gerçekten arayan ile birlikte olmalıdır. Aksi takdirde arayan, arayüzlerin amacını tamamen yenen uygulayıcıya derhal bağımlı olur. Ayrıca bkz: Temel sınıfla aynı dosyada arabirim bildirme, iyi bir uygulama mı? ve Neden bunları uygulayan sınıflarla arabirimleri yerleştirmeliyiz? StackOverflow üzerinde.


1
Yaptığım güncellemeyi görebiliyor musun?
EdvRusj

"çağıran uygulayıcıya derhal bağımlı olur " ... sadece DIP'yi (bağımlılık tersine çevirme ilkesi) ihlal ederseniz, çağıran iç değişkenleri, parametreleri, dönüş değerleri vb. tip ICookyerine SomeCookImplementortipse, DIP zorunlu kıldıysa, bağımlı olmak zorunda değil SomeCookImplementor.
Tulains Córdova

@ user61852: Arabirim bildirimi ve uygulayıcı aynı birimdeyse, hemen bu uygulayıcıya bağımlı olurum. Çalışma zamanında değil, kesinlikle proje düzeyinde, sadece orada olması gerçeğiyle. Proje artık onsuz veya ne kullanıyorsa derleyemez. Ayrıca, Bağımlılık Enjeksiyonu Bağımlılık Ters Çevirme İlkesi ile aynı değildir. Eğer vahşi doğada DIP
Marjan Venema

Kod örneklerinizi bu soruda yeniden kullandım programmers.stackexchange.com/a/271142/61852 , kabul edildikten sonra geliştirdim. Örnekler için gerekli krediyi verdim.
Tulains Córdova

Cool @ user61852 :) (ve krediniz için teşekkürler)
Marjan Venema

14

Dörtlü Gang belgelerinde kullanılan "istemci" kelimesini, bir hizmetin tüketicisinde olduğu gibi bir "istemci" ile karıştırıyorsunuz.

Bir "istemci", Gang of Four tanımlarının amaçladığı gibi, bir arabirim uygulayan bir sınıftır. A sınıfı B arabirimini uygularsa, A'nın B'nin bir müşterisi olduğunu söylerler. Aksi takdirde "istemciler, kullanmadıkları arabirimleri uygulamaya zorlanmamalıdır" ifadesi, "müşteriler" (tüketicilerde olduğu gibi) hiçbir şey uygulamıyor. Bu ifade yalnızca "müşteri" yi "uygulayıcı" olarak gördüğünüzde anlamlıdır.

"İstemci", büyük arayüzü uygulayan başka bir sınıfın yöntemlerini "tüketen" (çağıran) bir sınıf anlamına geliyorsa, önemsediğiniz iki yöntemi çağırarak ve geri kalanını görmezden gelmek, sizi diğerlerinden ayırmak için yeterli olacaktır. kullanmadığınız yöntemler.

İlkenin ruhu, yalnızca ilgili bir dizi yöntemi önemsediğinde tüm arabirime uymak için "istemci" nin (arabirimi uygulayan sınıf) kukla yöntemler uygulamaktan kaçınmasıdır.

Ayrıca, mümkün olan daha az sayıda bağlantının olması, bir yerde yapılan değişikliklerin daha az etkiye neden olmasını amaçlamaktadır. Arabirimleri ayırarak kaplini azaltırsınız.

Bu sorunlar, arabirim çok fazla iş yaptığında ve yalnızca bir yerine birkaç arabirime bölünmesi gereken yöntemlere sahip olduğunda ortaya çıkar.

Her iki kod örneğiniz de iyi . Sadece ikincisinde "istemci" nin "başka bir sınıf tarafından sunulan hizmetleri / yöntemleri tüketen / çağıran bir sınıf" anlamına geldiğini varsayarsınız.

Verdiğiniz üç bağlantıda açıklanan kavramlarda hiçbir çelişki bulamıyorum.

Sadece açık tutmak "müşteri" gerçekleştirmecisidir KATI konuşmasında,.


Ancak @pdr'ye göre, tüm bağlantılardaki kod örnekleri ISS'ye bağlıyken, ISS tanımı "istemciyi (başka bir sınıfın yöntemlerini çağıran bir sınıf) hizmet hakkında daha fazla bilgi sahibi olmaktan" daha fazla " istemedikleri arayüzleri uygulamaya zorlanan müşterilerden (uygulayıcılardan) korunma. "
EdvRusj

1
@EdvRusj Cevabım, Martin'in ünlü Gang of Four'dayken yazdığı Object Mentor (Bob Martin Enterprise) web sitesindeki belgelere dayanıyor. Bildiğiniz gibi, Gnag of Four, Martin dahil, SOLID kısaltmasını oluşturan bir grup yazılım mühendisiydi, ilkeleri belirledi ve belgeledi. docs.google.com/a/cleancoder.com/file/d/…
Tulains Córdova

Yani @pdr ile aynı fikirde değilsiniz ve böylece ISS'nin ilk tanımını (orijinal yazıma bakın) daha uygun buluyor musunuz?
EdvRusj

@EdvRusj Her ikisinin de doğru olduğunu düşünüyorum. Ancak ikincisi, istemci / sunucu metaforunu kullanarak gereksiz karışıklık yaratır. Birini seçmek zorunda kalırsam, resmi Gang of Four'la giderdim, ilk olan. Ama sonuçta, SOLID ilkelerinin ruhu olan eşleşmeyi ve gereksiz bağımlılıkları azaltmak önemlidir. Hangisinin doğru olduğu önemli değil. Önemli olan, arayüzlere göre davranışları ayırmanızdır. Bu kadar. Ancak şüphe duyduğunuzda, sadece orijinal kaynağa gidin.
Tulains Córdova

3
"Müşteri" nin SOLID konuşmasında uygulayıcı olduğu iddiasına katılmıyorum. Birincisi, bir sağlayıcıya (uygulayıcı) sağladığı (uygulayan) müşteriyi aramak dilsel saçmalıktır. Ayrıca, SOLID hakkında bunu aktarmaya çalışan hiçbir makale görmedim, ancak bunu kaçırmış olabilirim. En önemlisi, bir arabirimin uygulayıcısını arabirimde ne olması gerektiğine karar veren olarak ayarlar. Ve bu benim için bir anlam ifade etmiyor. Bir arabirimin arayanları / kullanıcıları, bir arabirimden neye ihtiyaç duyduklarını tanımlar ve bu arabirimin uygulayıcıları (çoğul) bunu sağlamak zorundadır.
Marjan Venema

5

İSS, müşterinin hizmet hakkında bilmesi gerekenden daha fazla şey bilmesini sağlamakla ilgilidir (örneğin, ilgisiz değişikliklere karşı korumak). İkinci tanımınız doğru. Okuduğum kadarıyla, bu üç makaleden sadece biri aksini öneriyor ( birincisi ) ve sadece yanlış. (Düzenleme: Hayır, yanlış değil, sadece yanıltıcı.)

İlk tanım LSP ile çok daha sıkı bağlantılıdır.


3
ISS'de istemciler kullanmadığı arabirim bileşenlerini TÜKETMEYE zorlanmamalıdır. LSP'de, SERVICES yöntem D'yi uygulamak zorunda bırakılmamalıdır, çünkü çağıran kod A yöntemi gerektirir. Çelişkili değil, tamamlayıcıdırlar.
pdr

2
@EdvRusj, ClientA'nın çağırdığı InterfaceA'yı uygulayan nesne aslında İstemci B'nin ihtiyaç duyduğu InterfaceB'yi uygulayan aynı nesne olabilir. Aynı istemcinin farklı Sınıflarla aynı nesneyi görmesi gereken nadir durumlarda, kod genellikle "dokunma". Buna bir amaç için A ve diğer amaç için B olarak bakacaksınız.
Amy Blankenship

1
@EdvRusj: Burada arayüz tanımınızı yeniden düşünmeniz yardımcı olabilir. Her zaman C # / Java terimleriyle bir arayüz değildir. İstemci A, X hizmetiyle "arabirim" için sarıcı sınıf AX kullanacak şekilde, etrafına sarılmış birkaç basit sınıfla karmaşık bir hizmete sahip olabilirsiniz. Bu nedenle, X'i A ve AX'ı etkileyecek şekilde değiştirdiğinizde, BX ve B'yi etkilemeye zorlandı
pdr

1
@EdvRusj: A ve B'nin hem X'i mi yoksa biri Y'yi mi, diğeri de Z'yi aradığını umursamayacaklarını söylemek daha doğru olur. BU ISS'nin temel noktasıdır. Böylece hangi uygulamayı seçeceğinizi seçebilir ve daha sonra fikrinizi kolayca değiştirebilirsiniz. İSS bir rotayı ya da diğerini desteklemez, ancak LSP ve SRP bunu yapabilir.
pdr

1
@EdvRusj Hayır, A istemcisi Service X'i Service y ile değiştirebilir, her ikisi de AX arabirimini uygular. X ve / veya Y diğer Arabirimleri uygulayabilir, ancak İstemci bunları AX olarak adlandırdığında, diğer Arabirimlerle ilgilenmez.
Amy Blankenship
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.