2011-10-18 12 views
5

vs RAII nesneleri tahsis etti.Stack Sık sık kod daha güvenilir hale ve kod daha fazla ölçülebilir hale getirmek için yığın onları ayırmak (ve bad_alloc önlemek için) RAII tarzı nesneleri kullanmak C++ DI ilkesine

Ancak yığın üzerinde bir beton sınıfı nesnesi oluşturmak, bağımlılık ters çevirme (DI) ilkesini ihlal eder ve bu nesnenin alay edilmesini önler.

Aşağıdaki kodu düşünün: Ben IBar::process test edebilirsiniz

struct IInputStream 
{ 
    virtual vector<BYTE> read(size_t n) = 0; 
}; 

class Connection : public IInputStream 
{ 
public: 
    Connection(string address); 
    virtual vector<BYTE> read(size_t n) override; 
}; 

struct IBar 
{ 
    virtual void process(IInputStream& stream) = 0; 
}; 

void Some::foo(string address, IBar& bar) 
{ 
    onBeforeConnectionCreated(); 
    { 
     Connection conn(address); 
     onConnectionCreated(); 
     bar.process(conn); 
    } 
    onConnectionClosed(); 
} 

, ama aynı zamanda gerçek Bağlantı nesnesi oluşturmadan, Some::foo test etmek istiyorum.

Elbette bir fabrika kullanabilirim, ancak kodu önemli ölçüde karmaşıklaştıracak ve yığın ayırmayı başlatacaktır.
Ayrıca, Connection::open yöntemini eklemek istemiyorum, tamamen başlatılmış ve tamamen işlevsel nesneler oluşturmayı tercih ediyorum.

Ben Some için Connection türü bir şablon parametresi yapmak (ya foo için eğer bir serbest fonksiyonu olarak bunu ayıklamak), ama doğru yolu (şablonlar birçok kişiye bir kara büyü gibi görünür, bu yüzden olduğundan emin değilim ediyorum) dinamik polimorfizm kullanmayı tercih

+2

Şablonlar kara büyü için az ya da çok yetkin C++ programcısı olmamalı, ben bunları önlemek için bir neden göremiyorum.Ayrıca, yığın tahsisinin * pahalı olduğunu sanmıyorum (bu, tabii ki, yazdığınız yazılıma bağlıdır), bu yüzden de (akıllı işaretçilerle kullanıldığında) bundan kaçınmak için bir neden göremiyorum. –

+4

@Alex B: Onlardan sakınmak için bir neden var, ancak bunun “kara büyü” oldukları için olmadığına katılıyorum. Çünkü her şey şablon parametreleri ile enjekte edilirse, yazdığınız her şey bir şablontur, kütüphaneniz sadece başlıklıdır ve derleme veya dağıtım açısından oldukça hantal olabilir. Her ne kadar, dikkatli bir şekilde, tek başına kütüphaneyi test edebileceksiniz, ancak ondan, uygulamanın ihtiyaç duyduğu örneklemleri içeren bir TU'yu inşa edebilirsiniz. –

+1

RAII ve DI birlikte harika çalışıyor, bu yüzden başlık yanıltıcı, senin sorunun Stack Allocation vs DI. –

cevap

5

Şimdi ne doğru yapıyorsun "kuvvet-kavrama" RAII sınıfı ve test edilebilir bir istiyorum, gerçekten yerine bir arayüz olmalıdır servis sağlayıcı sınıfı() 'dir.

  • Örneğin o
  • üstünde de ray sağlayan ayrı bir ScopedConnection sınıf var IConnection içine Connection abstracting

    1. : Bu Adres By

      void Some::foo(string address, IBar& bar) 
      { 
          onBeforeConnectionCreated(); 
          { 
           ScopedConnection conn(this->pFactory->getConnection()); 
           onConnectionCreated(); 
           bar.process(conn); 
          } 
          onConnectionClosed(); 
      } 
      
    +2

    Ve "ScopedConnection" ın alay edilmesine gerek olmadığını kabul edin, 'Bazı :: foo'yu izole etmesi gereken testlerde bile gerçek sürümü kullanmak için "güvenli". bu kabul edilemez Veya, diş gıcırdatmak ve şablon parametresi olarak enjekte veya standart bir sınıf olarak (ya da 3. parti hala C++ 03 iseniz) de ray sağlamak için 'scoped_ptr' kullanmak zor kabul edilebilir olduğunu bağımlılık. –

    +0

    Fabrika hakkında yazdığım şey buydu. Cevabınızı takip edebilmek için, sadece Bağlantı için bir fabrika veya pek çok ilgisiz sınıf için fabrika oluşturmanız gerekir (önerdiğiniz gibi). Bu fabrikayı bir çok katmandan (bazılarını) küreselleştirin. – Abyx

    +0

    @Abyx: fabrika elle geçirilerek veya küresel sahip tercih olacaktır DI bir aday olurdu. Ama soyutlamayı arttırmak için buna ihtiyacın var. – Jon

    1

    "Ben kullanabilirsiniz bir fabrika, ancak kodları karmaşık bir şekilde karmaşıklaştıracak ve yığın ayırma işlemini başlatacaktır "Aşağıdaki adımları kastediyorum:

    unique_ptr<AConnection> conn(createConnection(address)); 
    
    1

    gerçek arasında seçim için akıllı pointer yığın-tahsis connectonu değiştirin soyut sınıf oluşturun ve

    struct AConnection : IInputStream 
    { 
        virtual ~AConnection() {} 
    }; 
    

    Add fabrika yöntemi ile Some

    class Some 
    { 
    ..... 
    protected: 
        VIRTUAL_UNDER_TEST AConnection* createConnection(string address); 
    }; 
    

    den Connection türetmek uygulama ve alay edilen, gerçek ty enjekte Bir şekilde inşa etmek istediğin pe. Tavsiye ettiğim yol, türü isteğe bağlı bir şablon parametresi olarak enjekte etmektir. Alışılagelmiş bir şekilde Some::foo'u alışkın olarak kullanmanıza izin verir, ancak bir test durumunda oluşturulan bağlantıyı değiştirmenizi sağlar. Derleme zamanında asıl türünü biliyorsanız

    template<typename ConnectionT=Connection> // models InputStream 
    void Some::foo(string address, IBar& bar) 
    { 
        onBeforeConnectionCreated(); 
        { 
         ConnectionT conn(address); 
         onConnectionCreated(); 
         bar.process(conn); 
        } 
        onConnectionClosed(); 
    } 
    

    Bir fabrika ve çalışma zamanı polimorfizm masraflı olmaz.

    İlgili konular