Demeter yasasına göre, bir sınıf üyelerinden birini iade edebilir mi?


13

Demeter yasasıyla ilgili üç sorum var.

Fabrika ve inşaatçı sınıfları gibi nesneleri döndürmek için özel olarak atanan sınıflar dışında, bir nesnenin, örneğin sınıfın özelliklerinden biri tarafından tutulan bir nesnenin veya demeter yasasını ihlal eden bir nesnenin döndürülmesi uygun mudur (1) ? Demeter yasasını ihlal ederse, geri döndürülen nesnenin bir veri parçasını temsil eden ve bu veriler için alıcılardan başka bir şey içermeyen değişmez bir nesne olması önemli midir? Yoksa böyle bir ValueObject kendi içinde bir anti-kalıp mıdır, çünkü o sınıftaki verilerle yapılan her şey sınıf (2b) dışında mı yapılır?

Sahte kodda:

class A {}

class B {

    private A a;

    public A getA() {
        return this.a;
    }
}

class C {

    private B b;
    private X x;

    public void main() {
        // Is it okay for B.getA to exist?
        A a = this.b.getA();

        a.doSomething();

        x.doSomethingElse(a);
    }
}

Demeter yasasının yukarıdaki gibi bir modeli yasakladığından şüpheleniyorum. doSomethingElse()Yasayı ihlal etmeden aranabilmesini sağlamak için ne yapabilirim (3)?


9
Birçok insan (özellikle işlevsel programcılar) Demeter Yasasını bir anti-model olarak görüyor. Diğerleri ise bunu "Nadiren Yararlı Demeter Önerisi" olarak görüyor.
David Hammen

1
Bu soruyu değerlendireceğim çünkü Demeter Yasasının saçmalıklarını gösteriyor. Evet, LoD "zaman zaman yararlıdır", ancak genellikle aptaldır.
user949300

Nasıl xkurulur?
candied_orange

@CandiedOrange x, değeri yalnızca çağırabilen bir nesne olan farklı bir özelliktir doSomethingElse.
user2180613

1
Örneğiniz LOD'u ihlal etmiyor. LOD, bir nesnenin istemcilerinin o nesnelerin dahili ayrıntılarına veya ortak çalışanlarına eşleşmesini önlemektir. gibi şeylerden kaçınmak istersiniz user.currentSession.isLoggedIn(). çünkü müşterilerini usersadece userdeğil, aynı zamanda işbirlikçisiyle de eşleştiriyor session. Bunun yerine yazabilmek istiyorsunuz user.isLoggedIn(). bu genellikle uygulama temsilcilerine bir isLoggedInyöntem eklenerek usergerçekleştirilir currentSession.
Jonah

Yanıtlar:


7

Birçok programlama dilinde, tüm döndürülen değerler nesnedir. Diğerlerinin söylediği gibi, döndürülen nesnelerin yöntemlerini kullanmamak sizi hiçbir şey döndürmemenize zorlar. Kendinize, "A, B ve C sınıfı sorumlulukları nelerdir?" Diye sormalısınız. Bu nedenle A, B ve C gibi metasyntaktik değişken isimlerini kullanmak her zaman "buna bağlıdır" cevabını gerektirir çünkü bu terimlerde kalıtsal sorumluluk yoktur.

İşlevselliğin nereye gitmesi gerektiği ve kimin neyi çağırması gerektiği konusunda size daha nüanslı bir sezgisel tarama seti verecek olan Etki Alanına Dayalı Tasarım'a bakmak isteyebilirsiniz.

Değişmez nesnelere ilişkin ikinci sorunuz Varlık nesnesine kıyasla bir
Değer Nesnesi kavramına değinmektedir. DDD'de, değer nesnelerini neredeyse hiç kısıtlama olmadan geçirebilirsiniz. Bu durum varlık nesneleri için doğru değildir.

Basit LOD, Agregalara erişim kuralları olarak DDD'de daha iyi ifade edilir . Hiçbir dış cisim, toplanma üyelerine başvuruda bulunmamalıdır. Sadece bir kök referansı tutulmalıdır.

DDD bir yana, en azından sınıflarınız için alanınız için uygun olan kendi stereotip setlerinizi geliştirmek istiyorsunuz. Sisteminizi tasarlarken bu stereotiplerle ilgili kuralları uygulayın.

Ayrıca, tüm bu kuralların karmaşıklığı yönetmek olduğunu, kendinizi zorlamak olmadığını unutmayın.


1
DDD, veriler gibi sınıf üyelerinin ne zaman açığa çıkabileceğine ilişkin herhangi bir kural sağlıyor mu (yani alıcılar olmalıdır)? Tüm tartışmalar etrafında Law of Demeter, tell, don't ask, getters are evil, object encapsulationve single responsibility principlebu soruya aşağı kaynatın görünmektedir: Bir etki alanı nesnesine heyet ne zaman kullanılması gerektiğini nesnenin değer elde ve onunla bir şey yapıyor aksine? Bu tür bir kararın verilmesi gereken durumlara nüanslı yeterlilikler uygulamak çok yararlı olacaktır.
user2180613

Birçok kişi sınıf örneklerini veri yapıları olarak ele aldığı için (getters kötüdür) gibi kılavuzlar mevcuttur. Bu nesne yönelimli programlama değildir. Nesneler, aslında bazı işler sağlayan işlevsellik / davranış demetleridir . İşin kapsamı sorumluluk tarafından tanımlanır. Bu çalışma, yöntemlerden vb. Döndürülen nesneleri açıkça yaratacaktır. Sınıfınız aslında "veri nesnesini X, Y ve Z nitelikleriyle temsil et" ifadesinin ötesinde açıkça tanımlayabileceğiniz anlamlı bir iş yapıyorsa, doğru yoldasınız demektir. . Bunun üzerinde durmam.
Jeremy

Bu noktayı genişletmek için, çok sayıda alıcı / soru soran kullandığınızda, bu genellikle Fowler'ın bir işlem komut dosyası olarak adlandırdığı her şeyi düzenleyen bir yerde büyük bir yönteminiz olduğu anlamına gelir. Alıcıyı kullanmaya başlamadan önce kendinize sorun ... neden bu verileri nesneden soruyorum? Neden bu işlev nesne üzerindeki bir yöntemde değil? Bu işlevi uygulamak bu sınıfın sorumluluğunda değilse, devam edin ve bir alıcı kullanın. Fowler'in [Refactoring] ( martinfowler.com/books/refactoring.html ) kitabı bu tür sorular için sezgisel tarama üzerine bir kitap.
Jeremy

Etki Alanına Dayalı Tasarımın Uygulanması kitabında Vaughn Vernon, Demeter ve Söyle, Sorma, Bölüm 10, s. 382'den bahsediyor. Buna erişiminiz varsa bir göz atmaya değer olabilir.
Jeremy

6

Demeter yasasına göre, bir sınıf üyelerinden birini iade edebilir mi?

Evet kesinlikle.

Ana noktalara bakalım :

  • Her birim diğer birimler hakkında sadece sınırlı bilgiye sahip olmalıdır: sadece mevcut birim ile ilgili "yakından" birimler.
  • Her birim sadece arkadaşlarıyla konuşmalıdır; yabancılarla konuşma.
  • Sadece yakın arkadaşlarınızla konuşun.

Bunların üçü de size bir soru soruyor: Arkadaş kim?

Ne döneceğine karar verirken, Demeter Yasası veya en az bilgi ilkesi (LoD), onu ihlal etmekte ısrar eden kodlayıcılara karşı savunmanızı gerektirmez. Kodlayıcıları ihlal etmeye zorlamamanızı gerektirir.

Bunların kafasını karıştırmak, birçoğunun neden bir ayarlayıcının her zaman geçersiz kalması gerektiğini düşünmesidir. Hayır. Sistemin durumunu değiştirmeyen sorgular (alıcılar) oluşturmanın bir yolunu kullanmalısınız. Bu temel komut sorgusu ayırmasıdır .

Bu, istediğiniz herhangi bir kod zincirini araştırmakta özgür olduğunuz anlamına mı geliyor? Hayır. Birlikte zincirleme sadece zincirleme kastedilen şeydi. Aksi takdirde zincir değişebilir ve aniden eşyalarınız bozulur. Arkadaşlar tarafından kastedilen budur.

Uzun zincirler için tasarlanabilir. Akıcı arayüzler, iDSL, çoğu Java8 ve iyi eski StringBuilder, uzun zincirler oluşturmanıza izin vermek içindir. LoD'yi ihlal etmezler çünkü zincirdeki her şey birlikte çalışmak içindir ve birlikte çalışmaya devam edeceğine söz vermiştir. Birbirinizi hiç duymayan şeyleri birbirine zincirlediğinizde demeter ihlal ediyorsunuz. Arkadaşlar zincirinizi çalışmaya devam edeceğine söz veren kişilerdir. Arkadaşların arkadaşları yoktu.

Fabrika ve inşaatçı sınıfları gibi nesneleri döndürmek için özel olarak atanan sınıflar dışında, bir nesnenin, örneğin sınıfın özelliklerinden biri tarafından tutulan bir nesnenin veya demeter yasasını ihlal eden bir nesnenin döndürülmesi uygun mudur (1) ?

Bu sadece demeti ihlal etme fırsatı yaratır. Bu bir ihlal değil. Bu hiç de fena değil.

Demeter yasasını ihlal ederse, geri döndürülen nesnenin bir veri parçasını temsil eden ve bu veriler için alıcılardan başka bir şey içermeyen değişmez bir nesne olması önemli midir?

Değişmez iyidir ama burada önemsizdir. Daha uzun bir zincirden geçmek, onu daha iyi yapmaz. Onu daha iyi yapan şey, kullanımı elde etmekten ayırmaktır. Kullanıyorsanız, parametre olarak neye ihtiyacınız olduğunu sorun. Yabancıların avcılarına girerek bunun için avlanmayın.

Sahte kodda:

Demeter yasasının yukarıdaki gibi bir modeli yasakladığından şüpheleniyorum. Yasayı ihlal etmeden (3) doSomethingElse () yönteminin çağrılmasını sağlamak için ne yapabilirim?

Konuşmak önce yaklaşık x.doSomethingElse(a)sen ikonların yazdım anlıyoruz

b.getA().doSomething()

Şimdi, LoD nokta sayma egzersizi değildir . Ancak bir zincir oluşturduğunuzda, A(kullanarak B) nasıl alacağınızı ve nasıl kullanılacağını bildiğinizi söylüyorsunuz A. Peki şimdi Ave Byakın arkadaş olsan iyi olur çünkü onları bir araya getirdin.

Sen sadece edecek bir şeyler istedi olsaydı bir Aşey gibi kullanmak olabilir Ave geldiği ve nerede bakım olmazdı Balma ile hayat mutlu ve saplantı serbest yaşayabilir Adan B.

Gelince x.doSomethingElse(a)nerede ayrıntıları olmadan xgeldi LoD bu konuda söylenecek hiç bir ilgisi yoktur.

LOD kullanımı inşaattan ayırmayı teşvik edebilir . Ama ben dinsel olarak her nesneye dostça davranırsanız, statik yöntemlerle kod yazmaya sıkışıp kalacağınıza dikkat çekeceğim. Bu şekilde ana olarak harika karmaşık bir nesne grafiği oluşturabilirsiniz, ancak sonunda çalışmayı başlatmak için üzerine bir yöntem çağırmanız gerekir. Sadece arkadaşlarınızın kim olduğuna karar vermelisiniz. Bundan kaçmak yok.

Yani evet, bir sınıfın LoD kapsamında üyelerinden birini iade etmesine izin verilir. Bunu yaptığınızda, o üyenin sınıfınızla dost olup olmadığını açıkça belirtmelisiniz çünkü eğer değilse, bazı müşteriler kullanmadan önce onu almak için sizi bu sınıfa bağlamayı deneyebilir. Bu önemlidir, çünkü şimdi geri döndüğünüz şey her zaman bu kullanımı desteklemelidir.

Bunun sadece bir endişe olmadığı birçok durum var. Koleksiyonlar bunu görmezden gelebilir, çünkü onları kullanan her şeyle arkadaş olmak içindir. Benzer şekilde değer nesneleri herkesle dosttur. Ancak, bir çalışan nesnesinin bir adresden çıkarılmasını isteyen bir adres doğrulama yardımcı programı yazdıysanız, sadece adresi isteyin, çalışanın ve adresin aynı kütüphaneden gelmesini umarsınız.


Akıcı arayüzler bazı dostluk kavramları nedeniyle LOD için bir istisna değildir .... Akıcı bir arayüzde, zincirdeki yöntemlerin her biri aynı nesne üzerinde çağrılır, çünkü hepsi kendi kendine döner. settings.darkTheme().monospaceFont()sadece sözdizimsel şekersettings.darkTheme(); settings.monospaceFont();
Jonah

3
@Jonah Kendi kendini döndürmek en büyük dostluktur. Ve tüm akıcı arayüzler bunu yapmaz. Birçok iDSL de akıcıdır ve mevcut yöntemleri farklı noktalarda değiştirmek için farklı türler döndürür. JOOQ , adım oluşturucu , sihirbaz oluşturucu ve kendi kabus canavarı oluşturucumu düşünün . Akıcı bir tarz. Bir desen değil.
candied_orange

2

Nesneleri iade etmek için özel olarak atanan sınıflar dışında [...]

Bu argümanı tam olarak anlamıyorum. Herhangi bir nesne, bir mesaj çağrısının sonucu olarak bir nesneyi döndürebilir. Özellikle "herşey nesne olarak" diye düşünürseniz.

Ancak sınıflar, kendi türlerine ait yeni nesneleri "yeni" mesajını göndererek (veya çoğu zaman en yaygın OOP dillerinde yeni bir anahtar kelimeyle değiştirerek) başlatabilen tek nesnedir.


Her neyse, sorunuzla ilgili olarak, başka bir yerde kullanmak için özelliklerinden birini döndüren bir nesne hakkında oldukça şüpheliyim. Bu nesne, durumu veya uygulama ayrıntıları hakkında bilgi sızdırıyor olabilir.

Ve C nesnesinin B'nin uygulama ayrıntıları hakkında bilgi sahibi olmasını istemezsiniz. Bu, C nesnesinin perspektifinden A nesnesine istenmeyen bir bağımlılıktır.

Bu yüzden kendinize, A'yı bilerek C'nin LOD'dan bağımsız olarak yapması gereken iş için çok fazla şey bilmediğini sormak isteyebilirsiniz.

Bunun meşru olabileceği durumlar vardır, örneğin, bir koleksiyon nesnesinden eşyalarından birini istemek uygundur ve herhangi bir Demeter kuralını ihlal etmemektedir. Öte yandan bir Book nesnesinden SQLConnection nesnesi istemesi risklidir ve bundan kaçınılmalıdır.

LOD vardır, çünkü birçok programcı bir çalışma yapmadan önce nesnelerden bilgi isteme eğilimindedir. LOD bize, bazen (her zaman olmasa da) nesnelerimizin çok fazla şey yapmalarını isteyerek işleri çok karmaşık hale getirdiğimizi hatırlatmak için var. Bazen başka bir nesneden işi bizim için yapmasını istemeliyiz. LOD, davranışı nesnelerimize nereye yerleştirdiğimiz konusunda iki kez düşünmemizi sağlıyor.


0

Fabrika ve kurucu sınıfları gibi nesneleri döndürmek için özel olarak atanan sınıfların dışında, bir yöntemin bir nesneyi döndürmesi uygun mudur?

Neden olmasın? Diğer programcılarınıza, sınıfın özellikle nesneleri döndürmek için olduğunu veya nesnelerin hiç döndürülmemesi gerektiğini belirtmek gibi görünüyor. Her şeyin bir nesne olduğu dillerde, bu, hiçbir şey döndüremeyeceğiniz için seçeneklerinizi ciddi şekilde sınırlar.


Örnek, örtük b.getA().doSomething()vex.doSomethingElse(b.getA())
user2180613

A a = b.getA(); a.DoSomething();- bir noktaya geri dönelim.
Robert Harvey

2
@ user2180613 - Sormak istediyseniz b.getA().doSomething()ve x.doSomethingElse(b.getA())bunu açıkça sormuş olmalısınız. Durduğu gibi, sorunuz hakkında this.b.getA().
David Hammen

1
As Aüyesi değildir C, değil oluşturulan Cve sağlanan değil C, kullanarak görünüyor Aiçinde C(her ne şekilde cinsinden) LOD ihlal ediyor. Nokta saymak LOD'un konusu değil, sadece açıklayıcı bir araçtır. Driver().getCar().getSteeringWheel().getFrontWheels().toRight()Her biri bir nokta içeren ayrı ifadelere dönüştürmek mümkündür , ancak LoD ihlali hala oradadır. Bu forumdaki birçok gönderide, eylemleri gerçekleştirmenin gerçek davranışı uygulayan nesneye devredilmesi gerektiğini okudum tell don't ask. Dönen değerler bununla çelişiyor gibi görünüyor.
user2180613

1
Sanırım bu doğru ama soruyu cevaplamıyor.
user2180613

0

Ne yaptığınıza bağlı. Sınıfınızın bir üyesini ortaya çıkarırsanız, hiç kimsenin işi bu sınıfınızın bir üyesi değilse veya o üyenin türü nedir, bu kötü bir şeydir. Daha sonra o üyenin türünü ve üyeyi döndüren işlevin türünü değiştirirseniz ve herkesin kodunu değiştirmek zorunda kalırsa, bu kötülüktür.

Öte yandan, sınıfınızın arayüzü, tanınmış bir A sınıfı örneğini sağlayacağını belirtiyorsa, sorun değil. Eğer şanslıysanız ve bunu sınıfınızın bir üyesini sağlayarak yapabilirsiniz, size iyi. Bu üyeyi A olmaktan B'ye dönüştürdüyseniz, A'yı döndüren yöntemi yeniden yazmanız gerekir, açıkçası şimdi bir tane oluşturmalı ve yalnızca bir satır döndürme yerine kullanılabilir verilerle doldurmalıdır. Bir üye. Sınıfınızın arayüzü değişmez.

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.