Bir güncelleme yöntemine iade türü eklemek “Tek Sorumluluk İlkesi” ni ihlal ediyor mu?


37

Çalışan verilerini veritabanında güncelleyen bir yöntemim var. Employee"Güncelleme" nesne araçları aslında yeni bir nesne örneğini böylece sınıf, sabittir.

UpdateYöntemin Employeegüncellenmiş verilerle yeni bir örneğini döndürmesini istiyorum , ancak şu andan itibaren yöntemin sorumluluğu çalışan verilerini güncellemek ve veritabanından yeni bir Employeenesneyi almak olduğunu söyleyebilirim, Tek Sorumluluk İlkesini ihlal ediyor mu?

DB kaydının kendisi güncellenir. Ardından, bu kaydı temsil etmek için yeni bir nesne başlatılır.


5
Küçük bir sözde kod burada çok uzun yollara gidebilir: Gerçekten yeni bir DB kaydı var mı, yoksa DB kaydının kendisi güncelleniyor mu, ancak "istemci" kodunda sınıf değişmez olarak modellendiği için yeni bir nesne yaratıldı mı?
Martin Ba,

Ek olarak, Martin Bra'nın sorduğu şeye, veritabanını güncellerken neden bir Çalışan örneği döndürüyorsunuz? Çalışan örneğinin değerleri Güncelleme yönteminde değişmedi, bu nedenle neden geri döndüğünüzde (arayanlar örneğe zaten erişebiliyor ...). Veya güncelleme yöntemi, veritabanından (potansiyel olarak farklı) değerleri de alıyor mu?
Thorsal

1
@Thorsal: Eğer veri varlıklarınız değişemezse, güncellenmiş verilerle bir örnek döndürmek oldukça SÇP'dir, çünkü aksi takdirde değiştirilen verinin başlatılmasını kendiniz yapmanız gerekecektir.
mikołak

21
Compare-and-swapve test-and-setçok iş parçacıklı programlama teorisinde temel işlemlerdir. Her ikisi de, geri dönüş türüne sahip güncelleme yöntemleridir ve başka türlü çalışamazlardı. Bu, komut-sorgu ayrımı veya Tek Sorumluluk İlkesi gibi kavramları bozuyor mu? Evet, ve bütün mesele bu . SRP evrensel olarak iyi bir şey değildir ve aslında aktif olarak zararlı olabilir.
MSalters

3
@ MSalters: Kesinlikle. Komut / sorgu ayırma işlemi iddiasız olarak algılanabilecek sorguları ve bir cevap beklemek zorunda kalmadan komutları yayınlamanın mümkün olduğunu, ancak atomik okuma-değiştirme-yazma işleminin üçüncü bir işlem kategorisi olarak tanınması gerektiğini söylüyor.
supercat

Yanıtlar:


16

Her kuralda olduğu gibi, buradaki önemli şeyin kuralın amacını, ruhunu göz önünde bulundurmak ve kuralın bazı ders kitaplarında nasıl ifade edildiğini ve bu davaya nasıl uygulanacağını analiz etmekten rahatsız olmamak olduğunu düşünüyorum. Buna avukat gibi yaklaşmamıza gerek yok. Kuralların amacı, daha iyi programlar yazmamıza yardımcı olmaktır. Program yazmanın amacı kurallara uymamaktır.

Tek sorumluluk kuralının amacı, her bir işlevin kendine yeten, tutarlı bir şey yapmasını sağlayarak programların anlaşılmasını ve sürdürülmesini kolaylaştırmaktır.

Örneğin, bir keresinde "checkOrderStatus" gibi bir şey çağırdığım ve bir emrin beklemekte, gönderilmekte, geri sipariş edilmek, ne olursa olsun ve hangisini belirten bir kod döndürdüğünü belirleyen bir işlev yazdım. Sonra başka bir programlayıcı geldi ve sipariş gönderildiğinde eldeki miktarı da güncellemek için bu işlevi değiştirdi. Bu, tek sorumluluk ilkesini ciddi şekilde ihlal etti. Bu kodu daha sonra okuyan başka bir programcı işlev adını görecek, dönüş değerinin nasıl kullanıldığını görecek ve bir veritabanı güncellemesi yaptığından asla şüphelenmeyecektir. Eldeki miktarı güncellemeden sipariş durumunu almak için gereken kişi garip bir pozisyonda olurdu: sipariş durumu bölümünü kopyalayan yeni bir fonksiyon yazmalı mı? DB güncellemesi yapılıp yapılmayacağını söylemek için bir bayrak ekle? Vb.

Öte yandan, "iki şeyi" neyin oluşturduğunu nitelemiyorum. Kısa süre önce, müşteri bilgilerini sistemimizden müşterinin sistemine gönderen bir işlev yazdım. Bu işlev, gereksinimlerini karşılamak için verilerin bazı biçimlendirmelerini yapar. Örneğin, veritabanımızda boş olabilecek bazı alanlarımız var, ancak boş değerlere izin vermiyorlar, bu yüzden "belirtilmemiş" gibi bazı sahte metinleri doldurmamız gerekiyor ya da tam kelimeleri unutuyorum. Muhtemelen bu işlev iki şey yapıyor: verileri yeniden biçimlendirmek VE göndermek. Fakat bunu “kasıtlı” ve “gönder” den ziyade bunu kasıtlı olarak tek bir işleve koydum çünkü hiçbir zaman, yeniden biçimlendirmeden göndermeyi istemiyorum. Birinin yeni bir arama yazmasını istemiyorum ve reformat çağırması ve sonra göndermesi gerektiğini fark etmiyorum.

Senin durumunda, veritabanını güncelle ve yazılan kaydın bir görüntüsünü döndür, mantıksal ve kaçınılmaz olarak bir araya gelebilecek iki şey gibi görünüyor. Uygulamanızın ayrıntılarını bilmiyorum, bu yüzden bu iyi bir fikirse de olmasa da kesin olarak söyleyemem ama mantıklı geliyor.

Kayıt için tüm verileri tutan bellekte bir nesne oluşturuyorsanız, veritabanında bunu yapmak için arama yaparsanız ve ardından nesneyi döndürürseniz, bu çok mantıklı olur. Nesnenin elinde var. Neden sadece geri vermiyorsun? Nesneyi geri getirmeseydin, arayan onu nasıl elde ederdi? Yeni yazdığınız nesneyi almak için veritabanını okumak zorunda kalacak mıydı? Bu oldukça verimsiz görünüyor. Rekoru nasıl bulur? Birincil anahtarı biliyor musunuz? Birisi, yazma işlevinin kaydı yeniden okuyabilmeniz için birincil anahtarı geri getirmesinin "yasal" olduğunu beyan ederse, neden sadece kaydı geri almak zorunda değilsiniz? Fark ne?

Öte yandan, nesneyi oluşturmak, veritabanı kaydını yazmaktan oldukça farklı bir grup iş ise ve arayan bir kişi yazmayı iyi yapmak isteyebilir, ancak nesneyi oluşturmak istemeyebilir, o zaman bu israf olabilir. Arayan bir nesneyi isteyebilir ancak yazma işlemini yapmıyorsa, o zaman nesneyi elde etmek için başka bir yol sağlamanız gerekir; bu, gereksiz kod yazmak anlamına gelebilir.

Ancak, senaryo 1'in daha muhtemel olduğunu düşünüyorum, o yüzden şunu söyleyebilirim ki, muhtemelen sorun değil.


Tüm cevaplar için teşekkürler, ama bu gerçekten bana en çok yardımcı oldu.
Sipo

Yeniden biçimlendirme yapmadan göndermek istemezseniz ve verileri daha sonra göndermenin yanı sıra yeniden biçimlendirmenin bir yolu yoktur, o zaman ikisi değil bir şeydir.
Joker_vD

public function sendDataInClientFormat() { formatDataForClient(); sendDataToClient(); } private function formatDataForClient() {...} private function sendDataToClient() {...}
CJ Dennis

@CJDennis Tabii. Ve aslında böyle yaptım: Biçimlendirme yapmak için bir işlev, gerçek gönderimi yapmak için bir işlev ve buraya girmeyeceğim iki işlev daha vardı. Daha sonra hepsini sırayla çağırmak için bir üst seviye işlev. "Biçimlendirme ve gönderme" nin bir mantıksal işlem olduğunu ve bu nedenle tek bir işlevde uygun şekilde birleştirilebileceğini söyleyebilirsiniz. Bunun iki olduğunu ısrar ediyorsanız, tamam, o zaman hala mantıklı olan şey, her şeyi yapan bir üst düzey fonksiyona sahip olmaktır.
Jay

67

SRP kavramı, ayrı ayrı yaptıklarında 2 farklı şey yapan modülleri durdurmaktır; zaman içinde daha iyi bakım ve daha az spagetti sağlar. SRP'nin dediği gibi "değişmesi için bir sebep".

Sizin durumunuzda, "güncellenmiş nesneyi güncelleyen ve döndüren" bir yordamınız varsa, nesneyi hala bir kez değiştiriyorsunuz - değişmesi için 1 neden. Nesneyi hemen geri döndürürseniz, ne burada ne de orada, o tek nesne üzerinde hala çalışıyorsunuz. Kodun bir ve bir tanesinin sorumluluğu var.

SRP, tüm işlemleri tek bir çağrıya indirgemeye çalışmakla ilgili değil, üzerinde çalıştığınızı ve üzerinde nasıl çalıştığınızı azaltmakla ilgili. Bu nedenle, güncellenen nesneyi döndüren tek bir güncelleme iyi.


7
Alternatif olarak, "tüm işlemleri tek bir aramaya indirmeye çalışmakla ilgili" değil, söz konusu öğeyi kullanırken düşünmeniz gereken farklı vakaları azaltmaktır.
jpmc26

46

Her zaman olduğu gibi, bu bir derece sorunudur. SRP, harici bir veritabanından kayıt alan, üzerinde hızlı bir Fourier dönüşümü gerçekleştiren ve sonuçta bir genel istatistik kayıt defterini güncelleyen bir yöntem yazmanızı engellemelidir. Bence neredeyse herkes bu şeylerin farklı yöntemlerle yapılması gerektiği konusunda hemfikirdir. Bir vazetmede tek her yöntem için sorumluluk sadece o noktayı en ekonomik ve unutulmaz bir yoludur.

Spektrumun diğer ucunda, bir nesnenin durumu hakkında bilgi veren yöntemlerdir. Tipik isActiveolarak bu bilgiyi tek bir sorumluluk olarak vermiştir. Muhtemelen herkes bunun iyi olduğunu kabul eder.

Şimdi, bazıları, bir başarı bayrağını döndürmeyi başardıkları rapor edilen eylemi gerçekleştirmekten farklı bir sorumluluk almayı düşünecekleri kadar ilkeyi genişletiyorlar. Son derece katı bir yorum altında, bu doğrudur, ancak alternatif, arayanı karmaşıklaştıran başarı durumunu elde etmek için ikinci bir yöntem çağırmak zorunda kalacağından, birçok programcı, başarı kodlarını, yan etkileri olan bir yöntemden döndürme konusunda gayet iyi durumdadır.

Yeni nesneyi iade etmek, birden fazla sorumluluğa giden yolda bir adım daha ileride. Arayan kişinin bütün bir nesne için ikinci bir arama yapmasını istemek, yalnızca birincinin başarılı olup olmadığını görmek için ikinci bir çağrı yapmaktan biraz daha makul. Yine de, birçok programcı güncellemenin sonucunu tamamen iyi bir şekilde iade etmeyi düşünür. Bu, iki hafif farklı sorumluluk olarak yorumlanabilse de, kesinlikle, başlangıç ​​ilkesine ilham veren korkunç suçlardan biri değildir.


15
Birincisinin başarılı olup olmadığını görmek için ikinci bir yöntemi çağırmanız gerekiyorsa, ikincisinin sonucunu almak için üçüncü bir yöntemi çağırmanıza gerek kalmaz mı? Öyleyse sonucu gerçekten istiyorsan ...

3
SRP'nin aşırı gayretli uygulamasının, başlı başına bir yük olan sayısız küçük sınıfa yol açtığını da ekleyebilirim.
Yükünüzün

8

Tek Sorumluluk İlkesini ihlal ediyor mu?

Şart değil. Eğer bir şey varsa, bu komut-sorgu ayrıştırma ilkesini ihlal ediyor .

Sorumluluk

çalışan verilerini güncelle

bu sorumluluğun operasyonun durumunu içerdiği açık anlayışı ile; örneğin, işlem başarısız olursa, bir istisna atılır, başarılı olursa güncelleme çalışanı iade edilir vb.

Yine, bunların hepsi bir derece ve öznel yargı meselesidir.


Peki ya komut-sorgu ayrımı?

Eh, bu ilke var, ancak bir güncellemenin sonucunu döndürmek aslında çok yaygın.

(1) Java Set#add(E), öğeyi ekler ve kümeye önceki dahil edilmesini döndürür.

if (visited.add(nodeToVisit)) {
    // perform operation once
}

Bu, iki arama yapmak zorunda kalabilecek CQS alternatifinden daha verimlidir.

if (!visited.contains(nodeToVisit)) {
    visited.add(nodeToVisit);
    // perform operation once
}

(2) Karşılaştır ve değiştir , getir ve ekle ve test et ve ayarla , eşzamanlı programlamanın var olmasına izin veren genel ilkelerdir. Bu modeller, düşük seviyeli CPU talimatlarından yüksek seviyeli eş zamanlı koleksiyonlara kadar sıklıkla görülür.


2

Tek sorumluluk, birden fazla nedenden dolayı değişmesi gerekmeyen bir sınıfla ilgilidir.

Örnek olarak, çalışanın telefon numaralarının bir listesi vardır. Çalışan numaralarını hiç değiştirmemesi gereken telefon numaralarını değiştirme biçiminiz değiştiğinde (ülke arama kodları ekleyebilirsiniz).

Çalışan sınıfının kendisini veritabanına nasıl kaydettiğini bilmesine ihtiyaç duymayacağım, çünkü çalışanlardaki değişiklikler ve verilerin depolanma şeklindeki değişiklikler için değişecektir.

Benzer şekilde, çalışan sınıfında CalculateSalary yöntemi olmamalıdır. Maaş malları tamamdır, ancak vergi vb. Hesaplamaları başka bir yerde yapılmalıdır.

Ancak, Güncelleme yöntemi sadece güncellemiş olduklarını döndürür.


2

Wrt. özel durum:

Employee Update(Employee, name, password, etc) (aslında bir çok parametreye sahip olduğumdan beri bir Builder kullanarak).

Gibi görünüyorUpdate Yöntem varolan alır Employee(?) Mevcut çalışan ve bu çalışanın üzerinde değişikliğe parametreleri grubu tanımlamak için ilk parametre olarak.

Ben do bu düşünmek olabilir bitti daha temiz. İki dava görüyorum:

(a) Employee aslında her zaman veritabanında tanımlanabileceği bir veritabanı / benzersiz bir kimlik içerir. (Yani, sen do not DB bulmak için ayarlanmış bütün kaydı değerleri gerekir.

Bu durumda, void Update(Employee obj)mevcut kaydı sadece ID'ye göre bulabilen ve sonra da geçirilen nesneden alanları güncelleyen bir yöntem tercih ederim . Ya da belki birvoid Update(ID, EmployeeBuilder values)

Bunun yararlı bulduğum bir varyasyon void Put(Employee obj), kaydın (kimliğe göre) bulunup bulunmadığına bağlı olarak yalnızca ekleyen veya güncelleyen bir yönteme sahip olmaktır .

(b) tam varolan rekor bu durumda hala olması daha mantıklı olabilir, DB arama için gereklidir: void Update(Employee existing, Employee newData).

Burada görebildiğim kadarıyla, saklamak ve gerçekten depolamak için yeni bir nesne (veya alt nesne değerleri) inşa etme sorumluluğunun ortogonal olduğunu söyleyebilirim, bu yüzden onları ayıracağım.

Diğer cevaplardaki (atomik set-ve-geri al / karşılaştır-takas vb.) Belirtilen eşzamanlı gereksinimler, şu ana kadar üzerinde çalıştığım DB kodunda bir sorun olmamıştır. Bir DB ile konuşurken, bunun işlem düzeyinde tek tek ifade düzeyinde değil, normal olarak ele alınması gerektiğini düşünüyorum. (Bu, "atom" un Employee[existing data] Update(ID, Employee newData)anlam ifade edemediği bir tasarım olamayacağı anlamına gelmez, ancak DB ile normalde gördüğüm bir şey değil.)


1

Şimdiye kadar buradaki herkes derslerden bahsediyor. Ancak bunun hakkında arayüz perspektifinden düşünün.

Bir arayüz yöntemi bir geri dönüş tipi ve / veya geri dönüş değeri hakkında açık bir söz verirse, her uygulamanın güncellenen nesneyi geri göndermesi gerekir.

Yani sorabilirsiniz:

  • Yeni bir nesneyi döndürmekle uğraşmak istemeyen olası uygulamaları düşünebiliyor musunuz?
  • Güncellenmiş çalışanın bir geri dönüş değeri olarak ihtiyacı olmayan arayüze bağlı (örneğin enjekte edilmiş halde) bileşenleri düşünebiliyor musunuz?

Ayrıca, birim testleri için sahte nesneleri düşünün. Açıkçası, dönüş değeri olmadan alay etmek daha kolay olacak. Bağımlı bileşenlerin daha basit arayüzleri varsa, bağımlı bileşenlerin test edilmesi daha kolaydır.

Bu düşüncelere dayanarak bir karar verebilirsiniz.

Ve sonradan pişman olursanız, ikisi arasında bağdaştırıcılar olan ikinci bir arayüz sunmaya devam edebilirsiniz. Tabii ki, bu tür ilave arayüzler bileşimsel karmaşıklığı arttırmaktadır, bu yüzden hepsi bir dengedir.


0

Yaklaşımınız iyi. Taklit edilebilirlik güçlü bir iddiadır. İstediğim tek şey şudur: Nesneyi yapılandırdığınız başka bir yer var mı. Nesneniz değişmez değilse, "Devlet" tanıtıldığından ek soruları cevaplamanız gerekir. Ve bir nesnenin durum değişikliği farklı sebeplerle ortaya çıkabilir. Öyleyse davalarınızı bilmelisiniz ve gereksiz veya bölünmüş olmamalıdırlar.

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.