Java'da birden fazla genel arayüzün uygulanması


10

Bana özel imza da dahil olmak üzere belirli bir yöntemi temin eden bir arayüze ihtiyacım var. Şimdiye kadar sahip olduğum şey:

public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

Sorun, bir sınıfın diğer birden çok varlıkla eşleştirilebilmesi gerektiğinde ortaya çıkar. İdeal durum şudur (java değil):

public class Something implements Mappable<A>, Mappable<B> {
    public A mapTo(A someObject) {...}
    public B mapTo(B someOtherObject) {...}
}

Mümkün olduğunca "jenerik" kalmayı başarmanın en iyi yolu ne olurdu?

Yanıtlar:


10

Bu, elbette, Tip Silme nedeniyle yapabileceğiniz bir şey değildir . Çalışma zamanında, iki yöntem vardır public Object mapTo(Object)besbelli arada bulunamaz.

Ne yazık ki, yapmaya çalıştığınız şey Java'nın tür sisteminin ötesinde.

Genel türünüzün her zaman birinci sınıf bir tür olduğunu varsayarsak, kendisi de genel değildir mapTo(Object, Class), belirli bir sınıfın çalışma zamanı denetimini gerçekleştirmenize ve hangi davranışın kullanılacağına karar vermenize olanak tanıyan yönteme sahip olarak benzer dışa dönük davranışlar elde edebilirsiniz . Açıkçası bu oldukça yetersiz - ve dönüş değerinin manuel dökümünü gerektirecek - ama yapabileceğiniz en iyi şey olduğunu düşünüyorum. Genel türleriniz kendileri genelse, genel parametreleri de silinir ve Sınıfları eşit olur, bu nedenle bu yöntem çalışmaz.

Ancak, @ Joachim'in cevabına da işaret ederdim, bu, davranışı ayrı bileşenlere ayırabileceğiniz ve tüm sorunu ortadan kaldırabileceğiniz bir durum olabilir.


3

Gördüğünüz gibi, aynı arabirimi farklı tür parametreleriyle iki kez uygulayamazsınız (silme nedeniyle: çalışma zamanında aynı arabirimlerdir).

Ayrıca, bu yaklaşım tek sorumluluk ilkesini ihlal eder: sınıfınız Something(ne anlama geliyorsa) olmaya odaklanmalı ve bu göreve Aveya buna B ek olarak eşleme yapmamalıdır .

Gerçekten a Mapper<Something,A>ve a olması gerektiği gibi geliyor Mapper<Something,B>. Bu şekilde, her sınıfın açıkça tanımlanmış tek bir sorumluluğu vardır ve aynı arabirimi iki kez uygulama sorunuyla karşılaşmazsınız.


Fikir, sınıfın içeriğini diğer nesnelere "dönüştürmekten" sorumlu olmasını sağlamaktır. Sonra onları Sınıf agnostik bir şekilde idare eden bir dağıtıcı var, bu nedenle jenerikler gereksinimi. Mantık ayıklama hakkında biraz düşüneceğim, ancak bu aslında sınıfı ikiye böldüğüm anlamına geliyor, ancak sıkıca bağlı kalıyorlar (alana erişim izni verilmeli, çoğu zaman diğerini değiştirmeyi ima ediyor, vb.)
estani

@estani: evet, bir şekilde sıkı sıkıya bağlılar, ama farklı sorumlulukları var. Ayrıca şunu da düşünün: yeni bir sınıf tanıttığınızda Cve Somethingbununla eşlenebilir olmak istediğinizde, değiştirmeniz gerekir Something, bu da çok fazla eşleşmedir. Sadece yeni bir SoemthingToCMapperşey eklemek daha az müdahaleci.
Joachim Sauer

1
Buna +1 - genel olarak konuşursak, OP'nin istediklerini (Java'da) elde etmeye çalışıyorsanız, miras yerine kompozisyonu tercih etmelisiniz. Varsayılan yöntemlere sahip Java 8 bunu daha da kolaylaştırır - ancak herkes henüz kanama kenarına atlayamaz :-).
Martijn Verburg

0

Çoklu arayüzler uygulamasına izin verilmediği göz önüne alındığında, kapsülleme kullanımını düşünebilirsiniz. (java8 + kullanan örnek)

// Mappable.java
public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

// TwoMappables.java
public interface TwoMappables {
    default Mappable<A> mapableA() {
         return new MappableA();
    }

    default Mappable<B> mapableB() {
         return new MappableB();
    }

    class MappableA implements Mappable<A> {}
    class MappableB implements Mappable<B> {}
}

// Something.java
public class Something implements TwoMappables {
    // ... business logic ...
    mapableA().mapTo(A);
    mapableB().mapTo(B);
}

Daha fazla bilgi ve daha fazla örnek için lütfen buraya bakın: İki jenerik tipte bir arayüz uygulayan bir Java sınıfı nasıl yapılır? .


-1
public interface IMappable<S, T> {
    T MapFrom(S source);
}

// T - target
// S - source

Kullanıcıyı UserDTO ile eşleştirmek ve Kullanıcı'yı UserViewModel ile eşlemek istiyorsanız, iki ayrı uygulamaya ihtiyacınız olacaktır. Tüm bu mantığı tek bir sınıfa yerleştirmeyin - bunu yapmak mantıklı değil.

Joachim'i mutlu etmek için güncelleme

public interface ITypeConverter<TSource, TDestination>
{
    TDestination Convert(TSource source);
}

Ancak şimdi Automapper bölgesinde bulunuyoruz ( http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters )


IMappableBaşka şeyleri haritalayan bir şey için iyi bir isim olduğunu düşünmüyorum . Mapper(veya IMappergerekirse ;-)) muhtemelen daha doğrudur. (Bu arada: hayır, bu benim düşüşüm değildi).
Joachim Sauer

Soruda ne olduğunu aldım ve bir arabirim olduğunu vurgulamak için bir I ile ön ek yaptım. Bir adlandırma sorununun aksine, tasarım problemini soru başına çözüyorum.
CodeART

1
üzgünüm, ama bence bir tasarım sorunu gerçekten "çözmek" ve adlandırma görmezden gelemez. Tasarım anlaşılır yapılar anlamına gelir. Yanlış adlandırma, anlaşılması gereken bir sorundur.
Joachim Sauer

Güncelleme aklını dinlendirmeli ;-)
CodeART

1
@CodeART Cevabınızı doğru anladıysam, "MapFrom" anlamına gelir (küçük harfle yazılması gerekir ;-) nesneyi oluşturur. Benim durumumda, daha önce oluşturulmuş bir nesne hakkındaki bilgileri dolduruyor.
estani
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.