Tür eşleme ve genişletme yöntemleri ile ilgili en iyi uygulamalar


15

Eşleme türleri ve uzantı yöntemlerini C # kullanarak en iyi uygulamalar hakkında bazı sorular sormak istiyorum. Bu konunun son birkaç yılda birçok kez tartışıldığını biliyorum, ancak birçok gönderi okudum ve hala şüphelerim var.

Karşılaştığım sorun "convert" işlevselliği ile sahip olduğum sınıf genişletme oldu. Diyelim ki bazı mantık tarafından kullanılacak bir nesneyi temsil eden "Kişi" sınıfım var. Ayrıca harici API bir yanıtı temsil eden bir sınıf "Müşteri" var (aslında birden fazla API olacak, bu yüzden ortak tip: Person her yanıtın eşlemek gerekir). Her iki sınıfın kaynak koduna erişimim var ve teorik olarak kendi yöntemlerimi uygulayabilirim. Veritabanına kaydedebilmem için Müşteriyi Kişiye dönüştürmem gerekiyor. Proje otomatik haritacı kullanmıyor.

Aklımda 4 olası çözüm var:

  1. Tüketici sınıfında .ToPerson () yöntemi. Bu basit, ancak Tek Sorumluluk modelini kırmak gibi görünüyor, özellikle de Tüketici sınıfı diğer sınıflarla (bazıları başka bir harici API için gerekli) eşlendiğinden, birden fazla eşleme yöntemi içermesi gerekiyor.

  2. Kişi sınıfında Tüketici'yi argüman olarak alan haritalama kurucusu. Ayrıca kolay ve aynı zamanda Tek Sorumluluk modelini kırmak gibi görünüyor. Birden fazla eşleme yapıcısına ihtiyacım olacak (çünkü başka bir API'dan gelen sınıf, Tüketici ile aynı verileri sağlayan ancak biraz farklı bir formatta olacak)

  3. Genişletme yöntemleri ile konvertörler sınıfı. Bu şekilde Tüketici sınıfı için .ToPerson () yöntemi yazabilirim ve kendi NewConsumer sınıfıyla başka bir API tanıtıldığında, sadece başka bir uzantı yöntemi yazabilir ve hepsini aynı dosyada tutabilirim. Uzatma yöntemlerinin genel olarak kötü olduğu ve sadece kesinlikle gerekli olması durumunda kullanılması gerektiğine dair bir görüş duydum, bu yüzden beni geride tutan şey bu. Aksi takdirde bu çözümü beğendim

  4. Dönüştürücü / Eşleyici sınıfı. Dönüşümleri işleyecek ve kaynak sınıf örneğini bağımsız değişken olarak alacak ve hedef sınıf örneği döndürecek yöntemleri uygulayacak ayrı bir sınıf oluşturuyorum.

Özetlemek gerekirse, sorunum soru sayısına indirilebilir (hepsi yukarıda açıkladığım bağlamda):

  1. Dönüştürme yöntemini (POCO?) Nesnesinin (Tüketici sınıfındaki .ToPerson () yöntemi gibi) içine koymak tek sorumluluk kalıbını kırıyor mu?

  2. (DTO benzeri) sınıfta dönüştürücü kurucuları kullanmanın tek sorumluluk modelini kırdığı düşünülüyor mu? Özellikle bu sınıf birden çok kaynak türünden dönüştürülebiliyorsa, çok sayıda dönüştürücü kurucu gerekli olur mu?

  3. Orijinal sınıf kaynak koduna erişirken genişletme yöntemlerini kullanmak kötü bir uygulama mıdır? Böyle bir davranış, mantığı ayırmak için uygulanabilir bir kalıp olarak kullanılabilir mi yoksa bir anti-kalıp mıdır?


Personsınıf DTO? herhangi bir davranış içeriyor mu?
Yacoub Massad

Bildiğim kadarıyla teknik olarak bir DTO olduğunu. Genişletme yöntemi olarak "enjekte edilen" bir mantık içerir, ancak bu mantık "ConvertToThatClass" yöntemleriyle sınırlıdır. Bunların hepsi eski yöntemlerdir. Benim işim, bu sınıflardan bazılarını kullanan yeni bir işlevsellik uygulamaktır, ancak bana sadece tutarlı kalmanın kötü olması durumunda mevcut yaklaşıma bağlı kalmamam gerektiği söylendi. Bu yüzden uzatma yöntemlerini kullanarak dönüştürme ile bu mevcut yaklaşımın iyi olup olmadığını merak ediyorum ve onunla sopa veya başka bir şey denemek gerekir
emsi

Yanıtlar:


11

Dönüştürme yöntemini (POCO?) Nesnesinin (Tüketici sınıfındaki .ToPerson () yöntemi gibi) içine koymak tek sorumluluk kalıbını kırıyor mu?

Evet, çünkü dönüşüm başka bir sorumluluktur.

(DTO benzeri) sınıfta dönüştürücü kurucuları kullanmanın tek sorumluluk modelini kırdığı düşünülüyor mu? Özellikle bu sınıf birden çok kaynak türünden dönüştürülebiliyorsa, çok sayıda dönüştürücü kurucu gerekli olur mu?

Evet, dönüşüm başka bir sorumluluktur. Yapıcılar veya dönüştürme yöntemleri (örneğin ToPerson) ile bunu yaparsanız hiçbir fark yaratmaz .

Orijinal sınıf kaynak koduna erişirken genişletme yöntemlerini kullanmak kötü bir uygulama mıdır?

Şart değil. Genişletmek istediğiniz sınıfın kaynak koduna sahip olsanız bile uzantı yöntemleri oluşturabilirsiniz. Bir uzatma yöntemi oluşturup oluşturmamanızın bu yöntemin doğası tarafından belirlenmesi gerektiğini düşünüyorum. Örneğin, çok fazla mantık içeriyor mu? Nesnenin kendisinin üyesi olan başka bir şeye mi bağlı? Çalışmak için bağımlılıklar gerektiren veya karmaşık mantık içeren bir uzatma yöntemine sahip olmamalısınız diyebilirim. Bir uzatma yönteminde sadece en basit mantık bulunmalıdır.

Böyle bir davranış, mantığı ayırmak için uygulanabilir bir kalıp olarak kullanılabilir mi yoksa bir anti-kalıp mıdır?

Mantık karmaşıksa, o zaman bir uzatma yöntemi kullanmamanız gerektiğini düşünüyorum. Daha önce de belirttiğim gibi, uzantı yöntemlerini sadece en basit şeyler için kullanmalısınız. Ben dönüşüm basit düşünün olmaz.

Dönüşüm hizmetleri oluşturmanızı öneririm. Bunun için tek bir genel arabiriminiz olabilir:

public interface IConverter<TSource,TDestination>
{
    TDestination Convert(TSource source_object);
}

Ve bunun gibi dönüştürücüler olabilir:

public class PersonToCustomerConverter : IConverter<Person,Customer>
{
    public Customer Convert(Person source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
}

Ve ve arasında dönüştürme yeteneği gerektiren herhangi bir sınıfa bir dönüştürücü (örn. ) Enjekte etmek için Bağımlılık Enjeksiyonu kullanabilirsiniz .IConverter<Person,Customer>PersonCustomer


Yanılmıyorsam, IConverterçerçeve içinde zaten var, sadece uygulanmayı bekliyor.
RubberDuck

@RubberDuck, nerede var?
Yacoub Massad

Ben düşünüyordum IConvertableki, değil burada aradığınızı. Benim hatam.
RubberDuck

Ah ha! Ne düşündüğümü @YacoubMassad buldum. çağrıldığında Converterkullanılır . msdn.microsoft.com/tr-tr/library/kt456a2y(v=vs.110).aspx Bunun OP için ne kadar yararlı olduğunu bilmiyorum. ListConvertAll
RubberDuck

Ayrıca ilgili. Burada önerdiğiniz yaklaşımı başka biri aldı. codereview.stackexchange.com/q/51889/41243
RubberDuck

5

Dönüşüm yöntemini (POCO?) Nesnesinin ( .ToPerson()Tüketici sınıfındaki yöntem gibi) içine koymak, tek sorumluluk modelini kırmak olarak kabul edilir mi?

Evet. Bir Consumersınıf, bir tüketici ile ilgili verilerin tutulmasından (ve muhtemelen bazı eylemlerin gerçekleştirilmesinden) sorumludur ve kendisini başka bir ilgisiz türe dönüştürmekten sorumlu olmamalıdır .

(DTO benzeri) sınıfta dönüştürücü kurucuları kullanmanın tek sorumluluk modelini kırdığı düşünülüyor mu? Özellikle bu sınıf birden çok kaynak türünden dönüştürülebiliyorsa, çok sayıda dönüştürücü kurucu gerekli olur mu?

Muhtemelen. Genellikle dönüştürme yapmak için hem etki alanı hem de DTO nesneleri dışında yöntemler var. Genellikle etki alanı nesnelerini alır ve (de) onları bir veritabanı, bellek, dosya, ne olursa olsun seri hale getiren bir depo kullanın. Bu mantığı etki alanı sınıflarıma koyarsam, şimdi onları belirli bir serileştirme biçimine bağladım, bu da test için kötü (ve diğer şeyler). Eğer mantığı DTO sınıflarıma koyarsam, tekrar testlerimi sınırlandırarak alanımla bağladım.

Orijinal sınıf kaynak koduna erişirken genişletme yöntemlerini kullanmak kötü bir uygulama mıdır?

Genel olarak, onlar olmasa olabilir gereksiz yere. Uzantı yöntemleriyle, kodun okunmasını kolaylaştıran isteğe bağlı uzantılar oluşturursunuz . Dezavantajları var - derleyicinin çözmesi gereken belirsizlikler oluşturmak daha kolaydır (bazen sessizce) ve kodun hata ayıklamasını daha zor hale getirebilir, çünkü uzantı yönteminin nereden geldiği tamamen açık değildir.

Sizin durumunuzda, bir dönüştürücü veya haritacı sınıfı en basit, en basit yaklaşım gibi görünüyor, ancak uzantı yöntemlerini kullanarak yanlış bir şey yaptığınızı düşünmüyorum .


2

AutoMapper veya özel haritalayıcı kullanmaya nasıl benzer?

MyMapper
   .CreateMap<Person>()
   .To<PersonViewModel>()
   .Map(p => p.Name, vm => vm.FirstName)
   .To<SomeDTO>()
   .Map(...);

alan adından

 db.Persons
   .ToListAsync()
   .Map<PersonViewModel>();

Kaputun altında AutoMapper'ı soyutlayabilir veya kendi eşleyicinizi yuvarlayabilirsiniz


2

Bunun eski olduğunu biliyorum, ama yine de şaşkın. Bu, her iki yolu da dönüştürmenize olanak tanır. Entity Framework ile çalışırken ve viewmodels (DTO'lar) oluştururken bunu yararlı buluyorum.

public interface IConverter<TSource, TDestination>
{
    TDestination Convert(TSource source_object);
    TSource Convert(TDestination source_object);
}

public class PersonCustomerConverter : IConverter<Person, Customer>
{
    public Customer Convert(Person source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
    public Person Convert(Customer source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
}

AutoMapper'ı gerçekten basit haritalama işlemi yapmak için biraz fazla buluyorum.

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.