Zaten soyut bir sınıfım varsa bir arayüz tanımlamak anlamlı mı?


12

Bazı varsayılan / paylaşılan işlevselliği olan bir sınıf var. Bunun için kullanıyorum abstract class:

public interface ITypeNameMapper
{
    string Map(TypeDefinition typeDefinition);
}

public abstract class TypeNameMapper : ITypeNameMapper
{
    public virtual string Map(TypeDefinition typeDefinition)
    {
        if (typeDefinition is ClassDefinition classDefinition)
        {
            return Map(classDefinition);
        }
        ...

        throw new ArgumentOutOfRangeException(nameof(typeDefinition));
    }

    protected abstract string Map(ClassDefinition classDefinition);
}

Gördüğünüz gibi arayüz de var ITypeNameMapper. Zaten soyut bir sınıfım varsa TypeNameMapperveya abstract classyeterliyse bu arayüzü tanımlamak mantıklı mıdır?

TypeDefinition bu minimal örnekte de soyuttur.


4
Sadece FYI - OOD'da, koddaki türleri denetlemek sınıf hiyerarşinizin doğru olmadığını gösterir - denetlediğiniz türden türetilmiş sınıflar oluşturmanızı söyler. Bu durumda, bir Map () yöntemi belirten bir arabirim ITypeMapper istiyorsunuz ve bu arabirimi hem TypeDefinition hem de ClassDefinition sınıflarına uygulayabilirsiniz. Bu şekilde, ClassAssembler'ınız ITypeMappers listesi üzerinden yinelenir, her birinde Map () öğesini çağırır ve her birinin kendi uygulaması olduğu için her iki türden de istenen dizeyi alır.
Rodney P. Barbati

1
Arabirim yerine temel sınıf kullanmaya kesinlikle gerekmedikçe "tabanı yakmak" denir. Bir arayüz ile yapılabilirse, bir arayüz ile yapın. Soyut sınıf daha sonra yardımcı bir ekstra haline gelir. artima.com/intv/dotnet.html size remoted istiyorsanız Özellikle, Remoting böylece MarshalByRefObject türetmek gerektirir olamaz şeyden kaynaklanıyor.
Ben

@ RodneyP.Barbati, duruma bağlı olarak değiştirebileceğim aynı tür için çok sayıda haritacıya sahip olmak istiyorsam çalışmaz
Konrad

1
@Ben ilginç üssü yakıyor. Mücevher için teşekkürler!
Konrad

@Konrad Bu yanlıştır - arabirimin başka bir uygulamasını çağırarak arabirimi uygulamanızı engelleyen hiçbir şey yoktur, bu nedenle, her tür, türdeki uygulama aracılığıyla açığa çıkardığınız takılabilir bir uygulamaya sahip olabilir.
Rodney P. Barbati

Yanıtlar:


31

Evet, çünkü C # arabirimler dışında birden çok kalıtıma izin vermez.

Yani hem TypeNameMapper hem de SomethingelseMapper olan bir sınıf varsa yapabilirim:

class MultiFunctionalClass : ITypeNameMapper, ISomethingelseMapper 
{
    private TypeNameMapper map1
    private SomethingelseMapper map2

    public string Map(TypeDefinition typeDefinition) { return map1.Map(typeDefintion);}

    public string Map(OtherDef otherDef) { return map2.Map(orderDef); }
}

4
@ Liath: Tek sorumluluk aslında mirasa neden ihtiyaç duyulduğuna katkıda bulunan bir faktördür. Üç olası özellikleri göz önünde bulundurun: ICanFly, ICanRun, ICanSwim. Her özellik kombinasyonu için bir tane olmak üzere 8 yaratık sınıfı oluşturmanız gerekir (YYY, YYN, YNY, ..., NNY, NNN). SRP, her davranışı (uç, koş, yüz) bir kez uygulamanı ve daha sonra bunları 8 yaratık üzerinde tekrar tekrar uygulamanı belirler. Kompozisyon burada daha da iyi olurdu, ancak kompozisyonu bir saniyeliğine göz ardı ederek, SRP'ye bağlı birden fazla ayrı yeniden kullanılabilir davranışın miras alınması / uygulanması gerektiğini zaten görmelisiniz .
flater

4
@Konrad demek istediğim bu. Arayüzü yazarsanız ve asla ihtiyacınız yoksa, maliyet 3 LoC'dir. Arayüzü yazmaz ve ihtiyacınız olduğunu fark ederseniz, maliyet tüm uygulamanızı yeniden düzenliyor olabilir .
Ewan

3
(1) Arabirimlerin uygulanması kalıtım mıdır? (2) Her iki soru ve cevap da nitelikli olmalıdır. "Değişir." (3) Birden fazla mirasın üzerinde bir uyarı etiketine ihtiyacı vardır. (4) Size kraptacular formal interfacemalpraktis K'ları gösterebilirim ... tabanda olması gereken - bağımsız olarak gelişen gereksiz uygulamalar !, şablon yöntemlerini, kırık kapsüllemeyi, var olmayan soyutlamayı istemek için bu önemsizliği yönlendiren koşullu mantık değişiklikleri. En sevdiğim: Her interfacebiri tek bir örnek içeren kendi 1 yöntemini uygulayan 1 yöntemli sınıflar . ... vb, vb.
radarbob

3
Kodda görünmez bir sürtünme katsayısı vardır. Gereksiz arayüzleri okumak zorunda kaldığınızda bunu hissedersiniz. Sonra bir arayüz uygulaması soyut sınıf aylar gibi atmosfere çarparak uzay mekiği gibi kendini ısınma gösterir. Bu Alacakaranlık Bölgesi ve bu servis Challenger. Kaderimi kabul ederek, öfkeli plazmayı korkusuz, müstakil bir merakla izliyorum. Kayıtsız sürtünme mükemmellik cephesini parçalayarak, parçalanmış gövdeyi gök gürültülü bir boink ile üretime bırakıyor.
radarbob

4
@radarbob Şiirsel. ^ _ ^ Kabul ediyorum; arayüzlerin maliyeti düşük olmakla birlikte, mevcut değildir. Onları yazarken çok fazla değil, ancak bir hata ayıklama durumunda, gerçek düzeye ulaşmak için 1-2 adım daha var, bu da yarım düzine soyutlama seviyesine ulaştığınızda hızlı bir şekilde toplanabilir. Bir kütüphanede genellikle buna değer. Ancak bir uygulamada, yeniden düzenleme erken genellemeden daha iyidir.
Errorsatz

1

Arayüzler ve soyut sınıflar farklı amaçlara hizmet eder:

  • Arayüzler API'leri tanımlar ve uygulamalara değil istemcilere aittir.
  • Sınıflar uygulamaları paylaşıyorsa, soyut bir sınıftan yararlanabilirsiniz .

Örneğin, interface ITypeNameMappermüşterilerin ihtiyaçlarını tanımlar ve abstract class TypeNameMapperherhangi bir değer katmaz.


2
Soyut sınıflar da müşterilere aittir. Bununla ne demek istediğini bilmiyorum. publicMüşteriye ait her şey . TypeNameMapperçok fazla değer katıyor çünkü uygulayıcıyı ortak bir mantık yazmaya karşı koruyor.
Konrad

2
Bir arabirimin bir olarak sunulup sunulmayacağı interfaceyalnızca C # ile tam olarak alakalı olup, birden fazla kalıtımın tamamen yasaklanmasını sağlar.
Deduplicator

0

Tüm arayüz kavramı, paylaşılan bir API'ya sahip bir sınıf ailesini desteklemek için oluşturuldu.

Bu açıkça bir arayüzün herhangi bir kullanımının, spesifikasyonunun birden fazla uygulamasının olduğunu (veya olması beklenen) ima ettiğini söylüyor.

DI çerçeveleri, burada sadece bir uygulama olmasına rağmen birçoğunun bir arayüze ihtiyaç duyduğu için suları karıştırdı - bana göre bu, çoğu durumda yeni çağırmanın daha karmaşık ve yavaş bir yolu için mantıksız bir yüktür, ancak ne olduğu.

Cevap sorunun kendisinde yatıyor. Soyut bir sınıfınız varsa, ortak bir API ile birden fazla türetilmiş sınıf oluşturmaya hazırlanırsınız. Böylece, bir arayüzün kullanımı açıkça belirtilmiştir.


2
"Soyut bir sınıfınız varsa, ... bir arayüzün kullanımı açıkça belirtilir." - Sonucum tam tersidir: Soyut bir sınıfınız varsa, o zaman bir arayüzeymiş gibi kullanın (DI onunla oynamıyorsa, farklı bir uygulamayı düşünün). Sadece gerçekten çalışmadığında, arayüzü oluşturun - daha sonra istediğiniz zaman yapabilirsiniz.
maaartinus

1
Aynen, @maaartinus. Ve hatta "... sanki bir arayüz gibiydi." Bir sınıfın genel üyeleri olan bir arayüz. Ayrıca "daha sonra istediğiniz zaman yapabilirsiniz" için ditto; Bu tasarım temelleri 101 kalbine gider.
radarbob

@maaartinus: Başlangıçtan itibaren bir arayüz oluşturuyor, ancak her yerde soyut sınıf türünün referanslarını kullanıyorsa, arayüzün varlığı hiçbir şeye yardımcı olmaz. Ancak, istemci kodu mümkün olduğunda soyut sınıf türü yerine arabirim türünü kullanırsa, soyut sınıftan dahili olarak çok farklı bir uygulama üretilmesi gerektiğinde işleri büyük ölçüde kolaylaştıracaktır. Bu noktada arabirimi kullanmak için istemci kodunu değiştirmek, başlangıçtan itibaren bunu yapmak için istemci kodu tasarlamaktan çok daha acı verici olacaktır.
supercat

1
@supercat Bir kütüphane yazdığınızı kabul edebilirim. Bir uygulama ise, tüm oluşumları bir kerede değiştirirken herhangi bir sorun göremiyorum. Tutulmam bunu yapabilir (Java, C # değil), ancak yapamazsa, tıpkı find ... -exec perl -pi -e ...önemsiz derleme hatalarını düzeltmek gibi . Demek istediğim: (Şu anda) işe yaramaz bir şey olmamasının avantajı, gelecekteki olası tasarruflardan çok daha büyük.
maaartinus

interfaceKod olarak işaretlediyseniz ( interfaceherhangi bir sınıf / işlev / vb. Kümesi gibi soyut bir arabirimden değil, C # - hakkında konuşuyorsanız ) ve bitirdiğinizde ilk cümle doğru olur "ve yine de tam çoklu yasadışı kalıtım." interfaceMI'nız varsa s'ye gerek yoktur .
Deduplicator

0

Soruyu açıkça cevaplamak istiyorsak, yazar "Eğer zaten bir soyut sınıf TypeNameMapper veya soyut sınıf yeterli ise bu arayüzü tanımlamak mantıklı mı?"

Cevap evet ve hayır - Evet, zaten soyut temel sınıfınız olsa bile arayüzü oluşturmalısınız (çünkü herhangi bir istemci kodunda soyut temel sınıftan bahsetmemelisiniz) ve hayır, çünkü oluşturmamalısınız bir arabirimin yokluğunda soyut temel sınıf.

Oluşturmaya çalıştığınız API hakkında yeterince düşünmediğiniz açıktır. Önce API ile gelin, ardından istenirse kısmi bir uygulama sağlayın. Soyut temel sınıfınızın kod yazımı denetimi yapması, doğru soyutlama olmadığını söylemek için yeterlidir.

OOD'daki çoğu şeyde olduğu gibi, oluktayken ve nesne modelinizi iyi yaptığınızda, kodunuz bir sonraki adımla ilgili olarak sizi bilgilendirir. Bir arayüz ile başlayın, bu arayüzü ihtiyacınız olan sınıflara uygulayın. Kendinize benzer bir kod yazarken bulursanız, bunu soyut bir temel sınıfa çıkarın - önemli olan arayüzdür, soyut temel sınıf sadece bir yardımcıdır ve birden fazla olabilir.


-1

Bu uygulamanın geri kalanını bilmeden cevaplamak oldukça zordur.

Bir çeşit DI modeli kullandığınızı ve kodunuzu olabildiğince genişletilebilir olarak tasarladığınızı varsayarsak ( TypeNameMapperkolayca yeni ekleyebilirsiniz ) evet diyebilirim.

Sebepleri:

  • Temel sınıf uyguladığı için etkili bir şekilde ücretsiz olarak alırsınız interface, herhangi bir çocuk uygulamasında bunun için endişelenmenize gerek yoktur.
  • Çoğu IoC çerçevesi ve Alay kütüphanesi interfaces. Birçoğu soyut sınıflarla çalışırken, her zaman verilen bir şey değildir ve mümkünse bir kütüphane kullanıcılarının diğer% 99'uyla aynı yolu izlemeye inanıyorum.

Sebepler:

  • Kesin olarak (işaret ettiğiniz gibi) GERÇEKTEN yapmanız gerekmiyor
  • Eğer class/ interfacedeğiştirir biraz ek yükü var

Her şeyi düşündüğümü yaratmanız gerektiğini söyleyebilirim interface. Sebepleri oldukça ihmal edilebilir, ancak alay ve IoC'de özetleri kullanmak mümkün olsa da, arayüzlerle genellikle çok daha kolaydır. Nihayetinde, başka bir yoldan giderse bir meslektaşının kodunu eleştirmezdim.

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.