Bu sınıf tasarımı tek sorumluluk ilkesini ihlal ediyor mu?


63

Bugün biriyle tartışmıştım.

Anemik bir etki alanı modelinin aksine zengin bir etki alanı modeline sahip olmanın faydalarını açıklıyordum. Ve şunun gibi basit bir sınıfla fikrimi geliştirdim:

public class Employee
{
    public Employee(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastname;
    }

    public string FirstName { get private set; }
    public string LastName { get; private set;}
    public int CountPaidDaysOffGranted { get; private set;}

    public void AddPaidDaysOffGranted(int numberOfdays)
    {
        // Do stuff
    }
}

Anemik model yaklaşımını savunduğu için, savlarından biri şuydu: "Ben SOLID'e inanıyorum . Hem verileri temsil ederken hem de aynı sınıfta mantığı gerçekleştirirken , tek sorumluluk ilkesini (SRP) ihlal ediyorsunuz."

Bu iddiayı gerçekten şaşırtıcı buldum, çünkü bu akıl yürütmeyi izleyen, bir özelliği ve bir yöntemi olan herhangi bir sınıf SRP'yi ihlal ediyor ve bu nedenle genel olarak OOP, KATI değil ve işlevsel programlama cennete giden tek yol.

Pek çok tartışmasına cevap vermemeye karar verdim, ama toplumun bu soru hakkında ne düşündüğünü merak ediyorum.

Eğer cevap verseydim, yukarıda belirtilen paradoksu işaret ederek başlayacaktım ve daha sonra SRP’nin dikkate almak istediğiniz ayrıntı düzeyine bağlı olduğunu ve yeterince ileri götürürseniz, birden fazla sınıf içeren herhangi bir sınıfa bağlı olduğunu belirtmek isterdim. özellik veya bir yöntem ihlal ediyor.

Ne söylerdin

Güncelleme: Örnek, yöntemi daha gerçekçi hale getirmek ve altta yatan tartışmaya odaklanmamıza yardımcı olmak için guntbert tarafından cömertçe güncellendi.


19
bu sınıf, SRP'yi ihlal ettiği için veriyi mantıkla karıştırdığı için değil, aynı zamanda düşük bir uyumluluğa sahip olduğu için - potansiyel Tanrı nesnesi
gnat

1
Çalışana tatil eklemek ne amaçtır? Belki bir takvim sınıfı ya da tatilleri olan bir şey olmalı. Bence arkadaşın haklı.
James Black,

9
Asla "X'e inanan biri olduğumu" söyleyen birini dinleme.
Stig Hemmer,

29
Bunun, SRP'nin ihlal edilip edilmediği veya modellemenin iyi olup olmadığı değil. Diyelim ki ben bir çalışanım. Menajerime onun için sorun olup olmadığını sorduğumda kayak yapmak için uzun bir hafta sonu alırsam yöneticim bana tatil eklemez . Buradaki kod, modellemek istediği gerçeğe uymuyor ve bu nedenle şüpheli.
Eric Lippert

2
Fonksiyonel programlama için +1 cennete giden tek yoldur.
erip

Yanıtlar:


68

Tek Sorumluluk, sisteminizdeki mantıksal görevlerin bir soyutlaması olarak anlaşılmalıdır. Bir sınıf, tek ve belirli bir görevi yerine getirme (gerekli her şeyi yapma) sorumluluğuna sahip olmalıdır. Bu aslında sorumluluğun ne olduğuna bağlı olarak iyi tasarlanmış bir sınıfa çok şey getirebilir. Örneğin, script motorunuzu çalıştıran sınıf, scriptleri işlemeyle ilgili birçok yöntem ve verilere sahip olabilir.

İş arkadaşınız yanlış şeye odaklanıyor. Soru, "bu sınıfın hangi üyeleri var?" Değil. "Bu sınıf program içerisinde hangi faydalı işlemleri gerçekleştirir?" Bu bir kez anlaşıldığında etki alanı modeliniz gayet iyi görünüyor.


Nesneye Yönelik Programlama yaratıldıysa ve gerçek hayattaki nesneleri ve sınıfları modellemeyi amaçladıysa (eylemler + öznitelikler) neden kod sınıfını birden fazla sorumluluğu olan (eylemler) yazmıyorsunuz ? Gerçek bir dünya nesnesinin birden fazla sorumluluğu olabilir. Örneğin, bir gazeteci haber bültenlerinde başyazılar yazar ve bir TV şovunda politikacılarla röportajlar yapar. Gerçek bir yaşam nesnesi için iki sorumluluk! Ya bir sınıf Gazetecisi yazacaksam?
user1451111

41

Tek Sorumluluk İlkesi, yalnızca bir kod parçasının (OOP'da, genellikle sınıflardan bahsediyoruz) tek bir işlevsellikten sorumlu olup olmadığına ilişkindir . Sanırım arkadaşın işlevlerin ve verilerin karışamayacağını söylüyor, bu fikri gerçekten anlamadı. Employeeİşyeriyle ilgili bilgileri de içerecek olsaydı , arabasının ne kadar hızlı gittiğini ve köpeğinin ne tür bir yemek yediğini sorun yaşayabilirdik.

Bu sınıf sadece bir ile Employeeilgilendiği için, SRP'yi açıkça ihlal etmediğini söylemenin adil olduğunu düşünüyorum, ancak insanlar her zaman kendi fikirlerine sahip olacaklar.

Geliştirebileceğimiz bir yer Çalışan bilgilerini (isim, telefon numarası, e-posta gibi) tatilinden ayırmaktır. Aklımda, bu, yöntemlerin ve verilerin birbirine karışamayacağı anlamına gelmiyor , bu sadece belki de tatil işlevselliğinin ayrı bir yerde olabileceği anlamına geliyor.


20

Aklıma göre, bu sınıf hem Employeeve hem de temsil etmeyi sürdürürse, SRP'yi potansiyel olarak ihlal edebilir EmployeeHolidays.

Olduğu gibi, ve eğer Akran Değerlendirmesi için bana gelirse, muhtemelen bırakmasına izin veririm. Daha fazla Çalışan için özel özellikler ve yöntemler eklenmişse ve daha fazla tatil için özel özellikler eklenmişse, muhtemelen hem SRP hem de ISS'yi işaret ederek bir bölünme önerebilirim.


Katılıyorum. Kod burada sağlanan kadar basitse, büyük olasılıkla kaymasına izin veririm. Ancak aklımda, kendi tatilini idare etmek için çalışanın sorumluluğu olmamalıyım. Sorumluluğun nereye yerleştirildiği ile büyük bir sorun gibi görünmüyor olabilir, ancak şu şekilde bakın: Eğer kod üssünde yeniyseniz ve tatile özgü işlevler üzerinde çalışmak zorunda kalsaydınız - ilk önce nereye bakarsınız? Tatil mantığı için, şahsen ben başlamak için Çalışan varlığına bakmayacağım.
Niklas H,

1
@ NiklasH Anlaşıldı. Şahsen ben rastgele
görünmezdim ve denemeye çalışırdım

4
Doğru. Fakat bu yeni sistemde "Tatil" denmiyorsa, "Tatil" veya "Serbest Zaman" dır. Ama katılıyorum, bununla tipik olarak sadece onu arama yeteneğine sahip olacağınız ya da bir iş arkadaşınıza sorabilirsiniz. Benim yorumum öncelikle OP'nin sorumluluğu zihinsel olarak modellemesini sağlamak ve mantık için en belirgin yerin :-) olacağı yerdi
Niklas H

İlk ifadene katılıyorum. Ancak, eğer akran incelemesine geldiyse, SRP'yi ihlal etmenin kaygan bir eğim olduğu ve bunun birçok kırık pencerenin ilki olacağı için yapabileceğimi sanmıyorum. Şerefe.
Jim Speaker,

20

SRP'nin mantıksal işlevsellik ile ilgili soyut bir kavram olduğuna işaret eden çok büyük cevaplar var ancak eklemeye değer olduğunu düşündüğüm daha ince noktalar var.

SOLID, SRP ve OCP'deki ilk iki harf, gereksinimlerinizdeki değişikliklere yanıt olarak kodunuzun nasıl değiştiğiyle ilgilidir. SRP'nin en sevdiğim tanımı şudur: "bir modül / sınıf / fonksiyonun değişmesi için tek bir nedeni olmalı." Kodunuzun değişmesinin olası nedenlerini tartışmak, kodunuzun SOLID olup olmadığını tartışmaktan çok daha verimlidir.

Çalışan sınıfınızın değişmesi için kaç neden var? Bilmiyorum, çünkü onu kullandığınız bağlamı bilmiyorum ve geleceği de göremiyorum. Yapabileceğim şey, geçmişte gördüklerime bağlı olarak olası değişiklikleri beyin fırtınası yapmak ve ne kadar muhtemel olduklarını öznel olarak değerlendirebilirsiniz. "Makul bir olasılıkla" ile "kodum bu kesin nedenden dolayı zaten değişti" arasında birden fazla puan alırsa, bu tür bir değişikliğe karşı SRP'yi ihlal ediyorsunuz demektir. İşte bir tane: ikiden fazla isme sahip biri şirketinize katılıyor (veya bir mimar bu mükemmel W3C makalesini okuyor ). İşte bir tane daha: şirketiniz tatil günlerini nasıl paylaştığını değiştiriyor.

AddHolidays yöntemini kaldırsanız bile bu nedenlerin eşit derecede geçerli olduğuna dikkat edin. Birçok anemik alan modeli, SRP'yi ihlal ediyor. Birçoğu sadece veritabanı kodlu veritabanı tablolarıdır ve veritabanı tablolarının değişmesi için 20'den fazla sebebi olması çok yaygındır.

İşte dikkat etmeniz gereken bir şey var: sisteminizin çalışanların maaşlarını izlemesi gerekiyorsa, Çalışan sınıfınız değişebilir mi? Adresler? Acil iletişim bilgileri? Bunlardan ikisine "evet" (ve "olması muhtemel") demiş olsaydınız, sınıfınız henüz kodu olmasa bile SRP'yi ihlal ediyor olacaktı! SOLID, kodla olduğu kadar süreçler ve mimariyle de ilgilidir.


9

Sınıfın verileri temsil etmesi sınıfın sorumluluğunda değildir, özel bir uygulama detayıdır.

Bir çalışanı temsil etmek için sınıfın bir sorumluluğu vardır. Bu bağlamda, çalışanlarla başa çıkmak için ihtiyaç duyduğunuz işlevselliği sağlayan bir ortak API sunar (AddHolidays'ın iyi bir örnek olup olmadığı tartışılır).

Uygulama içtir; Bu, bazı özel değişkenlere ve bir miktar mantığa ihtiyaç duyduğu için olur. Bu, sınıfın artık birden fazla sorumluluğu olduğu anlamına gelmez.


İlginç düşünce hattı, paylaşım için çok teşekkürler
tobiak777

Nice - OOP'nin amaçlanan hedeflerini ifade etmenin iyi bir yolu.
user949300

5

Mantık ve veriyi herhangi bir şekilde karıştırmanın her zaman yanlış olduğu düşüncesi o kadar saçma ki tartışmaya bile değmez. Bununla birlikte, örnekte açıkça tek bir sorumluluğun ihlali söz konusudur, ancak bunun bir özelliği DaysOfHolidaysve işlevi olmadığı için değildir AddHolidays(int).

Bunun nedeni, çalışanın kimliğinin kötü olan tatil yönetimiyle iç içe olması. Çalışanın kimliği, tatil, maaş, fazla mesai, kimin hangi takımda olduğunu göstermesi, performans raporlarına bağlanması vb. İçin gereken karmaşık bir şeydir. işçi, çalışan, personel. Çalışanlar, ASCII ve unicode yazım gibi adlarının birden çok yazımına sahip olabilirler. Kişiler, 0'dan n'ye ad ve / veya soyadına sahip olabilir. Farklı yetki alanlarında farklı isimler olabilir. Bir çalışanın kimliğini takip etmek, ikinci bir sorumluluk olmadan, tatil veya tatil yönetiminin üstüne eklenememesi sorumluluğudur.


“Bir çalışanın kimliğini takip etmek, ikinci bir sorumluluk olmadan, tatil veya tatil yönetiminin üstüne eklenemeyeceği bir sorumluluk için yeterlidir.” + Çalışanların birçok adı olabilir… vb. Modelin amacı, eldeki problem için gerçek dünyanın ilgili gerçeklerine odaklanmaktır. Bu modelin optimal olduğu gereksinimler var. Bu gereksinimlerde, Çalışanlar yalnızca tatillerini değiştirebildiğimiz ölçüde ilgi çekicidir ve gerçek yaşam özelliklerinin diğer yönlerini yönetmekle çok fazla ilgilenmiyoruz.
tobiak777,

@ reddy "Çalışanlar yalnızca tatillerini değiştirebildiğimiz ölçüde ilginçtir" - Bu, onları doğru tanımlamanız gerektiği anlamına gelir. Bir çalışanınız olur olmaz, evlilik veya boşanma nedeniyle herhangi bir zamanda soyadlarını değiştirebilirler. Ayrıca isimlerini ve cinsiyetlerini de değiştirebilirler. Soyadları, başka bir çalışanın adıyla eşleşecek şekilde değişirse, çalışanları kovacak mısınız? Şu anda tüm bu işlevleri eklemeyeceksiniz. Bunun yerine ihtiyacınız olduğunda ekleyin, ki bu iyidir. Ne kadar uygulandığına bakılmaksızın, tanımlama sorumluluğu aynı kalır.
Peter

3

"Ben SOLID'e inanıyorum. Hem veriyi temsil ederken hem de aynı sınıfta mantığı gerçekleştirirken, tek sorumluluk ilkesini (SRP) ihlal ediyorsunuz."

Diğerleri gibi, buna katılmıyorum.

Sınıfta birden fazla mantık gerçekleştiriyorsanız, SRP'nin ihlal edildiğini söyleyebilirim . Bu tek mantık parçasını elde etmek için sınıf içinde ne kadar veri depolanması gerektiğinin önemi yoktur.


Hayır! SRP, birden fazla mantık parçası, birden fazla veri parçası veya ikisinin bir kombinasyonu tarafından ihlal edilmez. Tek gereksinim, nesnenin amacına sadık kalmasıdır. Amacı potansiyel olarak birçok operasyon gerektirebilir.
Martin Maat

@MartinMaat: Birçok işlem, evet. Sonuç olarak bir mantık parçası. Sanırım aynı şeyi söylüyoruz ama farklı terimlerle (ve bu şeyleri sık sık çalışmadığım için sizinkilerin doğru olanlar olduğunu varsaymaktan mutluyum)
Orbit'teki Hafiflik Yarışları

2

Bugünlerde neyin tek bir sorumluluk veya niçin değişmek için bir sebep teşkil ettiği hakkında tartışmanın faydalı olmadığını düşünüyorum. Yerine bir Asgari Keder Prensibi öneriyorum:

Minimum Keder Prensibi: Kod, değişiklik yapma olasılığını en aza indirmeyi veya değiştirilme kolaylığını en üst seviyeye çıkarmaya çalışmalıdır.

O nasıl? Bunun neden bakım maliyetlerini azaltmaya yardımcı olabileceğini bulmak için bir roket bilimci almamalı ve umarım sonsuz bir tartışma konusu olmamalıdır, ancak genel olarak SOLID'de olduğu gibi, her yerde kör olarak uygulanacak bir şey değildir. Takas dengeleme sırasında göz önünde bulundurulması gereken bir şey.

Değişiklik gerektiren olasılık gelince, bu aşağı gider:

  1. İyi test (geliştirilmiş güvenilirlik).
  2. Özel bir şeyler yapmak için gereken yalnızca minimum kodun dahil edilmesi (bu, afferent kuplajların azaltılmasını içerebilir).
  3. Sadece kodun ne yaptığını badass yapmak (Badass Prensibi Yapma bölümüne bakınız).

Değişiklik yapma zorluğuna gelince, efferent kuplajlarla yükseliyor. Test, farklı kaplinleri tanıtır, ancak güvenilirliği artırır. İyi yapıldığında, genellikle zarardan daha iyidir ve Asgari Keder Prensibi tarafından tamamen kabul edilebilir ve desteklenir.

Badas Prensibi olun: birçok yerde kullanılan sınıflar harika olmalı. Kalite, vb. İle bağları varsa güvenilir, verimli olmalılar.

Ve Badass Yapma Prensibi, En Az Keder Prensibi'ne bağlıdır, çünkü badass işleri, yaptıkları şeyleri emen şeylerden daha düşük bir değişiklik gerektirme olasılığını bulacaktır.

Yukarıda belirtilen paradoksa işaret ederek başlayacaktım ve daha sonra SRP'nin dikkate almak istediğiniz ayrıntı düzeyine bağlı olduğunu ve yeterince ileri götürürseniz, birden fazla özellik veya bir yöntem içeren herhangi bir sınıfın ihlal ettiğini belirtmek isterdim. o.

Bir SRP açısından bakıldığında, neredeyse hiç bir şeyi yapmayan bir sınıfın değişmesi için yalnızca bir (bazen sıfır) sebep olabilir:

class Float
{
public:
    explicit Float(float val);
    float get() const;
    void set(float new_val);
};

Bunun pratik olarak değişmesi için hiçbir sebep yok! SRP'den daha iyi. ZRP!

Bunun dışında, Badass Prensibi yapmanın açıkça ihlal edildiğini ileri sürmemiştim. Aynı zamanda kesinlikle değersiz. Çok az şey yapan bir şey badass olmayı ümit edemez. Çok az bilgi (TLI) var. Ve tabii ki, TLI olan bir şeye sahip olduğunuzda, içerdiği bilgilerle bile, gerçekten anlamlı bir şey yapamaz, bu yüzden başka birinin gerçekten anlamlı ve kötü bir şey yapması umuduyla dış dünyaya sızdırması gerekir. Ve bu sızıntı sadece veri toplamayı amaçlayan bir şey için tamamdır, başka bir şey yoktur, ama bu eşik "veri" ve "nesneler" arasındaki fark olarak.

Tabii ki TMI olan bir şey de kötü. Tüm yazılımımızı bir sınıfa koyabiliriz. Sadece bir runyöntem bile olabilir . Ve birileri bile şimdi bunun değişmesi için çok geniş bir nedene sahip olduğunu iddia edebilir: "Bu sınıf sadece yazılımın geliştirilmesi gerekiyorsa değişmesi gerekir." Aptallık yapıyorum, ama elbette bununla ilgili tüm bakım konularını hayal edebiliyoruz.

Bu yüzden, tasarladığınız nesnelerin ayrıntı dereceleri veya kalınlıkları ile ilgili olarak dengeleyici bir hareket var. Genellikle dış dünyaya ne kadar bilgi sızdırmanız gerektiği ve ne kadar anlamlı işlevsellik gösterebileceği konusunda karar veririm. Badass Prensibi Yap'ı, Minimum Keder Prensibi ile birleştirirken dengeyi bulmak için sık sık yardımcı olurum.


1

Aksine, benim için Anemik etki alanı modeli bazı OOP ana kavramlarını (özellikleri ve davranışları birbirine bağlar) bozuyor ancak mimari seçimlere dayanarak kaçınılmaz olabilir. Anemik alanların düşünülmesi daha kolaydır, daha az organik ve daha sıralıdır.

Birçok sistem, birden fazla katmanın aynı verilerle (servis katmanı, web katmanı, müşteri katmanı, aracılar ...) oynaması gerektiğinde bunu yapma eğilimindedir.

Veri yapısını bir yerde ve davranışı diğer hizmet sınıflarında tanımlamak daha kolaydır. Aynı sınıf birden fazla katmanda kullanılmışsa, bu büyük büyüyebilir ve hangi katmanın ihtiyaç duyduğu davranışı tanımlamaktan sorumlu olduğunu ve yöntemleri kimin arayabildiği sorusunu sorar.

Örneğin, tüm çalışanlarınızın istatistiklerini hesaplayan ve ücretli günler için bir hesaplama çağırabilecek bir Ajan işleminden daha iyi bir fikir olmayabilir. Ve çalışan listesi GUI'sinde bu istatistik aracısında (ve bununla birlikte gelen teknik verilere) kullanılan tüm yeni toplu kimlik hesaplamasına kesinlikle gerek yoktur. Yöntemleri bu şekilde ayırdığınızda, genellikle yalnızca veri yapılarına sahip bir sınıfla sonlanırsınız.

Herhangi bir nesne kavramı / sorumluluğu hakkında endişe duymadan "nesne" verilerini veya bunların bir kısmını veya başka bir biçime (json) çok kolayca seri hale getirebilir / seri hale getirebilirsiniz. Bu sadece geçen veridir. Her zaman iki sınıf arasında veri haritalama yapabilirsiniz (Çalışan, Çalışan VO, Çalışan İstatistiksel…) ancak Çalışan burada gerçekten ne anlama geliyor?

Yani evet, etki alanı sınıflarındaki verileri ve hizmet sınıflarındaki veri işlemlerini tamamen ayırır, ancak burada buna ihtiyaç vardır. Bu tür bir sistem aynı zamanda, işletme sorumluluğu getirecek işlevsel bir sistemdir ve uygun bir sorumluluk kapsamı taşırken ihtiyaç duyulan her yerde verileri yaymak için de teknik bir sistemdir (ve dağıtılmış nesne de bunu çözmez).

Davranış kapsamlarını ayırmanız gerekmiyorsa, nesneyi nasıl gördüğünüze bağlı olarak yöntemleri hizmet sınıflarına veya etki alanı sınıflarına koymakta daha özgürsünüz. Bir nesneyi "gerçek" kavram olarak görmeye meyilliyim, bu doğal olarak SRP'yi tutmaya yardımcı olur. Öyleyse, size örnek olarak, Çalışan Patronunun PayDayAccount hesabına verilmiş olan maaş günü eklemesinden daha gerçekçi. Bir Çalışan Şirket Tarafından İşe Alınmıştır, Çalışanlar, Bir Hastadan Tavsiye Alınabilir ya da Tavsiye İstenebilir ve Payday hesabı vardır (Patron doğrudan doğrudan ondan veya bir PayDayAccount kayıt defterinden alabilir ...) Ama toplanmış bir kısayol oluşturabilirsiniz basitlik için basit bir yazılım için çok fazla karmaşıklık istemiyorsanız, burada basitlik için.


Girişin Vince için teşekkürler. Mesele şu ki, zengin bir etki alanınız olduğunda servis katmanına ihtiyacınız yok. Davranıştan sorumlu tek bir sınıf var ve bu sizin etki alanı varlığınız. Diğer katmanlar (web katmanı, UI vb ...) genellikle DTO'lar ve ViewModels ile ilgilidir ve bu iyidir. Etki alanı modeli etki alanını modellemektir, kullanıcı arabirimi çalışmaları yapmaz veya internet üzerinden mesaj gönderir. Mesajınız, insanların OOP'nin tasarımlarına nasıl sığacaklarını bilmedikleri gerçeğinden gelen bu yaygın yanılgıyı yansıtıyor. Ve bunun çok üzücü olduğunu düşünüyorum - onlar için.
tobiak777,

Zengin bir etki alanı olan OOP hakkında yanlış bir fikrim olduğunu sanmıyorum, bu yolla birçok yazılım tasarladım ve bunun bakım / evrim için gerçekten iyi olduğu doğru. Ama bunun gümüş bir mermi olmadığını söylediğim için üzgünüm.
Vince

Öyle demiyorum. Bir derleyici yazmak için muhtemelen değildir, ancak iş / SaaS uygulamalarının çoğu için önerdiğinizden çok daha az sanat / bilim olduğunu düşünüyorum. Etki alanı modeline olan ihtiyaç matematiksel olarak kanıtlanabilir, örnekleriniz beni
OOP'taki

0

Hem veriyi temsil ettiğiniz hem de aynı sınıfta mantığı gerçekleştirdiğiniz için tek sorumluluk ilkesini (SRP) ihlal ediyorsunuz.

Bu benim için çok makul geliyor. Eylemleri ortaya çıkarması durumunda modelin ortak mülkiyeti olmayabilir. Temelde bir Komut Sorgu Ayırma fikridir. Unutmayın ki Komut kesin olarak özel bir duruma sahip olacak.


0

Tek Sorumluluk İlkesini ihlal edemezsiniz, çünkü bu Doğa'nın bir kuralı değil, sadece estetik bir kriterdir. Bilimsel olarak isimlendiren isim ve büyük harfler ile yanlış yönlendirilmeyin.


1
Bu neredeyse gerçek bir cevap değil, ancak soruya bir yorum olarak çok iyi olurdu.
Jay Elston
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.