Her sınıfın sadece bir sorumluluğu olduğundan emin olun, neden?


37

Microsoft belgelerine göre, Wikipedia SOLID ilke makalesi veya çoğu BT mimarı, her sınıfın yalnızca bir sorumluluğunun olmasını sağlamalıyız. Nedenini bilmek istiyorum, çünkü eğer herkes bu kurala katılıyorsa kimse bu kuralın nedenleri konusunda hemfikir değil gibi görünüyor.

Bazıları daha iyi bakım sunulduğunu, bazılarının kolay test edildiğini veya sınıfın daha sağlam veya güvenli olduğunu belirtti. Doğru ve ne anlama geliyor? Bakım neden daha iyi, testler daha kolay mı yoksa kod daha sağlam mı?



1
Sizin de ilgili bulabileceğiniz bir sorum vardı: Bir sınıfın asıl sorumluluğu nedir?
Pierre Arlaud

3
Tek sorumluluğun tüm nedenleri doğru olamaz mı? Bakımı kolaylaştırır. Testi kolaylaştırır. Sınıfı daha sağlam kılar (genel olarak).
Martin York

2
Aynı sebepten dolayı AT ALL sınıfları var.
Davor Ždralo

2
Bu bildirimle her zaman bir sorunum oldu. Tek bir sorumluluğun ne olduğunu tanımlamak çok zor. Tek bir sorumluluk, "1 + 1 = 2" olduğunu doğrulamak ile "kurumsal bankacılık hesaplarına ödenen tüm paraların doğru bir kaydını tutmak" arasında olabilir.
Dave Nay

Yanıtlar:


57

Modülerlik. Herhangi bir makul dil size kod parçalarını bir arada yapıştırma imkanı verecektir, ancak programcı kaynak üzerinde ameliyat gerçekleştirmeden büyük bir kod parçasını yapıştırmanın genel bir yolu yoktur. Bir kod yapısına birçok görevi sıkıştırarak kendinizi ve başkalarını parçalarını başka şekillerde bir araya getirme fırsatını soyar ve bir parçanın diğerlerini etkileyebilecek değişikliklere neden olabilecek gereksiz bağımlılıklar ortaya çıkarırsınız.

SRP, sınıflar için olduğu kadar işlevler için de geçerlidir, ancak genel OOP dilleri birbirine yapıştırma işlevlerinde göreceli olarak zayıftır.


12
+ 1'i, işlevsel programlamayı, soyutlamaları oluşturmanın daha güvenli bir yolu olarak belirtmek için.
logc

> fakat ana akım OOP dilleri bir arada yapıştırma işlevlerinde nispeten zayıftır.
Jeff Hubbard

@JeffHubbard C / C ++ 'dan sonra kullandıkları için demek istediğini düşünüyorum. Onları fonksiyonlarla karşılıklı olarak özel yapan nesneler hakkında hiçbir şey yoktur. Cehennem, bir nesne / arayüz sadece fonksiyonların bir kaydı / yapısıdır. İşlevlerin önemsiz olması, hem teoride hem de pratikte önemsizdir. Zaten bunlardan kaçınılmazlar - sonunda "Runnables", "Fabrikalar", "Sağlayıcılar" ve "Eylemler" etrafında atmak zorunda kalmazsınız ya da "Strateji ve Şablon Yöntemi" kalıplarını denemek zorunda kalırsınız. aynı işi yapmak için gereksiz bir demet bir demet.
Doval

@Doval Hayır, OOP'un mesajlarla ilgilendiğini gerçekten kastediyorum. Bu, belirli bir dilin, birbirine yapıştırma işlevlerinde işlevsel bir programlama diline göre daha iyi veya daha kötü olduğu anlamına gelmez - sadece dilin öncelikli meselesi değildir .
Jeff Hubbard

Temel olarak, eğer diliniz OOP'yi daha kolay / daha iyi / daha hızlı / daha güçlü hale getirmekle ilgili ise, o zaman tutkalın güzelce bir arada çalışıp çalışmayacağına odaklandığınız şey değildir.
Jeff Hubbard

30

Daha iyi bakım, kolay test, daha hızlı hata düzeltme, SRP uygulamasının sadece (çok hoş) sonuçlarıdır. Asıl sebep (Robert C. Matin'in söylediği gibi):

Bir sınıfın değişmesi için bir ve sadece bir nedeni olmalıdır.

Başka bir deyişle, SRP değişim yerini yükseltir .

SRP ayrıca DRY kodunu da teşvik eder. Sadece bir sorumluluğu olan sınıflarımız olduğu sürece, onları istediğimiz her yerde kullanmayı seçebiliriz. İki sorumluluğu olan bir sınıfımız varsa, ancak bunlardan sadece birine ihtiyacımız var ve ikincisi müdahale ediyorsa, 2 seçeneğimiz var:

  1. Sınıfı bir diğerine kopyalayıp yapıştırın ve hatta başka bir çok sorumluluklu mutant yaratın (teknik borcu biraz artırın).
  2. Sınıfı bölün ve orijinal sınıfın kapsamlı kullanımı nedeniyle pahalı olabilecek ilk etapta olması gerektiği gibi yapın.

1
+1 SRP'yi DRY'ye bağlamak için.
Mehdi

21

Belirli bir sorunu çözmek için kod oluşturmak kolaydır. Daha sonra güvenli bir şekilde yapılacak değişikliklere izin verirken bu sorunu gideren bir kod oluşturmak için daha karmaşıktır. SOLID, kodu daha iyi yapan bir dizi uygulama sağlar.

Hangisinin doğru olduğuna gelince: Üçü de. Hepsi tek bir sorumluluk kullanmanın yararı ve bunu kullanmanızın nedenidir.

Ne demek istediklerini gelince:

  • Daha iyi bakım, bunun daha kolay olduğu ve sık sık değişmediği anlamına gelir. Daha az kod olduğundan ve bu kod belirli bir şeye odaklandığından, sınıfla ilgili olmayan bir şeyi değiştirmeniz gerekirse, sınıfın değişmesi gerekmez. Ayrıca, sınıfı değiştirmek istediğinizde, genel arayüzü değiştirmek zorunda olmadığınız sürece, yalnızca o sınıf için endişelenmeniz gerekir, başka bir şey yoktur.
  • Kolay test, daha az test, daha az kurulum demektir. Sınıfta o kadar hareketli parça yoktur, bu nedenle sınıfta olası arızaların sayısı daha küçüktür ve bu nedenle test etmeniz gereken daha az vaka vardır. Kurulacak daha az özel alan / üye olacak.
  • Yukarıdaki ikisinden dolayı daha az değişen ve daha az başarısız olan bir sınıf alırsınız ve bu nedenle daha sağlamdır.

Prensibi izleyerek bir süre kod oluşturmaya çalışın ve daha sonra bazı değişiklikleri yapmak için bu kodu tekrar ziyaret edin. Sağladığı büyük avantajı göreceksiniz.

Her bir sınıfa aynısını yaparsınız ve hepsi de SRP'ye uyan daha fazla sınıfa girersiniz. Yapıları bağlantılar açısından daha karmaşık hale getirir, ancak her bir sınıfın sadeliği onu haklı çıkarır.


Burada belirtilen noktaların haklı olduğunu sanmıyorum. Özellikle, bir sınıfı basitleştirmenin diğer sınıfların daha karmaşık hale gelmesine neden olduğu ve bir bütün olarak kod tabanı üzerinde net bir etkisi olmadığı sıfır toplamlı oyunlardan nasıl kaçınırsınız? @ Doval'ın cevabının bu konuyu ele aldığına inanıyorum .

2
Aynı şeyi tüm sınıflara da yapıyorsun. Hepsi SRP ile uyumlu, daha fazla sınıfla bitiyorsunuz. Bağlantıları açısından yapıyı daha karmaşık hale getirir. Ancak her sınıftaki sadelik onu haklı çıkarmaktadır. @ Doval'ın cevabını seviyorum.
Miyamoto Akira

Cevabınıza bunu eklemeniz gerektiğini düşünüyorum.

4

İşte benim görüşüme göre, Tek Sorumluluk İlkesinin iyi bir uygulama olduğu iddiasını destekleyen argümanlar. Ayrıca, daha ayrıntılı nedenleri okuyabileceğiniz ve benimkinden daha belirgin olan diğer literatürlere bağlantılar da veriyorum:

  • Daha iyi bakım : İdeal olarak, sistemin bir işlevselliğinin değişmesi gerektiğinde, değiştirilmesi gereken bir ve yalnızca bir sınıf olacaktır. Sınıflar ve sorumluluklar arasında net bir eşleştirme, projeye dahil olan herhangi bir geliştiricinin hangi sınıf olduğunu tanımlayabildiği anlamına gelir. (@ MaciejChałapuk'un belirttiği gibi, bkz. Robert C. Martin, "Temiz Kod" .)

  • Daha kolay test : İdeal olarak, sınıflar mümkün olduğunca az ortak arayüze sahip olmalı ve testler yalnızca bu ortak arayüze hitap etmelidir. Net bir şekilde test edemiyorsanız, sınıfınızın birçok bölümü özel olduğu için, bu, sınıfınızın çok fazla sorumluluğa sahip olduğunun ve daha küçük alt sınıflara ayırmanız gerektiğinin açık bir işaretidir. Lütfen bunun 'herkese açık' veya 'özel' sınıf üyesi olmayan diller için de geçerli olduğunu unutmayın; Küçük bir ortak arayüze sahip olmak, sınıfın hangi bölümlerini kullanması gerektiğini müşteri koduna göre açık bir şekilde ifade eder. (Daha fazla bilgi için Kent Beck, "Test Odaklı Geliştirme" kısmına bakın.)

  • Sağlam kod : Kodunuz iyi yazılmış olduğundan daha fazla veya daha az başarısız olmaz; ancak, tüm kodlar gibi, asıl amacı makine ile değil, diğer geliştiricilerle iletişim kurmaktır (bkz. Kent Beck, "Uygulama Kalıpları" , Bölüm 1). ve bir hatayı keşfetme ve çözme arasında daha az zaman geçecektir.


İş sebeplerinden dolayı bir şeylerin değişmesi gerekiyorsa, bana SRP ile birden fazla görüşme yapmanız gerekecek. Bir sınıfın değişmesi yalnızca bir nedenden ötürü, bir değişiklik olması durumunda sadece bir sınıfı etkileyeceğini söylemek demek değildir.
Bastien Vandamme

@ B413: Herhangi bir değişikliğin, tek bir sınıfta tek bir değişiklik olduğu anlamına gelmiyordu. Sistemin birçok bölümünün, tek bir işletme değişikliğine uyması için değişikliklere ihtiyacı olabilir. Fakat belki de bir noktanız var ve "sistemin işlevselliği ne zaman değişmeli" yazmalıydım.
logc

3

Çok sayıda neden var, ancak sevdiğim şey, UNIX'in ilk programlarının çoğunun kullandığı yaklaşım: Bir şeyi iyi yapın. Bunu bir şeyle yapmak yeterince zor ve yapmaya çalıştığınız şeyleri zorlaştırmak zorlaşıyor.

Diğer bir sebep de yan etkileri sınırlamak ve kontrol etmektir. Kombinasyon kahve makinemin kapı açıcısını çok sevdim. Ne yazık ki, ziyaretçilerim varken kahve genellikle taşardı. Geçen gün kahve içtikten sonra kapıyı kapatmayı unuttum ve birisi çaldı.

Psikolojik açıdan bakıldığında, bir seferde yalnızca birkaç şeyi takip edebilirsiniz. Genel tahminler yedi artı veya eksi ikidir. Bir sınıf birden fazla şey yaparsa, hepsini bir kerede takip etmeniz gerekir. Bu, ne yaptığınızı izlemek için yeteneğinizi azaltır. Bir sınıf üç şey yaparsa ve bunlardan sadece birini istiyorsan, sınıfla ilgili bir şey yapmadan önce olayları takip etme yeteneğini tüketebilirsin.

Birden çok şey yapmak, kod karmaşıklığını arttırır. En basit kodun ötesinde, artan karmaşıklık böcek olasılığını artırır. Bu açıdan, sınıfların mümkün olduğunca basit olmasını istersiniz.

Bir şey yapan bir sınıfı test etmek çok daha kolaydır. Her test için sınıfın yaptığı veya gerçekleşmeyen ikinci şeyin olduğunu doğrulamak zorunda değilsiniz. Ayrıca bozuk koşulları düzeltmeniz ve bu testlerden biri başarısız olduğunda tekrar test etmeniz gerekmez.


2

Çünkü yazılım organik. Gereksinimler sürekli değişir, bu yüzden bileşenleri mümkün olduğunca az baş ağrısı ile manipüle etmeniz gerekir. SOLID ilkelerini takip etmeyerek, somut olarak belirlenmiş bir kod tabanına sahip olabilirsiniz.

Yük taşıyan beton duvarlı bir ev düşünün. Bu duvarı herhangi bir destek olmadan açtığınızda ne olur? Ev muhtemelen çökecek. Yazılımda bunu istemiyoruz, bu nedenle uygulamaları çok fazla zarara neden olmadan kolayca taşıyabileceğiniz / değiştirebileceğiniz / değiştirebileceğiniz şekilde yapılandırıyoruz.


1

Ben düşünce izleyin: 1 Class = 1 Job.

Bir Fizyoloji Analojisi Kullanmak: Motor (Sinir Sistemi), Solunum (Akciğerler), Sindirim (Mide), Koku (Gözlem), vb. ilgili alt sistemlerinin her birinin çalışma şekli ya da bir uç nokta alt sistemi olup olmadıkları ve bir parmağını kaldırmak ya da saç folikülü yetiştirmek gibi yalnızca 1 görevi yerine getirmeleri.

Bir işçi yerine yönetici olarak hareket ettiği gerçeğini karıştırmayın. Sonunda bazı işçiler, bir işlemin kendisi tarafından yürütülemeyecek kadar karmaşık hale geldiklerinde sonunda Müdür'e terfi etti.

Deneyimlediklerimin en karmaşık kısmı, bir Sınıfı Operatör, Süpervizör veya Yönetici süreci olarak ne zaman atayacağımı bilmektir. Ne olursa olsun, 1 sorumluluk (Operatör, Süpervizör veya Yönetici) işlevini gözlemlemeniz ve belirtmeniz gerekecektir.

Bir Sınıf / Nesne bu tür rollerden birden fazlasını gerçekleştirdiğinde, genel işlemin performans sorunları yaşamaya başlayacağını veya şişe boyunlarını işleyeceğini göreceksiniz.


Atmosferik oksijenin hemoglobine işlenmesi uygulamasının bir Lungsınıf Personolması Breathegerekse bile , bir örneğinin hala devam etmesi gerektiğini ve bu nedenle de Personsınıfın bu yöntemle ilgili sorumlulukları yerine getirmek için yeterli mantığı içermesi gerektiğini düşünüyorum. Ayrıca, sadece ortak bir işletme sahibi tarafından erişilebilen, birbirine bağlı varlıklardan oluşan bir ormanın, sebep olduğu için, birçok bağımsız erişim noktasına sahip bir ormandan daha kolay olduğunu da ileri süreceğim.
supercat

Evet, bu durumda Akciğer Yöneticisi, Villi bir Süpervizör olacak ve Solunum süreci, Hava partiküllerini Kan akışına transfer etmek için İşçi sınıfı olacaktır.
GoldBishop

@GoldBishop: Belki de doğru görüş, bir Personsınıfın işi olarak, çeşitli parçaların birleştirilmesi de dahil olmak üzere, canavarca karmaşıklığı olan "gerçek dünya-insan-insan" birliği ile ilişkili tüm işlevselliğin delegasyonu olduğunu söylemek olacaktır. . Bir varlığın 500 şey yapması çirkindir, ancak gerçek dünya sistemi modellenme şekli böyle ise, bir kimliği koruyarak 500 işlevi delegeleyen bir sınıfa sahip olmak, her şeyi ayrı parçalarla yapmaktan daha iyi olabilir.
supercat,

@supercat Ya da her şeyi bölümlere ayırıp alt süreçler boyunca gerekli Süpervizör ile birlikte 1 Sınıf sahibi olabilirsiniz. Bu şekilde, (teoride) her bir sürecin temel sınıftan ayrı Personolmasına rağmen, yine de başarı / başarısızlık zincirini rapor eder, ancak diğerini etkilemeyebilir. 500 İşlev (bir örnek olarak ayarlanmış olmasına rağmen, denize düştü ve desteklenemez), bu tür bir işlevselliği türetilmiş ve modüler tutmaya çalışıyorum.
GoldBishop

@GoldBishop: "Geçici" bir tür ilan etmenin bir yolu olsaydı, öyle ki dış kod onun üzerine üyeleri çağırabilir ya da türün kendi yöntemlerine bir parametre olarak geçirebilirdi, ama başka bir şey yapmazdı. Bir kod organizasyon açısından bakıldığında, alt bölümlere ayırma sınıfları mantıklı ve hatta bir seviye aşağı (örneğin yöntemlerle çağırmak dışında kod sahip Fred.vision.lookAt(George)mantıklı ama söz hakkından için kod sağlayan someEyes = Fred.vision; someTubes = Fred.digestiono arasındaki ilişkiyi gizler beri iğrenç görünüyor someEyesve someTubes.
SuperCat

1

Özellikle Tek Sorumluluk gibi önemli bir ilkeyle, şahsen insanların bu ilkeyi benimsemelerinin birçok nedeni olduğunu umuyorum.

Bu nedenlerden bazıları şunlar olabilir:

  • Bakım - SRP, bir sınıftaki sorumluluğun değiştirilmesinin diğer sorumlulukları etkilememesini sağlayarak bakımı kolaylaştırır. Bunun nedeni, eğer her bir sınıfın yalnızca bir sorumluluğu varsa, bir sorumluluğa yapılan değişikliklerin diğer sorumluluklardan ayrı tutulmasıdır.
  • Test Etme - Bir sınıfın bir sorumluluğu varsa, bu sorumluluğun nasıl test edileceğini bulmayı çok kolaylaştırır. Bir sınıfın birden fazla sorumluluğu varsa, doğru olanı test ettiğinizden ve testin sınıfın sahip olduğu diğer sorumluluklardan etkilenmediğinden emin olmalısınız.

Ayrıca, SRP'nin bir SOLID ilkeleri paketi ile geldiğini de unutmayın. SRP'ye uymak ve gerisini görmezden gelmek, ilk başta SRP yapmamak kadar kötü. Bu nedenle, SRP'yi kendi başınıza değil, tüm SOLID ilkeleri bağlamında değerlendirmelisiniz.


... test is not affected by other responsibilities the class has, lütfen bunu biraz detaylandırabilir misiniz?
Mehdi,

1

Bu ilkelerin önemini anlamanın en iyi yolu ihtiyaç duymaktır.

Acemi bir programcı olduğum zaman, tasarım konusunda fazla düşünmedim, hatta tasarım desenlerinin var olduğunu bile bilmiyordum. Programlarım büyüdükçe, bir şeyi değiştirmek, diğer birçok şeyi değiştirmek anlamına geliyordu. Hataları takip etmek zordu, kod çok büyük ve tekrarlıydı. Nesne hiyerarşisinde pek bir şey yoktu, her yerdeydi. Yeni bir şey eklemek veya eski bir şeyi kaldırmak, programın diğer bölümlerinde hatalara yol açar. Git figürü.

Küçük projelerde önemli olmayabilir, ancak büyük projelerde işler çok kabus olabilir. Daha sonra tasarım patlayıcılarının konseptleriyle karşılaştığımda kendime "ah ya, bunu yapmak o zamanları çok daha kolay hale getirecekti" dedim.

İhtiyaç doğana kadar tasarım kalıplarının önemini gerçekten anlayamazsınız. Kalıplara saygı duyuyorum çünkü deneyimlerimden kod bakımını kolaylaştırdığını ve kodun sağlam olduğunu söyleyebilirim.

Ancak, tıpkı sizin gibi, hala "kolay test" konusunda kararsızım çünkü ünite testine henüz girmedim.


Desenler bir şekilde SRP'ye bağlanmıştır, ancak tasarım desenleri uygulanırken SRP gerekli değildir. Desen kullanmak ve SRP'yi tamamen göz ardı etmek mümkündür. İşlevsel olmayan gereksinimleri ele almak için desenler kullanıyoruz, ancak hiçbir programda desen kullanmak gerekli değildir. Prensipler gelişimi daha az acı verici hale getirmek için esastır ve (IMO) bir alışkanlık olmalıdır.
Maciej Chałapuk

Sana tamamen katılıyorum. SRP, bir dereceye kadar uygulayıcıları, temiz nesne tasarımı ve hiyerarşisini temizler. Bir geliştiricinin girmesi iyi bir zihniyettir, çünkü geliştiriciyi nesneler ve nasıl olması gerektiği hakkında düşünmeye başlaması sağlanır. Şahsen benim gelişimim üzerinde de gerçekten iyi bir etkisi oldu.
harsimranb

1

Cevap, diğerlerinin de belirttiği gibi, hepsi doğrudur ve hepsi birbiriyle beslenir, test edilmesi daha kolay hale getirir, bakımı daha kolay hale getirir kodları daha sağlam hale getirir, bakımı daha kolay hale getirir ...

Bunların hepsi kaynatılarak temel bir prensip - kod, işi yapabilmek için gereken kadar küçük ve gereken kadar az olmalıdır. Bu, bir uygulama, dosya veya bir işlev için olduğu kadar bir sınıf için de geçerlidir. Bir kod parçasının yaptığı şey ne kadar çoksa, anlamak, sürdürmek, genişletmek veya test etmek zorlaşır.

Bence tek kelimeyle özetlenebilir: kapsam. Yapıtların kapsamına yakından dikkat edin, bir uygulamada herhangi bir noktada kapsam dahilindeki şeyler ne kadar azsa o kadar iyidir.

Genişletilmiş kapsam = daha karmaşıklık = işlerin yanlış gitmesi için daha fazla yol.


1

Bir sınıf çok büyükse, sürdürmek, test etmek ve anlamak zorlaşır, diğer cevapların da bu iradeyi kapsaması gerekir.

Bir sınıfın problemsiz olarak birden fazla sorumluluğu olması mümkündür, ancak yakında çok karmaşık sınıflarla ilgili problemlere çarpıyorsunuz.

Ancak basit bir “tek sorumluluk” kuralına sahip olmak, yeni bir sınıfa ihtiyaç duyduğunuzda bilmenizi kolaylaştırır .

Bununla birlikte, “sorumluluğu” tanımlamak zordur, “uygulamanın belirttiği her şeyi yap” anlamına gelmez, asıl beceri, sorunu “sorumluluk” un küçük birimlerine nasıl ayıracağını bilmektir.

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.