KATI Prensipler ve kod yapısı


150

Yakın tarihli bir iş görüşmesinde, çeşitli ilkelerin temel anlamını sağlamanın ötesinde, SOLID ile ilgili bir soruya cevap veremedim . Bu beni gerçekten rahatsız ediyor. Birkaç gün etrafta kazmaya değer bir şey yaptım ve henüz tatmin edici bir özet bulamadım.

Görüşme sorusu şuydu:

SOLID prensiplerine kesinlikle uyduğunuzu söylediğim bir Net projesine bakacak olsaydınız, proje ve kod yapısı açısından ne görmeyi beklerdiniz?

Biraz etrafımı karıştırdım, soruyu gerçekten cevaplamadım ve bombalandı.

Bu soruyu nasıl daha iyi ele alabilirdim?


3
Ben de belli değil merak ediyorum KATI için wiki sayfası
BЈовић

Genişletilebilir Soyut Yapı Taşları.
rwong

Nesneye Dayalı Tasarım'ın SOLID İlkelerini takip ederek, sınıflarınız doğal olarak küçük, iyi faktörlü ve kolayca test edilebilir. Kaynak: docs.asp.net/en/latest/fundamentals/…
WhileTrueSleep

Yanıtlar:


188

S = Tek Sorumluluk İlkesi

Bu yüzden iyi organize edilmiş bir klasör / dosya yapısı ve Nesne Hiyerarşisi görmeyi bekliyorum. Her işlev sınıfı / işlevsellik, işlevselliklerinin çok açık olduğu adlandırılmalıdır ve yalnızca bu görevi gerçekleştirmek için mantık içermelidir.

Binlerce kod satırına sahip dev yönetici sınıfları görürseniz, bu tek bir sorumluluğun takip edilmediğinin bir işareti olacaktır.

O = Açık / Kapalı Prensibi

Bu, temel olarak, mevcut işlevselliğin değiştirilmesi / üzerinde değişiklik gerektiren minimum etkiye sahip yeni sınıflar aracılığıyla yeni işlevselliklerin eklenmesi gerektiği düşüncesidir.

Bir işlevsellik tasarımını asıl uygulamadan ayırmak için başkalarının yanlarına gelip uygulamalarını etkilemeden uygulamalarını ayırmak için nesne kalıtımı, alt yazma, arayüzler ve soyut sınıfların birçok kullanımını görmeyi beklerdim. orijinal.

L = Liskov ikame prensibi

Bunun alt türleri üst tür olarak ele alma becerisiyle ilgisi var. Doğru bir kalıtsal nesne hiyerarşisi uyguluyorsanız C # içindeki kutudan çıkar.

Ortak nesneleri temel tür olarak ele almanın kodunu ve alt türlerin kendilerine örnek vermek ve çalışmak yerine temel / soyut sınıflarda çağrı yöntemlerini görmeyi beklerdim.

I = Arayüz Ayrıştırma İlkesi

Bu, SRP'ye benzer. Temel olarak, daha küçük işlevsellik alt kümelerini arabirimler olarak tanımlar ve sisteminizi ayrı tutmak için çalışanlarla çalışır (örneğin FileManager, Dosya G / Ç ile ilgili tek bir sorumluluk üstlenebilir, ancak bu , okuma için belirli yöntem tanımlarını içeren IFileReaderve uygulayabilen) IFileWriterve dosyaların yazılması).

D = Bağımlılık İnversiyon Prensibi.

Yine, bu bir sistemin ayrı tutulması ile ilgilidir. Belki de çözümde kullanılmakta olan bir .NET Bağımlılık Enjeksiyonu kütüphanesinin, Unityya Ninjectda ya da bunun gibi bir ServiceLocator sisteminin kullanılmasında aranıyor olmalısınız AutoFacServiceLocator.


36
C # 'da çok sayıda LSP ihlali gördüm, ne zaman birisi kendi özel alt tipinin uzmanlaştığına karar verdiğinde ve bu yüzden arayüzün bir parçasını uygulamaya gerek duymaz ve sadece bu parçaya istisna atar ... Bu ortak bir gençlik yaklaşımıdır. yanlış anlama arayüzü uygulaması ve tasarımı sorununu çözme
Jimmy Hoffa

2
@JimmyHoffa Bu, Kod Sözleşmelerini kullanmakta ısrar etmemdeki temel nedenlerden biri; Sözleşmeleri tasarlamanın düşünce sürecinden geçmek, insanları bu kötü alışkanlıktan kurtarmaya yardımcı olur.
Andy,

12
"LSP C # kutusundan çıkıyor" ve DIP'yi Bağımlılık enjeksiyon uygulamasına eşitlemekten hoşlanmıyorum.
Euphoric

3
+1 ancak Bağımlılık Ters Çevirme <> Bağımlılık Enjeksiyonu. Birlikte iyi oynuyorlar, ancak bağımlılık inversiyonu sadece bağımlılık enjeksiyonundan çok daha fazlası. Referans: vahşi DIP
Marjan Venema

3
@Andy: Bunun yanı sıra, tüm uygulayıcıların (mümkün olan / başlatılan herhangi bir sınıf) test edildiği arabirimlerde tanımlanan birim testleri de yardımcı olur.
Marjan Venema

17

Her yere bağımlılık enjeksiyonlu birçok küçük sınıf ve arayüz. Muhtemelen büyük bir projede, tüm bu küçük nesnelerin ömrünü oluşturmanıza ve yönetmenize yardımcı olmak için bir IoC çerçevesi kullanırsınız. Bkz https://stackoverflow.com/questions/21288/which-net-dependency-injection-frameworks-are-worth-looking-into

STRICTLY'nin SOLID ilkelerini takip ettiği büyük bir .NET projesinin mutlaka herkes için çalışmak için iyi bir kod tabanı anlamına gelmediğine dikkat edin. Görüşme yapan kişinin kim olduğuna bağlı olarak, SOLID'in ne anlama geldiğini anladığınızı göstermenizi ve / veya tasarım ilkelerini ne kadar dogmatik olarak takip ettiğinizi kontrol etmenizi isteyebilir.

Görüyorsunuz, KATI olmak için takip etmeniz gerekiyor:

S ingle sorumluluk ilkesi, bunlardan her biri tek bir şey yapıyor çok sayıda küçük sınıflar olacak bu yüzden sadece

O .NET genellikle de I ve aşağıdaki D gerektirir bağımlılık enjeksiyon ile uygulanmaktadır kalem kapalı ilkesi, ...

L iskov sübstitüsyon ilkesinin c # ile bir liner ile açıklanması imkânsızdır. Neyse ki bunu ele alan başka sorular var, örneğin https://stackoverflow.com/questions/4428725/can-you-explain-liskov-substitution-principle-with-a-good-c-sharp-example

Ben ayrılığı prensibi Açık-Kapalı ilkesine birlikte çalışır nterface. Kelimenin tam anlamıyla izlenmesi halinde, birkaç "büyük" arabirim yerine çok sayıda çok küçük arabirimin tercih edilmesi anlamına gelir.

D üst düzey sınıfları düşük seviyeli sınıfları bağımlı olmamalıdır ependency ters ilişki ilkesi, her iki soyutlamaların bağlı olmalıdır.


SRP, "sadece bir şey yap" anlamına gelmez.
Robert Harvey

13

Günlük işlerinde SOLID kullanan bir mağazanın kod tabanında görmeyi beklediğim bazı temel şeyler:

  • Birçok küçük kod dosyası - .NET'te en iyi uygulama olarak dosya başına bir sınıf ve küçük modüler sınıf yapılarını teşvik eden Tek Sorumluluk İlkesi ile her biri küçük, odaklanmış bir sınıf içeren birçok dosya görmeyi beklerim.
  • Çok sayıda Adaptör ve Kompozit kalıp - Çok sayıda Adaptör kalıbının (farklı bir arayüzün fonksiyonelliğine "geçiş yapan" bir arayüz uygulayan bir sınıf) kullanılmasını beklerdim, bir amaç için geliştirilen bir bağımlılıktan hafifçe tıkanmayı kolaylaştırmak için işlevselliğinin de gerekli olduğu farklı yerler. Bir konsol günlüğünü bir dosya günlüğüyle değiştirmek kadar basit olan güncellemeler, arayüz kullanılacak dosya adını belirtmek için bir araç ortaya çıkarmak üzere güncellenirse LSP / ISP / DIP'yi ihlal edecektir; bunun yerine, dosya günlüğü sınıfı ek üyeleri ortaya çıkarır ve ardından bir Adaptör yeni şeyleri gizleyerek dosya günlüğünün konsol günlüğü gibi görünmesini sağlar, böylece yalnızca tüm bunları birbirine geçiren nesnenin farkı bilmesi gerekir.

    Benzer şekilde, bir sınıf mevcut nesneyle benzer bir arayüze bağımlılık eklemek istediğinde, nesneyi (OCP) değiştirmekten kaçınmak için normal cevap, bir Bileşik / Strateji modeli uygulamaktır (bağımlılık arayüzünü uygulayan ve birden fazla diğerini tüketen bir sınıf) Bu ara yüzün uygulamaları, sınıfın bir çağrıyı bir, bir veya bir kısmına ya da tamamına uygulamasına geçirmesine izin veren değişken miktarlarda mantıkla).

  • Çok sayıda arayüz ve ABC - DIP mutlaka soyutlamaların yapılmasını gerektirir ve ISS bunları dar kapsamlı olarak teşvik etmeyi teşvik eder. Bu nedenle, arayüzler ve soyut temel sınıfları kuraldır ve kod tabanınızın paylaşılan bağımlılık işlevselliğini ele almak için bunlardan çoğuna ihtiyacınız vardır. Her ne kadar katı SOLID her şeyi enjekte etmek zorunda olsa da , bir yerde yaratmanız gerektiği açıktır ve eğer bir GUI formu sadece bir ebeveyne ait bir çocuk olarak oluşturulmuşsa, söz konusu ebeveyne bir miktar eylem uygulayarak, çocuk formunu yenileyecek hiçbir yeterliliğim yoktur. doğrudan ebeveynin içindeki koddan. Bu kodu genellikle kendi yöntemiyle yapıyorum, bu yüzden aynı formdaki iki işlem pencereyi açtıysa, yalnızca yöntemi çağırırım.
  • Birçok proje - Tüm bunların amacı, değişimin kapsamını sınırlamaktır. Değişim, yeniden derleme ihtiyacını da içerir (artık nispeten önemsiz bir egzersiz, ancak mobil bir ortama güncellemeler dağıtma gibi işlemci ve bant genişliği açısından kritik işlemlerin çoğunda hala önemlidir). Bir projedeki bir dosyanın yeniden oluşturulması gerekiyorsa, tüm dosyalar yapar. Bu, arayüzleri uygulamalarıyla aynı kütüphanelere yerleştirirseniz, noktayı kaçırıyorsunuzdur; Eğer arayüzün bir uygulamasını değiştirirseniz tüm kullanımları yeniden derlemeniz gerekir, çünkü arayüz tanımının kendisini de derlersiniz, bu da kullanımların sonuçtaki ikilide yeni bir konuma işaret etmesini gerektirir. Bu nedenle, arayüzleri kullanımlardan ayrı tutmak ve uygulamalar, genel kullanım alanına göre ayrıca ayrılırken, tipik bir en iyi uygulamadır.
  • "Dörtlü Çete" terminolojisine çok dikkat edildi - 1994 Tasarım Desenleri kitabında tanımlanan tasarım desenleri , SOLID'in oluşturmak istediği ısırık büyüklüğü, modüler kod tasarımını vurgular. Bağımlılık İnversiyon Prensibi ve Açık / Kapalı Prensip, örneğin, bu kitapta tanımlanan kalıpların çoğunun merkezindedir. Bu nedenle, Dörtlü Çetenin kitabındaki terminolojiyi benimsemek için SOLID ilkelerine sıkı sıkıya bağlı bir mağazayı beklemeliyim ve sınıfları "AbcFactory", "XyzRepository", "DefToXyzAdapter gibi bu satırlar boyunca işlevlerine göre adlandırabilirim. "," A1Command "vb.
  • Genel Bir Depo - ISP, DIP ve SRP ile genel olarak anlaşıldığı gibi, Repository, SOLID tasarımında neredeyse her yerde bulunur; çünkü kod alma / sürdürme mekanizması hakkında özel bir bilgiye ihtiyaç duymadan, veri sınıflarını soyutlanmış bir şekilde talep etmeye izin verir. DAO düzeninin aksine bunu yapan bir yere koyar (örneğin, bir Fatura veri sınıfına sahip olsaydınız, aynı zamanda bu tip sulu nesneler üreten bir FaturaDAO'nuz da olurdu, vb.) kod veri tabanındaki / şemadaki tüm veri nesneleri / tabloları).
  • Bir IoC Konteyneri - Bağımlılık enjeksiyonumun çoğunu yapmak için aslında bir IoC çerçevesi kullanmadığım için bunu eklemek için tereddüt ediyorum. Hızlı bir şekilde, her şeyi kabın içine atma, onu sallama ve enjekte edilmiş bir fabrika yöntemiyle ihtiyacınız olan dikey olarak hidratlanmış bağımlılığı boşa çıkaran bir God Object anti-modeli olur. Yapının oldukça yekpare hale geldiğini ve "akıcı" ise kayıt bilgisine sahip olan projenin çözümünüzdeki her şey hakkında her şeyi bilmesi gerekene kadar harika görünüyor. Değişmesi için pek çok neden var. Akıcı değilse (config dosyalarını kullanan geç kayıtlar), programınızın bir anahtar parçası “sihirli dizgilere” dayanır, tamamen farklı solucanlar olabilir.

1
neden indirimler?
KeithS

Bence bu iyi bir cevap. Bu terimlerin ne olduğuna dair birçok blog gönderisine benzemek yerine, kullanımlarını ve değerlerini gösterecek örnekler ve açıklamalar listeledik
Crowie

10

Onları Jon Skeet’in SOLID’deki “O” nun “yararsızdır ve iyi anlaşılmadığını” tartışması ile dikkatinizi dağıtın ve onları Alistair Cockburn'ün “korunan varyasyonu” ve Josh Bloch'un “miras için tasarlama veya yasaklama” hakkında konuşmasını sağlayın.

Skeet'in makalesinin kısa özeti (orijinal blog gönderisini okumadan ismini yazmayı tavsiye etmeme rağmen!):

  • Çoğu insan, 'açık-kapalı' ilkesinde 'açık' ve 'kapalı' ifadelerinin ne anlama geldiğini bilmemektedir.
  • Yaygın yorumlar:
    • bu modüller her zaman uygulama mirası yoluyla genişletilmelidir, veya
    • Orijinal modülün kaynak kodunun hiçbir zaman değiştirilemeyeceğini
  • OCP'nin temel amacı ve Bertrand Meyer'in orijinal formülasyonu gayet iyi:
    • Bu modüllerin, müşterilerinin güvenebileceği iyi tanımlanmış arayüzlere (mutlaka 'arayüz' teknik anlamında değil) sahip olmaları gerekir, ancak
    • Bu arayüzleri bozmadan yapabileceklerini genişletmek mümkün olmalıdır.
  • Ancak "açık" ve "kapalı" kelimeleri, güzel bir kısaltma yapsalar bile konuyu karıştırırlar.

OP, “Bu soruyu nasıl daha iyi ele alabilirdim?” Diye sordu. Röportaj yapan kıdemli bir mühendis olarak, farklı kod tasarım stillerinin artıları ve eksileri hakkında, mermi noktaları listesinden çınlayan birinden zekice konuşabilen bir adayla daha fazla ilgilenirim.

Bir başka iyi cevap, "Eh, bu, ne kadar iyi anladıklarına bağlı olarak değişir. Tek bildikleri SOLID sözcükleri ise, kalıtımın kötüye kullanılması, bağımlılık enjeksiyon çerçevelerinin aşırı kullanımı, hiçbiri olmayan küçük milyonlarca arabirim kullanılması beklenir. Ürün yönetimi ile iletişim kurmak için kullanılan alan kelime dağarcığını yansıtmak .... "


6

Muhtemelen bunun çeşitli zaman dilimlerinde cevaplanmasının birkaç yolu vardır. Ancak bunun daha çok "SOLID'in ne anlama geldiğini biliyor musunuz?" Dolayısıyla, bu soruyu cevaplamak muhtemelen sadece puanları vurmaktan ve proje olarak açıklamaktan kaynaklanıyor.

Yani, aşağıdakileri görmeyi beklersiniz:

  • Sınıfların tek bir Sorumluluğu vardır (örneğin, müşteriler için veri erişim sınıfı yalnızca müşteri veritabanından müşteri verilerini alacaktır).
  • Sınıflar, mevcut davranışı etkilemeden kolayca genişletilebilir. Ek işlevsellik eklemek için özellikleri veya diğer yöntemleri değiştirmek zorunda değilim.
  • Temel sınıflar için türetilmiş sınıflar ikame edilebilir ve bu baz sınıflarını kullanan fonksiyonlar, baz sınıfının bunları ele almak için daha spesifik türe açmak zorunda kalmazlar.
  • Arayüzler küçük ve anlaşılması kolaydır. Bir sınıf bir arayüzden yararlanırsa, bir görevi başarmak için birkaç yönteme bağlı olması gerekmez.
  • Kurallar, üst düzey uygulamaların somut olarak düşük seviye uygulamalara somut olarak bağımlı olmayacağı şekilde yeterince soyutlanmıştır. Üst seviye kodu etkilemeden düşük seviye uygulamasını değiştirebilmeliyim. Örneğin, SQL veri erişim katmanımı Web tabanlı bir servis için uygulamamın geri kalanını etkilemeden değiştirebilirim.

4

Bu mükemmel bir soru, bunun zor bir görüşme sorusu olduğunu düşünmeme rağmen.

SOLID ilkeleri, sınıfları ve arayüzleri ve birbirleriyle olan ilişkilerini gerçekten yönetir.

Bu soru gerçekten dosyalarla ilgisi var ve mutlaka sınıfları değil.

Vereceğim kısa bir gözlem ya da cevap, genelde sadece bir arayüze sahip dosyaları göreceğiniz ve çoğu zaman konvansiyonun başkent-I ile başlayacağıdır. Bunun ötesinde, dosyaların çoğaltılmış kodlara sahip olmayacağından (özellikle bir modül, uygulama veya kitaplığın içinde) ve bu kodun modüller, uygulamalar veya kütüphaneler arasındaki belirli sınırlar boyunca dikkatlice paylaşılacağından bahsedeceğim.

Robert Martin, bu konuyu , Booch Metodunu Kullanarak Nesneye Yönelik C ++ Uygulamaları Tasarlamadaki C ++ alanında ( Uyum, Kapatma ve Yeniden Kullanılabilirlik bölümlerine bakınız) ve Temiz Kod'da tartışmaktadır .


.NET kodlayıcıları IME genellikle "dosya başına 1 sınıf" kuralını izler ve ayrıca klasör / ad alanı yapılarını yansıtır; Visual Studio IDE her iki uygulamayı da teşvik eder ve ReSharper gibi çeşitli eklentiler bunları zorlayabilir. Bu yüzden, sınıf / arayüz yapısını yansıtan bir proje / dosya yapısı görmeyi beklerdim.
KeithS
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.