Bunun gibi bir kod “tren kazası” mı (Demeter Yasası'na aykırı olarak)?


23

Yazdığım bazı kodlara göz atarken, beni düşündüren şu yapıya rastladım. İlk bakışta yeterince temiz görünüyor. Evet, gerçek kodda, getLocation()yöntemin tam olarak hangi yeri aldığını açıklayan daha belirgin bir adı vardır.

service.setLocation(this.configuration.getLocation().toString());

Bu durumda, serviceyöntem içinde bildirilen bilinen bir türün örnek değişkenidir. this.configurationsınıf kurucusuna dahil olmaktan gelir ve belirli bir arayüzü (bir genel getLocation()yöntemi zorunlu kılan ) uygulayan bir sınıf örneğidir . Dolayısıyla, ifadenin dönüş tipi this.configuration.getLocation()bilinmektedir; Özellikle bu durumda, bir java.net.URL, oysa service.setLocation()bir String. İki tür Dize ve URL doğrudan uyumlu olmadığından, bazı dönüşüm çeşit yuvarlak deliğe kare peg sığdırmak için gereklidir.

Bununla birlikte , Temiz Kod'da belirtildiği gibi, Demeter Yasasına göre , C sınıfındaki bir f yöntemi yalnızca C yöntemlerini, f ile oluşturulan veya f için argüman olarak iletilen nesneleri ve C'nin örnek değişkenlerinde tutulan nesneleri çağırmalıdır . Bunun ötesindeki herhangi bir şey ( yukarıdaki özel davamdaki son , yöntem çağrısının kendisinin bir sonucu olarak oluşturulan geçici bir nesneyi düşünmediğiniz sürece, bu durumda tüm Kanun'un tartışmasız görünmesine izin verilmez).toString()

Yukarıdaki gibi bir çağrının, listelenen kısıtlamalar göz önüne alındığında, önerilmemesi veya hatta izin vermemesi gerektiğine dair geçerli bir neden var mı? Yoksa sadece aşırı nitpicky mi oluyorum?

URLToString()Basitçe toString()bir URLnesneyi çağıran (geri döndürülenler getLocation()gibi) bir parametre olarak uyguladıysam ve sonucu döndüren bir yöntem uygulayacak olsaydım getLocation(), aynı sonucu elde etmek için çağrıyı içine sarabilirdim ; etkili bir şekilde, dönüşümü bir adım öteye taşıdım. Bu bir şekilde kabul edilebilir yapar mı? (O görünüyor yaptığı tüm biraz etrafında hareket şeyler dolayısıyla, herhangi bir fark her iki şekilde yapmak gerektiğini, sezgisel, bana. Fakat Demeter Kanunun harfi ile gidiş atıf olarak, bu günümüze dek, kabul edilebilir o zaman doğrudan bir fonksiyona bir parametre üzerinde çalışıyor olabilir.)

Bu, toString()standart bir tür aramaktan biraz daha egzotik bir şeyle ilgili olsaydı, herhangi bir fark yaratır mıydı ?

Cevap verirken, servicedeğişkenin olduğu tipte davranış veya API'yi değiştirmenin pratik olmadığını unutmayın. Ayrıca, argüman uğruna, diyelim ki, geri dönüş tipini değiştirmenin getLocation()de pratik olmadığını diyelim .

Yanıtlar:


34

Buradaki sorun olduğunu imzası setLocation. Bu oluyor stringly yazdınız .

Detaylandırmak için: Neden beklesin String? A her türlü metin verisiniString temsil eder . Potansiyel bir yer dışında herhangi bir şey olabilir.

Aslında, bu bir soru soruyor: bir yer nedir? Kodunuza bakmadan nasıl anlarım ? Daha URLfazlası olsaydı, bu yöntemin beklediği şey hakkında çok daha fazla şey bilirim.
Belki de özel bir sınıf olması daha mantıklı olur Location. Tamam, ilk önce ne olduğunu bilemem, ama bir noktada (muhtemelen yazmadan önce this.configuration.getLocation()bu yöntemin ne işe yaradığını bulmak için bir dakikanızı alırdım).
Her iki durumda da, beklenenleri anlamak için başka bir yere bakmam gerekir. Bununla birlikte, ikinci durumda, anlarsam ne Locationolduğunu, API'nizi kullanabilirim, önceki durumda, anlarsam, ne Stringolduğunu (beklenen), API'nizin ne beklediğini hala bilmiyorum.

Olası senaryoda, bir konumun herhangi bir metinsel veri olduğu, bunu metinsel bir temsili olan herhangi bir veriye yeniden yorumlayacağım . Bunun Objectbir toStringyöntemi olduğu gerçeği göz önüne alındığında, kodunuzun istemcilerinden tam bir inanç sıçraması talep etmesine rağmen, bununla devam edebilirsiniz.

Ayrıca, bunun bahsettiğiniz Java'nın tasarım gereği çok az özelliğe sahip olduğunu da düşünmelisiniz. Sizi toStringsonunda çağırmaya zorlayan şey budur .
Örneğin, statik olarak da yazılmış olan C # alırsanız, o zaman bu çağrıyı örtük bir döküm için davranış tanımlayarak ihmal edebilirsiniz .
Objective-C gibi dinamik olarak yazılmış dillerde, gerçekten de çevrime ihtiyaç duymazsınız, çünkü değer bir dizge gibi davrandığı sürece herkes mutludur.

Birincisi, son çağrı yapılmasının toStringaslında Java'nın açıklık talebinin ürettiği gürültüden daha az bir çağrı olduğu söylenebilir . Herhangi bir Java nesnesinin sahip olduğu bir yöntemi çağırıyorsunuz , bu nedenle “uzak bir birim” hakkında hiçbir bilgiyi kodlamıyorsunuz ve böylece En Az Bilgi İlkesini ihlal etmiyorsunuz. Ne olursa olsun getLocation, bir toStringyöntemi yok, hiçbir yolu yoktur .

Fakat lütfen, en doğal tercih olmadıkça (ya da bir dil kullanmıyorsanız, hatta ... orada bile bulunmamışsa) dizeleri kullanmayın.


Sizinle aynı fikirde olmak isterdim, ancak zaten servis API'sini değiştiremediğini söyledi.
jhocking

1
jhocking: Yapamayacağını söylemedi. Pratik olmadığını söyledi. Katılmıyorum. API tasarımındaki hatalar etrafında çalışan koda en iyi uygulamaları uygulamaya çalışmak, yalnızca bu tür geçici çözümler mümkün olan tek seçenekse gerçekten mantıklı olur. Ancak burada API'yi düz ayarlamak en iyi seçenektir. Eksiklikleri gidermek, her zaman etraflarında çalışmaktan tercih edilir.
back2dos 21:11

Zaten +1 için "Java'nın
açıklık

1
Sadece "stringly yazılan" için bile bu + 1'i verirdim. Kodumda, mümkün olduğunca anlam / niyet ifade eden türleri geçmeye çalışıyorum, ancak üçüncü taraf kütüphaneleri ve API'lerle çalışırken, bazen söz konusu API'nin yazarlarının kararını kullanarak sıkışıp kalıyorsunuz. Ve IIRC, bir konum olabilir gerçekten "metinsel verilerin her türlü" olabilir, ama uygulanması için üzerinde çalışıyorum olmayan bir URL konumu tamamen anlamsızdır.
CVn

1
API'yi değiştirmek için ise; Bu bilgisayar yazılımı, bu yüzden bir şeyleri değiştirmek için hemen hemen her zaman mümkündür . (Başka hiçbir şey yapmazsanız, her zaman bir soyutlama katmanı yazabilirsiniz.) Ancak, bazen bunu yapmak hem kısa hem de uzun vadede haklı olunması için çok fazla çaba gerektirir. O zaman, bu tür değişiklikler yapmak tamamen mümkün olabilir, ancak yine de pratik değildir.
bir CVn

21

Demer'in kanunu, dini olarak izlenecek bir kanun değil, bir tasarım rehberidir.

Sınıflarınızın, hatta this.configuration.getLocation(), söylediğiniz gibi, API'nin diğer bölümlerini değiştirmenin pratik olmadığı durumlarda, satırda yanlış bir şey olmadığından yeterince ayrıldığını düşünüyorsanız.

Eğer Demeter Yasasını parçalara ayırsanız bile, istediğini ve zamanında teslim ettiğiniz sürece müşterinin tamamen mutlu olacağından eminim. Kötü yazılım yapmak için bir bahane değil, yazılım geliştirirken pragmatik olmak için bir hatırlatma.


1
Yana this.configurationince bile sıkı yorumuna göre bu arabirim tarafından görünüyor tanımlanır Üzerinde bir yöntemi çağırmak, bilinen bir arayüzünün bir türün örneği değişkendir. Evet, bunun KISS, SOLID, YAGNI vb. Gibi bir rehber olduğunu biliyorum. Genel yazılım geliştirmede gerçekten (yasal anlamda) herhangi bir "yasa" varsa çok az var.
bir CVn

4
Pragmatik olduğu için +1 ;-)
Treb

1
Demans Yasasının böyle bir durumda bile geçerli olması gerektiğini sanmıyorum - yapılandırma temel olarak bir konteynerdir. Konteynerlerin içine bakmakla çalıştığım her API'de normal ve beklenen davranış.
Loren Pechtel

2

Bu şekilde kod yazmamayı düşünebildiğim tek şey this.configuration.getLocation()null olursa ? Bu kodu kullanarak çevre kodunuza ve hedef kitlenize bağlıdır. Ama Marco'nun dediği gibi - Demer'in yasası bir kuraldır - takip etmek iyidir, ama gereksiz yere bunu yaparken sırtınızı kırmayın.


Eğer this.configuration.getLocation()boş döndürürsek, o zaman biz (a) büyük olasılıkla hiç bu kadar uzağa sahip olamazdık ya da (b) arada bir felaket yaşandı, bu durumda kodun başarısız olmasını isterdim . Dolayısıyla, bu genel olarak kesinlikle geçerli bir nokta olsa da, bu özel durumda, geçerli olmadığını söylemek oldukça güvenlidir. Ayrıca, tüm bunları ve daha fazlasını çevreleyen, bu tür beklenmeyen hatalarla başa çıkmak için özel olarak tasarlanmış bir istisna işleyicisidir.
bir CVn

2

Demeter Yasasını kesinlikle takip etmek, aşağıdaki gibi bir yapılandırma nesnesine bir yöntem uygulamanız gerektiği anlamına gelir:

function getLocationAsString() {
  return getLocation().toString();
}

ama kişisel olarak rahatsız olmazdım çünkü bu çok küçük bir durum ve zaten değiştiremediğiniz API yüzünden elleriniz biraz bağlı. Programlama kuralları, bir seçeneğiniz olduğunda yapmanız gerekenler hakkındadır, ancak bazen bir seçeneğiniz yoktur.


1
Burada önerilen aynı: c2.com/cgi/wiki?TrainWreck "İstenen davranışı temsil eden ve müşteriye ne yapması gerektiğini söyleyen bir yöntem oluşturun. Bu," söyle, sorma "ilkesini"
izler
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.