Net bir anlama sahip 2 yönteme mi yoksa sadece 1 ikili kullanım yöntemine mi sahip olmak daha iyidir?


30

Arayüzü basitleştirmek için, sadece getBalance()yönteme sahip olmamak daha mı iyi ? Geçme 0için charge(float c);aynı sonucu verecektir:

public class Client {
    private float bal;
    float getBalance() { return bal; }
    float charge(float c) {
        bal -= c;
        return bal;
    }
}

Belki bir not yazabilirim javadoc? Ya da dengeyi nasıl elde edeceğinizi öğrenmek için onu sadece sınıf kullanıcısına bırakın.


26
Bunun örnek, açıklayıcı kod olduğunu ve parasal değerleri saklamak için kayan nokta matematiği kullanmadığınızı farz ediyorum. Aksi halde tüm vaaz almam gerekirdi. :-)
corsiKa

1
@corsiKa nah. Bu sadece bir örnek. Ama, evet, gerçek bir sınıfta parayı temsil etmek için şamandıra kullanırdım ... Bana kayan nokta sayısının tehlikelerini hatırlattığın için teşekkürler.
david

10
Bazı diller, mutasyonerler ve erişimciler arasında iyi etkiye sahiptir. Sadece sabit olarak erişilebilen bir örneğin dengesini alamamak can sıkıcı olurdu!
JDługosz

Yanıtlar:


90

Bir ara yüzün karmaşıklığının, sahip olduğu elementlerin sayısıyla ölçülmekte olduğu anlaşılıyor gibi görünmektedir (bu durumda yöntemler). Birçoğu, chargeyöntemin Clientilave bir öğeye sahip olmaktan çok daha fazla karmaşıklık kattığını ekleyerek bir yöntemin dengesini döndürmek için kullanılabileceğini hatırlamak zorunda kalacağını savunuyordu getBalance. İşleri daha açık yapmak, özellikle arabirimdeki daha fazla öğe sayısına bakılmaksızın belirsizlik bırakmadığı noktalarda çok daha kolaydır.

Ayrıca, çağırmak , dakika başına WTF'ler olarak da bilinen (Temiz Koddan , aşağıdaki resimdeki) WTF'ler olarak da bilinen en az şaşkınlık ilkesinicharge(0) ihlal eder ; çağrının dengeyi sağlamak için kullanıldığını biliyorlar. Diğer okuyucuların nasıl tepki vereceğini düşünün:

görüntü tanımını buraya girin

Ayrıca, chargeyöntemin imzası, tek bir şey yapma ve komut sorgusu ayrıştırma kurallarına aykırıdır , çünkü nesnenin durumunu değiştirmesine ve aynı zamanda yeni bir değer döndürmesine neden olur.

Sonuçta, bu durumda en basit arayüzü olacağına inanıyorum:

public class Client {
  private float bal;
  float getBalance() { return bal; }
  void charge(float c) { bal -= c; }
}

25
Komut sorgusu ayırma ile ilgili nokta, IMO için son derece önemlidir. Bu projede yeni bir geliştirici olduğunuzu hayal edin. Kod üzerinden bakıldığında, bir getBalance()miktar değer döndüren ve hiçbir şeyi değiştirmediği hemen anlaşılıyor . Öte yandan, charge(0)muhtemelen bir şeyi değiştiriyor gibi görünüyor ... belki bir PropertyChanged olayı gönderir? Ve ne döndürüyor, önceki değer mi, yeni değer mi? Şimdi bunu daha net bir yöntem kullanarak kaçınılması mümkün olan doktora ve atık beyin gücüne bakmak zorundasınız.
Mage Xy 19

19
Ayrıca, kullanarak charge(0)hiçbir etkisi var olur, çünkü dengeyi almanın yolu olarak artık bu uygulama detaylara sizi evliler; Ücret sayısını takip etmenin alakalı hale geldiği bir gelecekteki gereksinimi kolayca hayal edebiliyorum , bu noktada kod tabanınızın gerçekten ücret almayan ücretlerle dolup taşması acı verici bir nokta haline geliyor. Müşterilerinizin yapması gereken şeyleri yapan, yalnızca müşterilerinizin yapması gereken şeyleri yapamayacak şeyleri ifade eden arayüz öğelerini sağlamalısınız .
Ben,

12
CQS uyarlaması mutlaka uygun değildir. Bir charge()yöntem için, eğer bu yöntem eşzamanlı bir sistemde atomikse, yeni veya eski değerin geri verilmesi uygun olabilir.
Dietrich Epp

3
CQRS için yaptığınız iki alıntıyı son derece ikna edici buldum. Mesela, genellikle Meyer'in fikirlerini severim, fakat bir şeyin değişip değişmediğini keyfi ve rahatsız edici olduğunu söyleyen bir fonksiyonun geri dönüş tipine bakıyorum. Birçok eşzamanlı veya değişmez veri yapısı, mutasyon + geri dönüşleri gerektirir. CQRS'yi ihlal etmeden nasıl bir String eklersiniz? Daha iyi alıntıların var mı?
user949300

6
Hemen hemen hiçbir kod Komut Sorgu Ayrımı için uygun değildir. Nesne yönelimli herhangi bir dilde herhangi bir deyimsel değildir ve şimdiye kadar kullandığım her dilin yerleşik API'leri tarafından ihlal edilir. Bunu "en iyi uygulama" olarak tanımlamak tuhaf görünüyor; Bu modeli izleyen bir yazılım projesini bile adlandırabilir misiniz?
Mark Amery,

28

IMO, değiştirilmesi getBalance()ile charge(0)uygulamanıza genelinde bir sadeleştirme değildir. Evet, daha az satırdır, ancak charge()sizin veya bir başkasının bu kodu tekrar gözden geçirmesi gerektiğinde potansiyel olarak hattın başını dönmesine neden olabilecek yöntemin anlamını gizler.

Aynı sonucu verseler de, bir hesap bakiyesini almak sıfır ücretle aynı değildir, bu nedenle endişelerinizi ayırmak en iyisi olacaktır. Örneğin charge(), bir hesap işlemi olduğunda oturum açmak için değişiklik yapmanız gerektiğinde , şimdi bir sorunla karşılaşırsınız ve işlevselliği yine de ayırmanız gerekir.


13

Kodunuzun kendi kendini belgelemesi gerektiğini hatırlamak önemlidir. Aradığımda charge(x), xtahsil edilmesini bekliyorum . Denge hakkında bilgi ikincildir. Dahası, onu charge()çağırdığımda nasıl uygulandığını bilmeyebilirim ve kesinlikle yarın nasıl uygulandığını bilmeyeceğim. Örneğin, gelecekteki bu potansiyel güncellemeyi aşağıdakilere göre düşünün charge():

float charge(float c) {
    lockDownUserAccountUntilChargeClears();
    bal -= c;
    Unlock();
    return bal;
}

Aniden charge()dengeyi almak için kullanmanın çok iyi görünmüyor.


Evet! Çok chargedaha ağır olan iyi bir nokta .
Deduplicator

2

Kullanılması charge(0);dengeyi elde etmek için kötü bir fikir: Bir gün biri fonksiyonunun diğer kullanım farkında olmadan ücretleri yapılıyor oturum açmak için bazı kod eklemek olabilir ve daha sonra her zaman birileri masraf olarak kaydedilir dengeyi alır. (Bunun gibi, şöyle bir şey söyleyen koşullu bir ifade gibi yollar vardır:

if (c > 0) {
    // make and log charge
}
return bal;

ancak bunlar, programcıya, onları uygulamak için gerekli olan derhal belli değilse yapamayacaklarını bilen programcıya güvenir.

Kısacası: Kullanıcılara veya programcı haleflerine güvenmeyin, charge(0);bu dengeyi sağlamanın doğru bir yol olduğunu fark edin ; mümkün.


0

Çok fazla cevap olduğunu biliyorum, ancak charge(0)bunun bir başka nedeni de , basit bir yazım kuralının, charge(9)dengesini almak istediğiniz her seferinde müşterinizin dengesinin düşmesine neden olmasıdır. İyi bir birim testiniz varsa, bu riski azaltabilirsiniz, ancak her aramada gayretli chargeolmamanız durumunda, bu yanlışlığı yaşayabilirsiniz.


-2

Daha az, çok amaçlı yöntemlere sahip olmanın mantıklı olacağı belirli bir durumdan bahsetmek isterim : Çok fazla polimorfizm varsa, yani bu arayüzün birçok uygulaması ; Özellikle, bu uygulamalar ayrı ayrı geliştirilen kodda ise, senkronize olarak güncellenemez (arayüz bir kütüphane tarafından tanımlanır).

Bu durumda, her bir uygulamanın yazılması işinin basitleştirilmesi, bunun kullanımın açıklığından çok daha değerlidir, çünkü birincisi, sözleşme ihlali hatalarından (iki yöntem birbiriyle tutarsızdır) kaçınır, ancak ikincisi, yalnızca getBalanceaçısından tanımlayan bir yardımcı işlev veya üst sınıf yöntemle geri kazanılabilir charge.

(Bu, belirli bir adı hatırlamadığım bir tasarım örüntüsüdür: minimal uygulayıcı dostu olan açısından karmaşık bir arayan dostu arayüz tanımlamak. Klasik Mac OS'de çizim işlemleri için minimal arayüz "darboğaz" olarak adlandırılmıştır. ama bu popüler bir terim gibi görünmüyor.)

Durum böyle değilse (az sayıda uygulama veya tam olarak bir tane varsa), netlik için yöntemleri ayırmak ve sıfır olmayan yüklerle ilgili basit bir davranış eklenmesine izin vermek charge()anlamlıdır.


1
Aslında, eksiksiz kapsama testini kolaylaştırmak için daha az arabirimin seçildiği durumlar elde ettim. Ancak bu mutlaka sınıfın kullanıcısına maruz kalmaz. Örneğin insert , ekleme ve silme kutu hepsi bir general bir ambalaj olmak yerine tüm kontrol yolları iyi çalışılmış ve test olan.
JDługosz

Böyle bir API gördüm. Lua 5.1+ tahsisatçısı bunu kullanıyor. Bir işlev sağlarsınız ve geçtiği parametrelere dayanarak yeni bellek ayırmaya, belleği ayırmaya mı yoksa eski bellekten yeniden ayırmaya mı karar verirsiniz . Bu tür fonksiyonlar yazmak acı vericidir . Daha doğrusu Lua'nın size iki işlev vermişti, biri tahsisat için diğeri de tahsisat için.
Nicol Bolas

@ NicoBolas: Ve yeniden tahsisat için yok? Her neyse, bu kişi reallocbazı sistemlerde neredeyse aynı olmanın "yararını" ekledi .
Deduplicator

@Deduplicator: tahsis vs yeniden tahsisat ... Tamam. Onları aynı işleve koymak harika değil, ama aynı işleve yerleştirme yapmak kadar kötü değil. Aynı zamanda yapılması kolay bir ayrım.
Nicol Bolas

Tahsisatın yeniden tahsis ile (yeniden) tahsis edilmesi, bu tür bir arayüzün özellikle kötü bir örneğidir. (Deallocation çok farklı bir sözleşme vardır: yok değil geçerli bir pointer ile sonuçlanabilir.)
Kevin Reid
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.