지난 몇 달 동안 여기 SE 및 다른 사이트의 사람들에게 내 코드에 대해 건설적인 비판을 제공했습니다. 거의 매번 튀어 나온 한 가지가 있으며 여전히 그 권장 사항에 동의하지 않습니다. : P 여기에서 논의하고 싶습니다. 아마도 상황이 더 명확해질 것입니다.
단일 책임 원칙 (SRP)에 관한 것입니다. 기본적으로 데이터 조작 클래스 Font
는 데이터 조작뿐만 아니라로드하는 함수를 보유합니다. 로드 함수는 팩토리 클래스 안에 있어야한다고 두 개는 분리되어야한다고 들었습니다. 나는 이것이 SRP의 잘못된 해석이라고 생각합니다 ...
내 서체 클래스의 조각
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;
};
제안 된 디자인은 아마도 SRP를 따른다고 생각하지만 동의하지 않습니다. 너무 멀다고 생각합니다. 이 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...
}
결론 ...
(여기에는 tl; dr을 넣고 싶지만 하나는 생각할 수 없습니다. : \)
글쎄, 거기에 최선을 다해 최선을 다했습니다. 당신이 가진 반론과 제안 된 디자인이 내 자신의 디자인보다 유리하다고 생각하는 장점을 게시하십시오. 기본적으로 내가 틀렸다는 것을 보여주십시오. :)