Alaycı üretim kodunda işleme koyuluyor


15

Bir IReader arayüzü, IReader arayüzü ReaderImplementation uygulaması ve okuyucudan veri tüketen ve işleyen bir sınıf ReaderConsumer varsayarsak.

public interface IReader
{
     object Read()
}

uygulama

public class ReaderImplementation
{
    ...
    public object Read()
    {
        ...
    }
}

Tüketici:

public class ReaderConsumer()
{
    public string location

    // constructor
    public ReaderConsumer()
    {
        ...
    }

    // read some data
    public object ReadData()
    {
        IReader reader = new ReaderImplementation(this.location)
        data = reader.Read()
        ...
        return processedData    
    }
}

ReaderConsumer ve işlemeyi test etmek için bir IReader taklidi kullanıyorum. Böylece ReaderConsumer:

public class ReaderConsumer()
{
    private IReader reader = null

    public string location

    // constructor
    public ReaderConsumer()
    {
        ...
    }

    // mock constructor
    public ReaderConsumer(IReader reader)
    {
        this.reader = reader
    }

    // read some data
    public object ReadData()
    {
        try
        {
            if(this.reader == null)
            {
                 this.reader = new ReaderImplementation(this.location)
            }

            data = reader.Read()
            ...
            return processedData    
        }
        finally
        {
            this.reader = null
        }
    }
}

Bu çözümde alaycı üretim kodu için bir if cümlesini getirir, çünkü sadece alaycı kurucu arabirimin bir örneğini sağlar.

Bunu yazarken, uygulamanın çalışma zamanı sırasında kullanıcının yerini değiştirmesini sağlamak için try-finally bloğunun biraz ilgisiz olduğunu anlıyorum.

Genel olarak koklamak hissediyor, nasıl daha iyi ele alınabilir?


16
Tipik olarak, bu bir sorun değildir, çünkü bağımlılığı enjekte edilen kurucu tek kurucu olacaktır. Bunu yapmak için söz konusu değildir ReaderConsumerbağımsız ReaderImplementation?
Chris Wohlert

Şu anda bağımlılığı ortadan kaldırmak zor olacaktır. Biraz daha bakarak sadece ReaderImplemenatation bağımlılığından daha derin bir sorun var. ReaderConsumer bir fabrikadan başlatma sırasında oluşturulduğundan, uygulamanın ömrü boyunca devam eder ve kullanıcılardan gelen değişiklikleri kabul eder, bu da biraz ekstra masaj gerektirir. Muhtemelen yapılandırma / kullanıcı girişi bir nesne olarak var olabilir ve bunun yerine ReaderConsumer ve ReaderImplementation anında oluşturulabilir. Verilen her iki cevap da daha genel durumu oldukça iyi çözer.
kristian mo

3
Evet . Bu husus TDD: yazma testlerine gerek ilk ima daha ayrılmış tasarımı (aksi takdirde yazmak mümkün değildir birim ... testleri). Bu, kodu daha sürdürülebilir ve genişletilebilir hale getirmeye yardımcı olur.
17'de Bakuriu

Bağımlılık Enjeksiyonu ile çözülebilen kokuları tespit etmenin iyi bir yolu 'yeni' anahtar kelimesini aramaktır. Bağımlılıklarınızı yenilemeyin. Bunun yerine enjekte edin.
Ebedi21

Yanıtlar:


67

Okuyucuyu yönteminizden başlatmak yerine bu satırı hareket ettirin

{
    this.reader = new ReaderImplementation(this.location)
}

Varsayılan parametresiz yapıcıya.

public ReaderConsumer()
{
    this.reader = new ReaderImplementation(this.location)
}

public ReaderConsumer(IReader reader)
{
    this.reader = reader
}

"Sahte kurucu" diye bir şey yoktur, eğer sınıfınız çalışmak için ihtiyaç duyduğu bir bağımlılığa sahipse, kurucuya bu şey sağlanmalı ya da yaratılmalıdır.


3
skor ++ ... DI bakış açısı hariç, varsayılan kurucu kesinlikle bir kod kokusudur.
Mathieu Guindon

3
@ Mat's: Varsayılan bir uygulamada yanlış hiçbir şey yapma Ctor zincirleme eksikliği burada kokudur. =;) -
RubberDuck

Oh, evet var - eğer kitabınız yoksa, bu makale piç enjeksiyon anti-paternini oldukça iyi tarif ediyor gibi görünüyor (sadece olsa da yağmalanmış).
Mathieu Guindon

3
@ Mat'sMug: DI'yi dogmatik olarak yorumluyorsunuz. Varsayılan kurucuyu enjekte etmeniz gerekmiyorsa, yapmazsınız.
Robert Harvey

3
@ Mat'sMug bağlantılı kitabı, bağımlılıkların gayet iyi olduğu için "kabul edilebilir varsayılanlar" dan bahseder (özellik enjeksiyonuna atıfta bulunmasına rağmen). Şöyle söyleyelim: İki kurucu yaklaşım , tek kurucu yaklaşımdan daha basit ve sürdürülebilirlik açısından daha maliyetlidir ve soru netlik için aşırı basitleştirildiğinde, maliyet haklı değildir, ancak bazı durumlarda olabilir .
Carl Leth

54

Yalnızca tek bir kurucuya ihtiyacınız vardır:

public class ReaderConsumer()
{
    private IReader reader = null

    public string location

    // constructor
    public ReaderConsumer(IReader reader)
    {
        this.reader = reader;
    }

üretim kodunuzda:

var rc = new ReaderConsumer(new ReaderImplementation(0));

testinizde:

var rc = new ReaderConsumer(new MockImplementation(0));

13

Bağımlılık Enjeksiyonu ve Kontrolün Tersine Bakın

Hem Ewan hem de RubberDuck'ın mükemmel cevapları var. Ancak bakmak için Bağımlılık Enjeksiyonu (DI) ve İnversiyon Kontrol (IoC) olan başka bir alandan bahsetmek istedim. Bu yaklaşımların her ikisi de, yaşadığınız sorunu bir çerçeve / kitaplığa taşır, böylece endişelenmenize gerek kalmaz.

Örneğiniz basittir ve hızlı bir şekilde dağıtılır, ancak kaçınılmaz olarak bunun üzerine inşa edersiniz ve tonlarca inşaatçı veya başlatma rutini ile sonuçlanırsınız:

var foo = yeni Foo (yeni Bar (yeni Baz (), yeni Quz ()), yeni Foo2 ());

DI / IoC ile arayüzleri uygulamalarla eşleştirme kurallarını hecelemenize izin veren bir kütüphane kullanırsınız ve daha sonra sadece "Bana bir Foo verin" deyin ve hepsini nasıl bağlayacağınız işe yarar.

Orada çok dostu IoC Konteyner (onlar denir gibi) vardır, ve ben bakmak için bir tavsiye edeceğim, ama, çok iyi seçimler olduğu gibi, keşfetmek lütfen.

Başlamak için basit bir tane:

http://www.ninject.org/

Keşfetmek için bir liste:

http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx


7
Hem Ewan'ın hem de RubberDuck'un cevapları DI'yi gösteriyor . DI / IoC bir araç veya çerçeve ile ilgili değil, kodun kontrol ters çevrilecek ve bağımlılıklar enjekte edilecek şekilde yapılandırılması ve yapılandırılmasıyla ilgilidir. Mark Seemann'ın kitabı , DI / IoC'nin bir IoC çerçevesi olmadan nasıl tamamen elde edilebildiğini ve bir IoC çerçevesinin neden ve ne zaman iyi bir fikir haline geldiğini açıklamak için harika (harika!) Bir iş yapıyor . .) =)
Mathieu Guindon

Ayrıca ... +1 çünkü Ninject harika ve yeniden okuduktan sonra cevabınız iyi görünüyor. İlk paragraf, Ewan ve RubberDuck'ın cevaplarının DI ile ilgili olmadığı gibi biraz okuyor, bu da ilk yorumumu istedi.
Mathieu Guindon

1
@ Mat'sMug Kesinlikle puan al. OP'yi diğer posterlerin aslında olduğu gibi DI / IoC'ye bakmaya teşvik etmeye çalışıyordum (aslında Ewan'ın Zavallı Adamın DI'sı), ama bahsetmedim. Tabii ki, bir çerçeve kullanmanın avantajı, kullanıcıyı DI dünyasına, ne yaptıklarını anlamalarına rehberlik edecek çok sayıda somut örnekle daldırmasıdır ... yine de ... her zaman değil. :-) Ve tabii ki, Mark Seemann'ın kitabını çok sevdim. Benim favorilerimden biri.
Reginald Blue

Bir DI çerçevesi hakkında ipucu için teşekkür ederiz, bu karışıklığı düzgün bir şekilde yeniden düzenleme yolu gibi görünüyor :)
kristian mo
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.