Aşırı somut yöntemler kod kokusu mu?


31

Somut yöntemlerin geçersiz kılınmasının bir kod kokusu olduğu doğru mu? Çünkü somut yöntemleri geçersiz kılmanız gerekiyorsa bence:

public class A{
    public void a(){
    }
}

public class B extends A{
    @Override
    public void a(){
    }
}

olarak yeniden yazılabilir

public interface A{
    public void a();
}

public class ConcreteA implements A{
    public void a();
}

public class B implements A{
    public void a(){
    }
}

ve eğer B A'daki () işaretini tekrar kullanmak istiyorsa, şu şekilde yeniden yazılabilir:

public class B implements A{
    public ConcreteA concreteA;
    public void a(){
        concreteA.a();
    }
}

Bu yöntem geçersiz kılmak için miras gerektirmeyen, bu doğru mu?



4
Aşağı indirildi, çünkü (aşağıdaki yorumlarda), Telastyn ve Doc Brown geçersiz kılmayı kabul ediyorlar ancak başlık koduna evet / hayır cevabını verdiler çünkü "kod kokusu" anlamını anlamadılar. Dolayısıyla, kod tasarımı ile ilgili olması amaçlanan bir soru, bir argo parçasına yüksek oranda bağlanmıştır. Ve gereksiz yere öyle düşünüyorum, çünkü soru özellikle bir başkasına karşı bir teknikle ilgilidir.
Steve Jessop

5
Soru Java etiketli değil, ancak kod Java gibi görünüyor. C # (veya C ++) 'ta, virtualgeçersiz kılmaları desteklemek için anahtar kelimeyi kullanmanız gerekir . Bu yüzden konu, dilin özellikleri tarafından daha fazla (ya da daha az) belirsiz hale getirildi.
Erik Eidt

1
Neredeyse her programlama dili özelliği kaçınılmaz olarak, yeterli yıllar sonra "kod kokusu" olarak sınıflandırılmaya başlıyor gibi görünüyor.
Brandon

2
finalDeğiştiricinin bu gibi durumlar için tam olarak var olduğunu hissediyorum . Yöntemin davranışı geçersiz kılmak için tasarlanmamışsa, o zaman işaretleyin final. Aksi takdirde, geliştiricilerin geçersiz kılmayı seçebileceklerini bekleyin.
Martin Tuskevicius

Yanıtlar:


34

Hayır, kod kokusu değil.

  • Bir sınıf nihai değilse, alt sınıflamaya izin verir.
  • Bir yöntem nihai değilse, geçersiz kılınmasına izin verir.

Alt sınıflamanın uygun olup olmadığını ve hangi metotların geçersiz kılınabileceğini dikkatlice değerlendirmek her sınıfın sorumlulukları dahilindedir .

Sınıf, kendisini veya herhangi bir yöntemi nihai olarak tanımlayabilir veya alt sınıflamanın nasıl ve nerede olduğu konusunda kısıtlamalar (görünürlük değiştiricileri, kullanılabilir yapıcılar) yerleştirebilir.

Geçersiz kılma yöntemleri için genel durum , alt sınıfta özelleştirilebilecek veya optimize edilebilecek temel sınıftaki varsayılan bir uygulamadır (özellikle arayüzlerde varsayılan yöntemlerin ortaya çıkmasıyla birlikte Java 8'de).

class A {
    public String getDescription(Element e) {
        // return default description for element
    }
}

class B extends A {
    public String getDescription(Element e) {
        // return customized description for element
    }
}

Davranışı geçersiz kılmanın bir alternatifi , davranışın bir arabirim olarak soyutlandığı Strateji Kalıbıdır ve uygulama sınıfta ayarlanabilir.

interface DescriptionProvider {
    String getDescription(Element e);
}

class A {
    private DescriptionProvider provider=new DefaultDescriptionProvider();

    public final String getDescription(Element e) {
       return provider.getDescription(e);
    }

    public final void setDescriptionProvider(@NotNull DescriptionProvider provider) {
        this.provider=provider;
    }
}

class B extends A {
    public B() {
        setDescriptionProvider(new CustomDescriptionProvider());
    }
}

Gerçek dünyada somut bir yöntemi geçersiz kılmanın bazılarına kod kokusu gelebileceğini biliyorum. Ancak, bu cevabı sevdim çünkü bana göre daha spesifik görünüyor.
Darkwater23

Son örnekte, Auygulamamalı DescriptionProvidermıyım?
Benekli

@Spotted: A verebilir uygulamak DescriptionProviderve böylece varsayılan açıklama sağlayıcısı. Ancak buradaki düşünce kaygıların ayrılmasıdır: Aaçıklamaya ihtiyaç duyuyor (diğer sınıflar da olabilir), ancak diğer uygulamalar bunları sağlar.
Peter Walser

2
Tamam, daha çok strateji modeline benziyor .
Benekli

@Spotted: haklısın, örnek, dekoratör kalıbını değil, bir dekoratör kalıbını (bir bileşene davranış ekleyeceğini) gösteren strateji modelini gösterir. Cevabımı düzeltirim, teşekkürler.
Peter Walser

25

Somut yöntemlerin geçersiz kılınmasının bir kod kokusu olduğu doğru mu?

Evet, genel olarak, somut yöntemlerin geçersiz kılınması bir kod kokudur.

Temel yöntemin, geliştiricilerin genellikle saygı duyduğu bir davranışa sahip olması nedeniyle, uygulamanız farklı bir şey yaptığında değişiklik yapılmasına neden olacak değişiklik yapılması. Daha kötüsü, davranışı değiştirirlerse, önceden doğru uygulamanız sorunu daha da kötüleştirebilir. Ve genel olarak, önemsiz olmayan somut yöntemler için Liskov Değiştirme İlkesine saygı duymak oldukça zordur .

Şimdi, bunun yapılabileceği ve iyi olduğu durumlar var. Yarı önemsiz yöntemler biraz güvenli bir şekilde kullanılamaz. Sıralama olduğu davranışın sadece bir "aklı başında varsayılan" olarak var Baz yöntemler geliyordu -basmış bitmesini iyi kullanan var.

Bu yüzden, bu bana bir koku gibi geliyor - bazen iyi, genellikle kötü, emin olmak için bir göz atın.


29
Açıklamanız iyi, ama ben tam tersi bir sonuca vardım: "Hayır, geçersiz kılma yöntemleri genel olarak bir kod kokusu değil " - yalnızca geçersiz kılınması amaçlanmayan yöntemleri geçersiz kıldığı zaman bir kod kokusudur. ;-)
Doc Brown

2
@DocBrown Kesinlikle! Ayrıca, açıklamanın (ve özellikle sonuç) ilk ifadeye karşı çıktığını da görüyorum.
Tersosauros

1
@Spotted: En basit counterexample, değeri bir sonraki geçerli değerine ayarlayan Numberbir increment()yöntemi olan bir sınıftır . Eğer içine alt sınıf ise EvenNumbernerede increment()iki yerine tek ekler (ve ayarlayıcı onu yürütmelidir bir homojenlik), OP ilk teklif yinelenen uygulamaları gerektirmektedir her sınıfta yöntemi ve onun ikinci üyesi yöntemleri çağırmak için yazma sarıcı yöntemleri gerektirir. Kalıtımın bir parçası da, çocuk sınıflarının yalnızca farklı olması gereken yöntemleri sağlayarak ebeveynlerden nasıl farklı olduklarını netleştirmeleridir.
Blrfl

5
@DocBrown: Bir kod kokusu olmak bir şeylerin mutlaka kötü olduğu anlamına gelmez , sadece olması muhtemeldir .
mikołak

3
@docbrown - benim için, bu "miras üzerindeki iyilik kompozisyonu" ile birlikte düşüyor. Aşırı somut yöntemler kullanıyorsanız, kalıtımsallığınız için zaten küçük kokulu bir yerdesiniz. Olmaması gereken bir şeye fazla binme ihtimaliniz nedir? Tecrübelerime dayanarak, bunun muhtemel olduğunu düşünüyorum. YMMV.
Telastyn

7

somut yöntemleri geçersiz kılmanız gerekirse [...] olarak yeniden yazılabilir.

Bu şekilde yeniden yazılabilirse, her iki sınıf üzerinde kontrolünüz olduğu anlamına gelir. Ama sonra A sınıfını bu şekilde türetilmek üzere tasarlayıp tasarlamadığınızı biliyorsunuz ve eğer öyleyse, bu bir kod kokusu değil.

Öte yandan, temel sınıf üzerinde kontrolünüz yoksa, bu şekilde yeniden yazamazsınız. Bu yüzden sınıf ya bu şekilde türetilmek üzere tasarlandı, bu durumda sadece devam edin, kod kokusu değil ya da olmadı, bu durumda iki seçeneğiniz var: tamamen başka bir çözüm arayın ya da yine de devam edin Çünkü çalışma kozları tasarım saflığıdır.


A sınıfı türetilmek üzere tasarlanmış olsaydı, amacı açıkça ifade etmek için soyut bir yönteme sahip olmak daha mantıklı olmaz mıydı? Yöntemi geçersiz kılan, yöntemin bu şekilde tasarlandığını nasıl bilebilir?
Benekli

@Spotted, varsayılan uygulamaların sağlanabileceği birçok durum vardır ve her uzmanlık sadece işlevselliği genişletmek veya verimlilik için bazılarını geçersiz kılmaya ihtiyaç duyar. Aşırı örnek ziyaretçilerdir. Kullanıcı, yöntemlerin dokümantasyondan nasıl tasarlandığını bilir; geçersiz kılmadan beklenenin ne olduğunu açıklamak için orada bulunmalıdır.
Jan Hudec

Sizin bakış açınızı anlıyorum, hala bir geliştiricinin sahip olduğu tek yöntemin geçersiz kılınabileceğini bilmek için dokümanı okumaktan dolayı tehlikeli olduğunu düşünüyorum çünkü: 1) geçersiz kılınması gerekiyorsa, tamamlanacak. 2) geçersiz kılılmaması gerekiyorsa, birileri kolaylık olması için bunu yapacak 3) herkes dokümanı okumaz. Ayrıca, hiçbir zaman bütün bir yöntemi geçersiz kılmam gereken bir durumla karşı karşıya gelmedim, ancak yalnızca parçalar (ve bir şablon deseni kullanarak sona erdi).
Benekli

@Spotted, 1), eğer gerekiyor overriden, olması gerektiği abstract; ama olması gerekmeyen birçok durum var. O gerekiyorsa 2) değil geçersiz kılınan olmak, olması gerektiği final(sanal olmayan). Fakat yöntemler birçok durumla karşılaşabiliyoruz olabilir geçersiz kılınabilir. 3) alt taraftaki geliştirici dokümanları okumazsa, kendilerini ayağından vururlar, ancak ne gibi önlemler almış olursanız olun bunu yapacaklardır.
Jan Hudec

Tamam, bizim bakış açımızın sapmaları sadece 1 kelimeyle yatmaktadır. Her yöntem olduğunu söylemek gerekir her yöntem söylerken soyut veya nihai olması gerekir soyut veya nihai olabilir. :) Bir yöntemin geçersiz kılınması gerektiğinde bu durumlar nelerdir (ve güvenlidir)?
Benekli

3

İlk önce, bu sorunun sadece belirli tipleme sistemlerinde geçerli olduğunu belirteyim. Örneğin, yapısal yazarak ve Ördek yazarak sistemleri - Bir sınıf basitçe sahip aynı adı & sahip bir yöntem imzası olur anlamına bu sınıf tür uyumlu (ördek yazma durumunda, söz konusu yöntem için en az).

Bu (açıkçası) , Java, C ++ gibi statik olarak yazılmış diller alanından çok farklıdır . . Hem C # hem de diğerlerinde olduğu gibi hem Java, hem de C ++ 'da kullanıldığı gibi Güçlü yazmanın .


Sana yöntemler açıkça gereken bir Java arka plan, geliyorlar tahmin ediyorum nihai olarak işaretlenmiş ise sözleşme tasarımcı onları için niyetinde değildir geçersiz etmek. Ancak, C # gibi dillerde, bunun tersi varsayılandır. Yöntemler (herhangi bir dekorasyon, özel anahtar kelimeler olmadan) " mühürlü " (C # sürümü final) ve açıkça açıkça belirtilmelidirvirtual onlar eğer izin geçersiz kılınan olmak.

Bir API ya da kütüphane somut yöntemi ile bu dillerde dizayn edilmişse olduğunu geçersiz kılınabilir, o zaman (en azından gerekir bazı marka anlamda durum (lar)) o geçersiz kılınan olmak için. Bu nedenle, açık / sanal / finalsomut olmayan bir yöntemi geçersiz kılmak bir kod kokusu değildir .     (Bu tabii ki, API tasarımcılarının kuralları takip ettiğini ve ya virtual(C #) olarak işaretlediğini ya da final(Java) olarak tasarladıkları için uygun yöntemleri işaretlediğini varsaymaktadır .)


Ayrıca bakınız


Ördek tiplendirmede, aynı yöntem imzasına sahip olan iki sınıf, tip uyumlu olmaları için yeterli midir, yoksa bazı ima edilen temel sınıflar için ikame edilebilecekleri şekilde, aynı semantiklere sahip olmaları da gerekli midir?
Wayne Conrad,

2

Evet, çünkü süper anti-patern çağırmaya yol açabilir . Ayrıca, yöntemin başlangıç ​​amacını denatüre edebilir, yan etkiler yaratabilir (=> hatalar) ve testlerin başarısız olmasına neden olabilir. Hangi ünite testlerinin kırmızı olduğunu gösteren bir sınıf kullanır mısınız? Yapmam.

Ben söyleyerek daha da ileri gideceğim, ilk kod kokusu bir yöntem olmadığı zaman abstract nefinal . Yapılı bir varlığın davranışını değiştirmeyi (geçersiz kılmayı) sağladığı için (bu davranış kaybolabilir veya değiştirilebilir). İle abstractvefinal niyet belli değil:

  • Özet: gerekir bir davranış sağlamak
  • Final: sen değil davranışını değiştirmek için izin

Mevcut bir sınıfa davranış eklemenin en güvenli yolu, son örneğinizin gösterdiği şeydir: dekoratör desenini kullanmak.

public interface A{
    void a();
}

public final class ConcreteA implements A{
    public void a() {
        ...
    }
}

public final class B implements A{
    private final ConcreteA concreteA;
    public void a(){
        //Additionnal behaviour
        concreteA.a();
    }
}

9
Bu siyah-beyaz yaklaşım gerçekten o kadar da kullanışlı değil. Object.toString()Java'da düşünün ... Bu abstractolsaydı, birçok varsayılan şey işe yaramazdı ve birisi toString()yöntemi geçersiz kılmadığında bile kod derlenmezdi ! Öyle finalolsaydı, işler daha da kötüye giderdi - Java yolu hariç nesnelerin dizeye dönüştürülmesine izin vermemek. As @Peter Walser hakkında 'ın cevabı görüşmelerinde, varsayılan uygulamalar (örneğin Object.toString()) olan önemli. Özellikle Java 8'de varsayılan yöntemler.
Tersosauros

@Tersosauros Haklısınız toString(), ya abstractda finalolsaydı ya da bir acı olurdu. Benim kişisel görüşüm, bu yöntemin bozulduğu ve hiç olmaması ( eşittir ve hashCode gibi ) olması daha iyi olurdu . Java varsayılanlarının ilk amacı, geriye dönük uyumlulukla API gelişimini sağlamaktır.
Benekli

"Uyumluluk" kelimesi çok fazla heceye sahiptir.
Craig

@Spotted Bu sitedeki somut mirasın genel olarak kabul edilmesinden çok hayal kırıklığına uğradım, daha iyisini beklerdim: / Yanıtınız çok daha fazla oy almalı
TheCatWhisperer

1
@ TheCatWhisperer aynı fikirdeyim, ancak diğer yandan, beni mirastan (yani ilk önce test yazmaya başladığımda netleşti) üzerine beton mirasa karşı dekorasyon kullanmanın ince avantajlarını bulmam çok uzun sürdü. . Yapılacak en iyi şey, Gerçeği (şaka) keşfedene kadar diğerlerini eğitmeye çalışmaktır . :)
Benekli
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.