Trong những tháng qua, tôi đã yêu cầu mọi người ở đây trên SE và trên các trang web khác cung cấp cho tôi một số lời chỉ trích mang tính xây dựng liên quan đến mã của tôi. Có một điều luôn xuất hiện gần như mọi lúc và tôi vẫn không đồng ý với khuyến nghị đó; : P Tôi muốn thảo luận về nó ở đây và có lẽ mọi thứ sẽ trở nên rõ ràng hơn với tôi.
Đó là về nguyên tắc trách nhiệm đơn (SRP). Về cơ bản, tôi có một lớp dữ liệu, Font
không chỉ chứa các hàm để thao tác dữ liệu mà còn để tải nó. Tôi đã nói với hai người nên tách biệt, rằng các chức năng tải nên được đặt bên trong một lớp nhà máy; Tôi nghĩ rằng đây là một cách hiểu sai về SRP ...
Một mảnh từ lớp phông chữ của tôi
class Font
{
public:
bool isLoaded() const;
void loadFromFile(const std::string& file);
void loadFromMemory(const void* buffer, std::size_t size);
void free();
void some();
void another();
};
Thiết kế đề xuất
class Font
{
public:
void some();
void another();
};
class FontFactory
{
public:
virtual std::unique_ptr<Font> createFromFile(...) = 0;
virtual std::unique_ptr<Font> createFromMemory(...) = 0;
};
Thiết kế được đề xuất được cho là theo SRP, nhưng tôi không đồng ý - tôi nghĩ nó đi quá xa. Các Font
lớp học không phải là còn tự túc (nó là vô ích mà không có nhà máy), và FontFactory
nhu cầu để biết thông tin chi tiết về việc thực hiện của tài nguyên, mà có lẽ là thực hiện thông qua tình bạn hoặc getters công cộng, trong đó tiếp tục phơi bày việc thực hiện Font
. Tôi nghĩ rằng đây là một trường hợp trách nhiệm phân mảnh .
Đây là lý do tại sao tôi nghĩ rằng cách tiếp cận của tôi là tốt hơn:
Font
là tự túc - Tự túc, dễ hiểu và dễ duy trì hơn. Ngoài ra, bạn có thể sử dụng lớp mà không cần phải bao gồm bất cứ điều gì khác. Tuy nhiên, nếu bạn thấy bạn cần một sự quản lý tài nguyên phức tạp hơn (một nhà máy), bạn cũng có thể dễ dàng làm điều đó (sau này tôi sẽ nói về nhà máy của riêng tôi,ResourceManager<Font>
).Theo thư viện chuẩn - Tôi tin rằng các loại do người dùng xác định nên thử càng nhiều càng tốt để sao chép hành vi của các loại tiêu chuẩn trong ngôn ngữ tương ứng đó. Nó
std::fstream
là tự cung cấp và nó cung cấp các chức năng nhưopen
vàclose
. Theo thư viện tiêu chuẩn có nghĩa là không cần phải nỗ lực học thêm một cách làm việc khác. Bên cạnh đó, nói chung, ủy ban tiêu chuẩn C ++ có thể biết nhiều về thiết kế hơn bất kỳ ai ở đây, vì vậy nếu có nghi ngờ, hãy sao chép những gì họ làm.Khả năng kiểm tra - Một cái gì đó sai, vấn đề có thể ở đâu? - Đó là cách
Font
xử lý dữ liệu của nó hay cáchFontFactory
tải dữ liệu? Bạn không thực sự biết. Có các lớp tự túc làm giảm vấn đề này: bạn có thể kiểm tra mộtFont
cách cô lập. Nếu sau đó bạn phải kiểm tra nhà máy và bạn biết nóFont
hoạt động tốt, bạn cũng sẽ biết rằng bất cứ khi nào xảy ra sự cố thì nó phải ở trong nhà máy.Đó là thuyết bất khả tri - (Điều này giao thoa một chút với điểm đầu tiên của tôi.) Thực
Font
hiện điều đó và không đưa ra giả định nào về cách bạn sẽ sử dụng nó: bạn có thể sử dụng nó theo bất kỳ cách nào bạn muốn. Buộc người dùng sử dụng một nhà máy làm tăng sự ghép nối giữa các lớp.
Tôi cũng có một nhà máy
(Bởi vì thiết kế Font
cho phép tôi.)
Hay đúng hơn là một người quản lý, không chỉ đơn thuần là một nhà máy ... Font
là tự túc nên người quản lý không cần biết cách xây dựng một nhà quản lý ; thay vào đó, người quản lý đảm bảo cùng một tệp hoặc bộ đệm không được tải vào bộ nhớ nhiều lần. Bạn có thể nói một nhà máy có thể làm điều tương tự, nhưng điều đó sẽ không phá vỡ SRP? Nhà máy sau đó không chỉ phải xây dựng các đối tượng, mà còn quản lý chúng.
template<class T>
class ResourceManager
{
public:
ResourcePtr<T> acquire(const std::string& file);
ResourcePtr<T> acquire(const void* buffer, std::size_t size);
};
Đây là một minh chứng về cách người quản lý có thể được sử dụng. Lưu ý rằng về cơ bản nó được sử dụng chính xác như một nhà máy.
void test(ResourceManager<Font>* rm)
{
// The same file isn't loaded twice into memory.
// I can still have as many Fonts using that file as I want, though.
ResourcePtr<Font> font1 = rm->acquire("fonts/arial.ttf");
ResourcePtr<Font> font2 = rm->acquire("fonts/arial.ttf");
// Print something with the two fonts...
}
Dòng dưới cùng ...
(Tôi muốn đặt một tl; dr ở đây, nhưng tôi không thể nghĩ ra một
thứ. Vui lòng gửi bất kỳ đối số bạn có và cũng có bất kỳ lợi thế nào mà bạn nghĩ rằng thiết kế được đề xuất có trên thiết kế của riêng tôi. Về cơ bản, hãy cố gắng cho tôi thấy rằng tôi sai. :)