Nói rằng tôi có một lớp Foobar
sử dụng (phụ thuộc) lớp Widget
. Trong những ngày tốt, Widget
wolud được khai báo là một trường trong Foobar
hoặc có thể là một con trỏ thông minh nếu cần hành vi đa hình, và nó sẽ được khởi tạo trong hàm tạo:
class Foobar {
Widget widget;
public:
Foobar() : widget(blah blah blah) {}
// or
std::unique_ptr<Widget> widget;
public:
Foobar() : widget(std::make_unique<Widget>(blah blah blah)) {}
(…)
};
Và chúng ta đã sẵn sàng và hoàn thành. Thật không may, ngày nay, những đứa trẻ Java sẽ cười nhạo chúng tôi khi chúng nhìn thấy nó, và đúng, khi nó kết đôi Foobar
và Widget
cùng nhau. Giải pháp có vẻ đơn giản: áp dụng Dependency Injection để xây dựng các phụ thuộc ra khỏi Foobar
lớp. Nhưng sau đó, C ++ buộc chúng ta phải suy nghĩ về quyền sở hữu phụ thuộc. Ba giải pháp xuất hiện trong tâm trí:
Con trỏ độc đáo
class Foobar {
std::unique_ptr<Widget> widget;
public:
Foobar(std::unique_ptr<Widget> &&w) : widget(w) {}
(…)
}
Foobar
tuyên bố quyền sở hữu duy nhất của Widget
nó được chuyển cho nó. Điều này có những lợi thế sau:
- Tác động đến hiệu suất là không đáng kể.
- Nó an toàn, vì
Foobar
kiểm soát trọn đờiWidget
, vì vậy nó đảm bảo rằngWidget
nó không đột nhiên biến mất. - Nó an toàn
Widget
sẽ không bị rò rỉ và sẽ bị phá hủy đúng cách khi không còn cần thiết.
Tuy nhiên, điều này đi kèm với chi phí:
- Nó đặt các hạn chế về cách các
Widget
thể hiện có thể được sử dụng, ví dụ: không thể sử dụng ngăn xếp được phân bổWidgets
, khôngWidget
thể chia sẻ.
Con trỏ dùng chung
class Foobar {
std::shared_ptr<Widget> widget;
public:
Foobar(const std::shared_ptr<Widget> &w) : widget(w) {}
(…)
}
Đây có lẽ là Java tương đương gần nhất và các ngôn ngữ được thu gom rác khác. Ưu điểm:
- Phổ quát hơn, vì nó cho phép chia sẻ phụ thuộc.
- Duy trì sự an toàn (điểm 2 và 3) của
unique_ptr
giải pháp.
Nhược điểm:
- Lãng phí tài nguyên khi không chia sẻ.
- Vẫn yêu cầu phân bổ heap và không cho phép các đối tượng được phân bổ stack.
Con trỏ quan sát đồng bằng ol '
class Foobar {
Widget *widget;
public:
Foobar(Widget *w) : widget(w) {}
(…)
}
Đặt con trỏ thô bên trong lớp và chuyển gánh nặng sở hữu cho người khác. Ưu điểm:
- Đơn giản như nó có thể nhận được.
- Phổ thông, chấp nhận bất kỳ
Widget
.
Nhược điểm:
- Không an toàn nữa.
- Giới thiệu một thực thể khác chịu trách nhiệm sở hữu cả hai
Foobar
vàWidget
.
Một số siêu mẫu điên
Ưu điểm duy nhất tôi có thể nghĩ đến là tôi có thể đọc tất cả những cuốn sách mà tôi không tìm thấy thời gian trong khi phần mềm của tôi đang hoạt động;)
Tôi nghiêng về giải pháp thứ ba, vì nó là phổ quát nhất, Foobars
dù sao cũng phải quản lý một cái gì đó , vì vậy quản lý Widgets
là thay đổi đơn giản. Tuy nhiên, sử dụng con trỏ thô làm phiền tôi, mặt khác, giải pháp con trỏ thông minh cảm thấy sai đối với tôi, vì chúng khiến người tiêu dùng phụ thuộc hạn chế cách tạo ra sự phụ thuộc đó.
Tui bỏ lỡ điều gì vậy? Hay chỉ là Dependency Injection trong C ++ không tầm thường? Lớp học nên sở hữu phụ thuộc của nó hay chỉ quan sát chúng?
Foobar
là chủ sở hữu duy nhất? Trong trường hợp cũ, nó đơn giản. Nhưng vấn đề với DI, như tôi thấy, là khi nó tách lớp khỏi việc xây dựng các phụ thuộc của nó, nó cũng tách nó khỏi quyền sở hữu của các phụ thuộc đó (vì quyền sở hữu gắn liền với xây dựng). Trong các môi trường rác được thu thập như Java, đó không phải là vấn đề. Trong C ++, đây là.
std::unique_ptr
là cách để đi. Bạn có thể sử dụngstd::move()
để chuyển quyền sở hữu tài nguyên từ phạm vi trên sang lớp.