Протягом останніх місяців я просив, щоб люди, які перебувають тут, на SE та інших сайтах, пропонували мені конструктивну критику щодо мого коду. Є одне, що постійно вискакує майже кожен раз, і я все ще не згоден з цією рекомендацією; : P Я хотів би це обговорити тут, і, можливо, мені все стане зрозумілішим.
Це стосується принципу єдиної відповідальності (СРП). В основному, у мене клас даних Font, який не тільки містить функції для маніпулювання даними, але і для його завантаження. Мені кажуть, що обидві повинні бути окремими, що функції завантаження повинні бути розміщені всередині заводського класу; Я думаю, що це неправильне тлумачення СРП ...
Фрагмент з мого класу шрифтів
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();
};
Пропонований дизайн
class Font
{
public:
void some();
void another();
};
class FontFactory
{
public:
virtual std::unique_ptr<Font> createFromFile(...) = 0;
virtual std::unique_ptr<Font> createFromMemory(...) = 0;
};
Запропонований дизайн нібито відповідає СРП, але я не згоден - я думаю, що це заходить занадто далеко. FontКлас більше не є самодостатнім (це марно без заводу), і FontFactoryповинен знати подробиці про реалізацію ресурсу, який, ймовірно , зроблений через дружбу або громадських добувач, які в подальшому викрити реалізацію Font. Я думаю, що це скоріше випадок фрагментарної відповідальності .
Ось чому я вважаю, що мій підхід кращий:
Fontє самодостатньою - Будучи самодостатньою, її легше зрозуміти та підтримувати. Крім того, ви можете використовувати клас, не включаючи нічого іншого. Якщо, однак, вам здається, що вам потрібно більш складне управління ресурсами (фабрикою), ви також можете це легко зробити (пізніше я розповім про свою власну фабрику,ResourceManager<Font>).Дотримується стандартної бібліотеки - я вважаю, що визначені користувачем типи повинні намагатися максимально скопіювати поведінку стандартних типів на відповідній мові. Це
std::fstreamсамодостатнє і забезпечує такі функції, якopenіclose. Дотримуватися стандартної бібліотеки означає, що не потрібно витрачати зусиль на вивчення ще одного способу виконання справ. Крім того, загалом кажучи, стандартний комітет C ++, напевно, знає більше про дизайн, ніж хто-небудь тут, тому, якщо коли-небудь сумніваєтесь, скопіюйте те, що вони роблять.Заповітність - щось піде не так, де може бути проблема? - Це спосіб
Fontобробки даних або спосібFontFactoryзавантаження даних? Ви насправді не знаєте. Наявність занять самодостатністю зменшує цю проблему: ви можете пройти випробуванняFontізольовано. Якщо потім доведеться протестувати фабрику, і ви знаєте, щоFontпрацює добре, ви також знатимете, що коли виникає проблема, вона повинна знаходитися всередині заводу.Це контекстно-агностичний - (Це трохи перетинається з моєю першою точкою.)
FontРобить свою справу і не робить жодних припущень щодо того, як ви будете ним користуватися: ви можете використовувати його будь-яким способом. Примушування користувача до використання фабрики збільшує зв'язок між класами.
У мене теж є фабрика
(Тому що дизайн Fontдозволяє мені.)
А точніше, більше керівника, а не просто фабрики ... Fontсамодостатня, тому менеджеру не потрібно знати, як його побудувати; натомість менеджер гарантує, що один і той же файл або буфер не завантажуються в пам'ять більше одного разу. Ви можете сказати, що фабрика може зробити те саме, але хіба це не зірве SRP? Тоді фабрика повинна не лише будувати об’єкти, але й керувати ними.
template<class T>
class ResourceManager
{
public:
ResourcePtr<T> acquire(const std::string& file);
ResourcePtr<T> acquire(const void* buffer, std::size_t size);
};
Ось демонстрація того, як менеджер може бути використаний. Зауважте, що він використовується в основному саме так, як завод.
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...
}
Нижня лінія...
(Тут хотілося б поставити тл; д-р, але я не можу придумати його.: \)
Ну, ось у вас це є, я зробив свою справу якнайкраще. Будь ласка, опублікуйте будь-які зустрічні аргументи, а також будь-які переваги, на які, на вашу думку, запропонована конструкція має над моїм власним дизайном. В основному, спробуйте показати мені, що я помиляюся. :)