2011-10-18 31 views
5

Trong C++, tôi thường sử dụng các đối tượng kiểu RAII để làm cho mã đáng tin cậy hơn và phân bổ chúng trên stack để làm cho mã hiệu suất hơn (và để tránh bad_alloc).Ngăn xếp các đối tượng RAII và nguyên tắc DI

Nhưng việc tạo đối tượng lớp bê tông trên ngăn xếp vi phạm nguyên tắc đảo ngược phụ thuộc (DI) và ngăn không cho nhại đối tượng này.

Xét đoạn mã sau:

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(); 
} 

tôi có thể kiểm tra IBar::process, nhưng tôi cũng muốn kiểm tra Some::foo, mà không cần tạo đối tượng kết nối thực.

Chắc chắn tôi có thể sử dụng một nhà máy, nhưng nó sẽ làm phức tạp đáng kể mã và giới thiệu phân bổ đống.
Ngoài ra, tôi không muốn thêm phương thức Connection::open, tôi thích xây dựng các đối tượng hoàn toàn được khởi tạo và đầy đủ chức năng.

Tôi sẽ thực hiện Connection nhập thông số mẫu cho Some (hoặc cho foo nếu trích xuất dưới dạng hàm miễn phí), nhưng tôi không chắc chắn rằng đó là cách đúng (mẫu giống như ma thuật đen cho nhiều người) thích sử dụng đa hình động)

+2

Mẫu không phải là ma thuật đen đối với lập trình C++ nhiều hoặc ít có năng lực, tôi không thấy lý do gì để tránh chúng.Ngoài ra tôi không nghĩ rằng phân bổ đống là * mà * đắt tiền (điều này, tất nhiên, phụ thuộc vào phần mềm bạn viết), vì vậy tôi thấy không có lý do để tránh nó hoặc (khi được sử dụng với con trỏ thông minh). –

+4

@ Alex B: có loại lý do để tránh chúng, mặc dù tôi đồng ý rằng không phải vì chúng là "ma thuật đen". Đó là bởi vì nếu mọi thứ được tiêm thông qua các tham số mẫu, thì mọi thứ bạn viết là một mẫu, thư viện của bạn chỉ là tiêu đề và có thể khá cồng kềnh về mặt biên dịch hoặc phân phối. Mặc dù, tôi cho rằng với sự quan tâm bạn có thể đơn vị kiểm tra thư viện chỉ tiêu đề, sau đó xây dựng từ nó một TU chỉ chứa các instantiations mà ứng dụng cần. –

+1

RAII và DI làm việc tuyệt vời với nhau, vì vậy tiêu đề là gây hiểu lầm, vấn đề của bạn là Phân bổ Stack so với DI. –

Trả lời

5

Những gì bạn đang làm bây giờ là "ghép nối lực" lớp RAII và lớp nhà cung cấp dịch vụ (nếu bạn muốn thử nghiệm, thực sự nên là một giao diện thay thế). Địa chỉ này bằng cách:

  1. trừu tượng Connection vào IConnection
  2. có một lớp riêng biệt ScopedConnection cung cấp RAII trên hết

Ví dụ:

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

Và chấp nhận rằng 'ScopedConnection' không cần phải được chế giễu, nó" an toàn "để sử dụng phiên bản thực ngay cả trong các thử nghiệm được cho là cô lập' Some :: foo'. Hoặc nếu không thể chấp nhận được, hãy nghiến răng và tiêm nó như là một tham số mẫu, hoặc sử dụng 'scoped_ptr' để cung cấp RAII, một lớp tiêu chuẩn (hoặc bên thứ 3 nếu bạn vẫn ở trên C++ 03) là một khó chấp nhận được phụ thuộc. –

+0

Đó là những gì tôi đã viết về nhà máy. Để làm theo câu trả lời của bạn, tôi nên tạo một nhà máy chỉ cho kết nối, hoặc nhà máy cho rất nhiều lớp học không liên quan (như bạn đề nghị). Mang nhà máy này đến 'Some' qua nhiều lớp (hoặc làm cho nó toàn cầu). – Abyx

+0

@Abyx: Nhà máy sẽ là một ứng cử viên chính cho DI, sẽ thích hợp hơn khi chuyển nó qua thủ công hoặc có toàn cầu. Nhưng bạn cần nó để tăng sự trừu tượng. – Jon

1

By "Tôi có thể sử dụng một nhà máy, nhưng nó sẽ có ý nghĩa phức tạp mã và giới thiệu phân bổ đống "Tôi có nghĩa là các bước sau:

Tạo lớp trừu tượng và lấy được Connection từ nó

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

Thêm nhà máy phương pháp để Some

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

Thay connecton đống phân bổ bởi con trỏ thông minh

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

Để chọn giữa thực tế của bạn thực hiện và mô hình được chế biến, bạn phải tiêm công ty thực tế pe mà bạn muốn xây dựng trong một số thời trang. Cách tôi khuyên bạn nên tiêm loại như là một tham số mẫu tùy chọn. Nó cho phép bạn không phô trương sử dụng Some::foo như bạn đã từng sử dụng, nhưng cho phép bạn trao đổi kết nối đã tạo trong trường hợp thử nghiệm.

Tôi sẽ không tạo chi phí cho nhà máy và đa hình thời gian chạy nếu bạn biết loại thực tế tại thời gian biên dịch.

Các vấn đề liên quan