Tek Sorumluluk İlkesini Netleştirin


64

Tek Sorumluluk İlkesi, bir sınıfın sadece bir tek şey yapması gerektiğini belirtir. Bazı davalar oldukça açık. Yine de diğerleri zordur, çünkü belirli bir soyutlama seviyesinde bakıldığında "bir şey" gibi görünen şey, daha düşük bir seviyede bakıldığında birçok şey olabilir. Ayrıca, Tek Sorumluluk Prensibi daha düşük seviyelerde onurlandırılırsa, aşırı derecede ayrışmış, ayrıntılı mantı kodu, her şey için küçük sınıflar oluşturmak için harcanan daha fazla satır harcanması ve etrafındaki sorunu çözmekten ziyade sıhhi tesisatların ortaya çıkabileceğinden korkuyorum.

"Bir şeyin" ne anlama geldiğini nasıl tarif edersiniz? Bir sınıfın "bir şeyden daha fazlasını" gerçekten yaptığını gösteren somut işaretler nelerdir?


6
Üstte "mantı kodu" için +1. Kariyerimin başında, çok ileri götüren insanlardan biriydim. Sadece sınıflarla değil, aynı zamanda yöntem modülerliğinde de. Kodum, basit bir şey yapan tonlarca küçük yöntemle doluydu, sadece bir sorunu ekrana sığmadan uyabilecek küçük parçalara bölmek adına. Açıkçası, bu genellikle çok ileri gidiyordu.
Bobby Tables

Yanıtlar:


50

Robert C. Martin'in (Bob Amca) Tek Sorumluluk İlkesini (PDF'ye bağlı) bildirmesini gerçekten seviyorum :

Bir sınıfın değişmesi için hiçbir zaman birden fazla sebep olmamalıdır.

Geleneksel olarak "sadece tek bir şey yapmalı" tanımından farklıdır ve bunu sevdim çünkü sizi sınıfınız hakkında düşünme şeklinizi değiştirmeye zorluyor. "Bu bir şey mi yapıyor?" Diye düşünmek yerine, neyin değişebileceğini ve bu değişikliklerin sınıfınızı nasıl etkilediğini düşünürsünüz. Örneğin, eğer veritabanı değişirse, sınıfınızın değişmesi gerekiyor mu? Çıkış cihazı değişirse (örneğin bir ekran veya bir mobil cihaz veya bir yazıcı)? Sınıfınızın birçok yönden değişmesi nedeniyle değişmesi gerekiyorsa, bu, sınıfınızın çok fazla sorumluluk sahibi olduğunun bir işaretidir.

Bağlantılı makalede, Bob Amca şu sonuca varıyor:

SRP, ilkelerin en basitlerinden ve doğru olmanın en zorlarından biridir. Sorumlulukları birleştirmek doğal olarak yaptığımız bir şeydir. Bu sorumlulukları birbirinden bulmak ve ayırmak, yazılım tasarımının gerçekte ne olduğu ile ilgilidir.


2
Ben de onun ifade ettiği yolu seviyorum, soyut "sorumluluk" yerine, değişimle ilgili olduğunda kontrol etmek daha kolay görünüyor.
Matthieu M.

Bu gerçekten koymak için mükemmel bir yol. Bunu sevdim. Bir not olarak, genellikle SRP'yi metotlara daha güçlü bir şekilde uygularken düşünüyorum. Bazen bir sınıf iki şey yapmak zorunda kalır (belki de iki alanlı köprüler sınıfıdır), ancak bir yöntem neredeyse hiçbir zaman türü imzasıyla kısaca tanımlayabileceğinizden fazlasını yapmamalıdır.
CodexArcanum

1
Bunu Grad'uma gösterdim - harika bir okuma ve kendime çok iyi bir hatırlatma.
Martijn Verburg

1
Tamam, bu durum, değiştirilmemiş kodun yalnızca öngörülebilecek olan gelecekteki muhtemel değişimleri planlaması gerektiği düşüncesiyle birleştirdiğinizde bir anlam ifade eder, olası her değişiklik için değil. Bunu, "Öngörülebilir bir gelecekte oluşması muhtemel olan bir sınıfın değişmesi için sadece bir sebep olmalı" diye söylerdim. Bu, tasarımın değişmesi muhtemel kısımlarda değişmesi muhtemel olmayan kısımlarında sadeliği desteklemeyi teşvik eder.
dsimcha

18

Kendime, SRP'nin hangi sorunu çözmeye çalıştığını sormaya devam ediyorum. SRP bana ne zaman yardım eder? İşte geldiğim şey:

Aşağıdaki durumlarda bir sınıfın sorumluluğunu / işlevselliğini yeniden değerlendirmelisiniz:

1) İşlevselliği çoğalttınız (DRY)

2) Kodunuzun, anlamanıza yardımcı olmak için başka bir soyutlama seviyesine ihtiyacı olduğunu farkedersiniz (KISS)

3) İşlevsellik parçalarının etki alanı uzmanlarınız tarafından farklı bir bileşenin dışında olduğu anlaşılıyor (Anlaşılır Dil)

Aşağıdaki durumlarda sınıf dışına çıkma sorumluluğu taşımamalısınız:

1) Yinelenen işlevsellik yok.

2) İşlevsellik, sınıfınızın kapsamı dışında bir şey ifade etmez. Başka bir deyişle, sınıfınız, işlevselliği anlamanın daha kolay olduğu bir bağlam sağlar.

3) Etki alanı uzmanlarınız bu sorumluluk kavramına sahip değil.

Bana göre, eğer SRP'ye geniş çapta uygulanırsa, bir tür karmaşıklığı (bir sınıfın başını veya kuyruklarını içeride çok fazla ilerleyerek yapmaya çalışarak) başka bir türle (ortak çalışanların / seviyelerini korumaya çalışarak) işlemekteyiz. bu sınıfların gerçekte ne yaptığını anlamak için soyutlama dümdüz.

Şüpheniz olduğunda, dışarıda bırakın! Bunun için net bir durum olduğunda, daha sonra her zaman refactor yapabilirsiniz.

Ne düşünüyorsun?


Bu kurallar yararlı olabilir veya olmayabilir, ancak SOLID'de tanımlandığı gibi SRP ile hiçbir ilgisi yoktur, değil mi?
sara,

Teşekkür ederim. SOLID ilkeleriyle ilgili deliliğin bir kısmına inanamıyorum, bu yüzden basit bir kodun yüzlerce kez daha karmaşık bir neden olmadan çok daha karmaşık hale getirdikleri . Verdiğiniz puanlar, SRP'yi uygulamak için gerçek dünya nedenlerini açıklar . Bence yukarıda verdiğin prensipler kendi kısaltmaları olmalı ve "KATI" ı atmalı, yarardan çok zarar veriyor. "Mimarlık Astronotlar" İçinde belirttiği gibi gerçekten de iplik beni buraya neden olduğunu.
Nicholas Petersen,

4

Bunun için objektif bir ölçek olup olmadığını bilmiyorum ama bunu verecek olan şey metotlar olacaktır - bunların sayısı değil, fonksiyonlarının çeşitliliği. Ayrışmaya çok fazla katılabileceğini kabul ediyorum, ancak bu konuda hızlı ve sert kurallara uymam.


4

Cevap tanımda yatıyor

Ne tanımlamak olmak sorumluluğunu sonuçta size sınır verir.

Örnek:

Faturaları görüntüleme sorumluluğu olan bir bileşene sahibim -> bu durumda, daha fazla bir şey eklemeye başlarsam ilkeyi çiğniyorum.

Öte yandan eğer ben sorumluluğunu desem taşıma faturaları - (örn baskı birden fazla küçük işlevleri ekleyerek> Fatura güncellenmesi, Fatura tüm bu sınırın içindedir).

Bununla birlikte, bu modül faturaların dışındaki herhangi bir işlevi yerine getirmeye başlarsa, o zaman bu sınırın dışında kalır.


Sanırım buradaki asıl sorun "ele alma" kelimesi. Tek bir sorumluluk tanımlamak çok genel. Tek bir bileşen tanıtıcısı yerine yazdırma faturasından sorumlu olan bir bileşenin ve güncelleme faturasından diğerine sahip olmanın daha iyi olacağını düşünüyorum {yazdırma, güncelleme ve - neden olmasın? - ekran, ne olursa olsun} fatura .
Machado

1
OP aslında “Bir sorumluluğu nasıl tanımlarsınız?” Diye soruyor. bu yüzden sorumluluk olarak tanımladığınız her şey olduğunu söylerken, sadece soruyu tekrar ediyor gibi görünüyor.
Despertar

2

Her zaman iki düzeyde görüyorum:

  • Yöntemlerimin sadece bir şeyi yaptığından ve iyi yaptığından eminim
  • Bir sınıfı, bir şeyi iyi temsil eden bu yöntemlerin mantıksal (OO) grubu olarak görüyorum.

Yani Dog denilen bir etki alanı nesnesi gibi bir şey:

Dogbenim sınıfım ama köpekler birçok şey yapabilir! Böyle gibi yöntemleri sahip olabilir walk(), run()ve bite(DotNetDeveloper spawnOfBill)(üzgün karşı değil, p).

Eğer Doghantallaşmazsa, o zaman bu metot gruplarının Movementbenim walk()ve run()metodları içerebilecek bir sınıf gibi başka bir sınıfta nasıl modellenebileceğini düşünürdüm .

Sert ve hızlı bir kural yoktur, OO tasarımınız zamanla gelişir. Net bir arayüz / genel API ile bir şeyi ve bir şeyi iyi yapan basit yöntemleri kullanmaya çalışıyorum.


Bite gerçekten Object'in bir örneğini almalı ve bir DotNetDeveloper bir Kişinin alt sınıfı olmalıdır (genellikle, yine de!)
Alan Pearce

@Alan - There - sizin için düzeltildi :-)
Martijn Verburg

1

Ona bir sınıfın çizgileri boyunca daha çok bakıyorum, sadece bir şeyi temsil etmeli . Karianna en Örneğin @ anlamak için, benim var Dogiçin yöntemleri vardır sınıfı walk(), run()ve bark(). Ben yöntemlerini eklemek için gitmiyorum meaow(), squeak(), slither()veya fly()bu köpekler şeyler değildir çünkü. Bunlar diğer hayvanların yaptığı şeylerdir ve diğer hayvanların onları temsil etmek için kendi sınıfları vardır.

(Köpek eğer BTW, yok sinek, o zaman muhtemelen pencereden dışarı onu atma durmalıdır).


"Köpeğiniz uçarsa, o zaman muhtemelen onu pencereden atmayı bırakmalısınız" için +1. :)
Bobby Tablolar

Sınıfın neyi temsil etmesi gerektiği sorusunun ötesinde, bir örnek neyi temsil ediyor? Biri Saygılarımızla Eğer SeesFoodbir özelliği olarak DogEyes, Barkbir şey olarak bir tarafından yapılır DogVoiceve Eatbir tarafından yapılan bir şey olarak DogMouth, daha sonra mantık gibi if (dog.SeesFood) dog.Eat(); else dog.Bark();olacak if (eyes.SeesFood) mouth.Eat(); else voice.Bark();gözler, ağız ve ses hepsi tek antite bağlı kimlik bir anlam kaybediyor.
supercat

@supercat bu bağlamda önemli olmasına rağmen, adil bir nokta. Bahsettiğiniz kod Dogsınıf içinde ise , muhtemelen Dogilişkilidir. Olmazsa, o zaman muhtemelen myDog.Eyes.SeesFoodsadece gibi bir şey ile sonuçlanacaktır eyes.SeesFood. Diğer bir olasılık da , özelliği ve yöntemi talep eden arayüzü Dogortaya çıkarmasıdır . ISeeDog.EyesSeesFood
JohnL

@JohnL: Görmenin asıl mekaniği, bir kedinin veya zebra'nınkiyle aynı şekilde bir köpeğin gözleriyle işleniyorsa, o zaman mekaniğin bir Eyesınıf tarafından ele alınması mantıklı gelebilir , ancak bir köpek "görmelidir" sadece görebilen gözlere sahip olmak yerine gözlerini kullanmak. Bir köpek göz değildir, ancak basitçe bir göz tutucu değildir. Bu, "en azından görmeyi deneyebilecek" bir şeydir ve arayüz olarak böyle tanımlanmalıdır. Kör bir köpeğe bile yiyecek görüp görmediği sorulabilir; Köpek her zaman "hayır" diyeceği için çok faydalı olmayacak, fakat sormakta hiçbir sakınca yok.
supercat

O zaman benim yorumumda açıkladığım gibi ISee arayüzünü kullanırsınız.
JohnL

1

Bir sınıf, kendi soyutlama düzeyinde bakıldığında bir şey yapmalıdır. Kuşkusuz, daha az soyut bir düzeyde birçok şey yapmak. Sınıflar programları daha sürdürülebilir hale getirmek için çalışır: yakından incelemenize gerek kalmazsa uygulama ayrıntılarını gizlerler.

Bunun için sınıf adlarını test olarak kullanıyorum. Bir sınıfa oldukça kısa bir tanımlayıcı ad veremezsem veya bu adın içinde "Ve" gibi bir sözcük varsa, muhtemelen Tek Sorumluluk İlkesini ihlal ediyorum.

Tecrübelerime göre, bu prensibi işlerin daha somut olduğu düşük seviyelerde tutmak daha kolay.


0

Benzersiz bir rôle sahip olmakla ilgili .

Her sınıfa bir rol adı ile devam edilmelidir. Bir rol aslında bir bağlamla ilişkili bir (fiil) fiildir.

Örneğin :

Dosya bir dosyanın erişimini sağlar. FileManager File nesnelerini yönetir.

Kaynak bir Dosyadan bir kaynak için veri tutma. ResourceManager tüm Kaynakları saklar ve sağlar.

Burada, "yönet" gibi bazı fiillerin başka bir fiil dizisini ima ettiğini görebilirsiniz. Tek başına fiiller, çoğu zaman sınıflardan çok fonksiyon olarak düşünülür. Fiil, kendi ortak bağlamı olan çok fazla eylem ifade ediyorsa, o zaman kendi içinde bir sınıf olmalıdır.

Dolayısıyla, fikir yalnızca sınıfın ne olduğu hakkında benzersiz bir rol tanımlayarak basit bir fikir edinmenize izin vermektir, bu, birkaç alt rolün (üye nesneler veya diğer nesneler tarafından gerçekleştirilir) anlaşması olabilir.

Sık sık içinde birkaç farklı sınıf daha olan Yönetici sınıfları yapıyorum. Bir Fabrika, Bir Kayıt Defteri, vb. Gibi Bir grup şefi, bir başka orkestra şefi gibi üst düzey bir fikir elde etmek için birlikte çalışmaya yönlendiren bir orkestra şefi gibi bir Yönetici sınıfına bakın. Bir rolü var ama içindeki diğer benzersiz rollerle çalışmak anlamına geliyor. Bir şirketin nasıl örgütlendiğini de görebilirsiniz: CEO saf verimlilik düzeyinde üretken değil, ancak o orada değilse, hiçbir şey birlikte doğru bir şekilde çalışamaz. Bu onun rolü.

Tasarlarken benzersiz rolleri tanımlayın. Ve her rol için başka rollerde kesilip kesilemeyeceğini tekrar görün. Bu şekilde, Yöneticinizin nesneler oluşturma şeklini değiştirmeye gerek duymamanız gerekiyorsa, yalnızca Fabrikayı değiştirin ve aklınızdakilere geçin.


-1

SRP sadece sınıfları bölmekle kalmıyor, aynı zamanda yetki vermeyi de sağlıyor.

Yukarıda kullanılan Köpek örneğinde, DogParker, DogWalker, vb. Gibi 3 ayrı sınıfa sahip olmak için SRP'yi gerekçe olarak kullanmayın (düşük uyumluluk). Bunun yerine, bir sınıfın yöntemlerinin uygulanmasına bakın ve "çok fazla şey biliyorlarsa" kararını verin. Hala dog.walk () yöntemine sahip olabilirsiniz, ancak muhtemelen walk () yöntemi başka bir sınıfa, yürüyüşün nasıl yapıldığının ayrıntılarını vermelidir.

Aslında Köpek sınıfının değişmesi için bir nedene izin veriyoruz: çünkü Köpekler değişiyor. Elbette, bunu diğer SOLID ilkeleriyle birleştirmek, Köpeği (açık / kapalı) değiştirmek yerine, yeni işlevsellik için Köpeği Uzatacaksınız. IMove ve IEat gibi bağımlılıklarınızı da enjekte edersiniz. Ve elbette bu ayrı arayüzleri (arayüz ayrımı) yaparsınız. Köpek sadece bir hata bulduğumuzda veya Köpekler temelde değiştiyse değişecektir (Liskov Sub, davranışları uzatma ve kaldırma).

SOLID’in net etkisi, mevcut kodu değiştirmek zorunda kaldığımızdan daha fazla yeni kod yazmamız ve bu büyük bir kazanç.


-1

Her şey sorumluluk tanımına ve bu tanımın kodunuzun bakımını nasıl etkileyeceğine bağlıdır. Her şey tek bir şeyle sonuçlanıyor ve bu şekilde tasarımınız kodunuzu korumanıza yardımcı olacak.

Ve birisinin dediği gibi "Suda yürümek ve verilen özelliklerden gelen yazılımı tasarlamak kolaydır, her ikisi de donmuşsa".

Yani, sorumluluğu daha somut bir şekilde tanımlıyorsak, o zaman değiştirmemize gerek kalmaz.

Bazen sorumluluklar göze çarpmayacak şekilde açık olacaktır, ancak bazen belirsiz olabilir ve makul bir şekilde karar vermemiz gerekir.

Farz edelim ki, catchThief () adlı Köpek sınıfına başka bir sorumluluk daha ekledik. Şimdi ek bir farklı sorumluluğa öncülük ediyor olabilir. Yarın, Dog'un hırsızı yakalama biçimi Polis Departmanı tarafından değiştirilmek zorundaysa, Dog sınıfı değiştirilmelidir. Bu durumda başka bir alt sınıf yaratmak ve buna ThiefCathcerDog adını vermek daha iyi olur. Fakat farklı bir bakış açısına göre, herhangi bir durumda değişmeyeceğinden veya herhangi bir koşulda yakalanmayacağından eminizse, bir dış parametreye bağlı olduğundan, bu sorumluluğa sahip olmak tamamen sorun olmaz. Sorumluluklar çarpıcı derecede garip değilse, kullanım durumuna dayanarak makul bir şekilde karar vermeliyiz.


-1

“Değişmenin bir nedeni”, sistemi kimin kullandığına bağlıdır. Her oyuncu için vaka kullandığınızdan ve en muhtemel değişikliklerin bir listesini yaptığınızdan emin olun, her olası kullanım değişikliği durumunda sadece bir sınıfın bu değişiklikten etkilendiğinden emin olun. Tamamen yeni bir kullanım örneği ekliyorsanız, bunu yapmak için yalnızca bir sınıfı genişletmeniz gerekeceğinden emin olun.


1
Değişimin bir nedeni, kullanım durumlarının, aktörlerin sayısı veya değişimin ne kadar muhtemel olduğu ile ilgisi yoktur. Muhtemel değişikliklerin bir listesini yapmamalısınız. Bir değişikliğin sadece bir sınıfı etkilemesi gerektiği doğru. Bir sınıfı bu değişime uyum sağlamak için genişletmek iyidir, ancak bu açık kapalı ilkedir, SRP değil.
candied_orange

Neden en olası değişikliklerin bir listesini yapmıyoruz? Spekülatif tasarım?
kiwicomb123

çünkü yardım etmeyecek, eksiksiz olmayacak ve değişim ile başa çıkmaya çalışmaktan daha etkili yollar var. Sadece bekle. Kararları ayırın ve etki minimum olacaktır.
candied_orange

Tamam, anlıyorum, "buna ihtiyacın yok" ilkesini ihlal ediyor.
kiwicomb123

Aslında yagni de ileriye itilebilir. Bu, spekülatif kullanım durumları uygulamanıza engel olmanız anlamına geliyor. Uygulanan bir kullanım durumunu izole etmemek için.
candied_orange
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.