주조
이것은 확실히 인용 된 책의 접근 방식과 완전히 접하게 될 것이지만, ISP에 더 잘 부합하는 한 가지 방법은 QueryInterface
COM 스타일 접근 방식을 사용하여 코드베이스의 한 중앙 영역에 캐스팅 사고 방식을 수용하는 것 입니다.
순수한 인터페이스 환경에서 겹치는 인터페이스를 디자인하려는 많은 유혹은 종종 정확하고 저격적인 책임을 수행하는 것보다 인터페이스를 "자급 자족"하도록 만드는 욕구에서 비롯됩니다.
예를 들어 다음과 같은 클라이언트 기능을 설계하는 것이 이상하게 보일 수 있습니다.
// Returns the absolute position of an entity as the sum
// of its own position and the position of its ancestors.
// `position` and `parenting` parameters should point to the
// same object.
Vec2i abs_position(IPosition* position, IParenting* parenting)
{
const Vec2i xy = position->xy();
auto parent = parenting->parent();
if (parent)
{
// If the entity has a parent, return the sum of the
// parent position and the entity's local position.
return xy + abs_position(dynamic_cast<IPosition*>(parent),
dynamic_cast<IParenting*>(parent));
}
return xy;
}
...이 인터페이스를 사용하여 클라이언트 코드에 오류가 발생하기 쉬운 캐스팅을 수행하거나 인수로 동일한 객체를 동일한 매개 변수의 여러 매개 변수에 여러 번 전달해야 할 책임이 있음을 감안할 때 상당히 추악하고 위험합니다. 함수. 우리는 결국 그래서 종종 더 희석의 우려 통합 인터페이스 설계를 원하는 IParenting
과 IPosition
같이 한 곳에서를 IGuiElement
다음 마찬가지로 더 많은 멤버 함수를 유혹한다 직교 인터페이스의 우려 중복에 취약하게되는 그런이나 뭐 동일한 "자급 자족"이유.
혼합 책임과 캐스팅
완전히 증류되고 초단 수 책임이있는 인터페이스를 설계 할 때 종종 여러 다운 로스팅을 수락하거나 인터페이스를 통합하여 여러 책임을 수행해야합니다 (따라서 ISP와 SRP 모두에서 진행).
COM 스타일 접근 방식 ( QueryInterface
일부만 사용)을 사용하여 다운 캐스팅 접근 방식을 사용하지만 코드베이스에서 하나의 중앙 위치로 캐스팅을 통합하고 다음과 같은 작업을 수행 할 수 있습니다.
// Returns the absolute position of an entity as the sum
// of its own position and the position of its ancestors.
// `obj` should implement `IPosition` and optionally `IParenting`.
Vec2i abs_position(Object* obj)
{
// `Object::query_interface` returns nullptr if the interface is
// not provided by the entity. `Object` is an abstract base class
// inherited by all entities using this interface query system.
IPosition* position = obj->query_interface<IPosition>();
assert(position && "obj does not implement IPosition!");
const Vec2i xy = position->xy();
IParenting* parenting = obj->query_interface<IParenting>();
if (parenting && parenting->parent()->query_interface<IPosition>())
{
// If the entity implements IParenting and has a parent,
// return the sum of the parent position and the entity's
// local position.
return xy + abs_position(parenting->parent());
}
return xy;
}
... 물론 안전한 유형의 래퍼와 원시 포인터보다 안전한 것을 얻기 위해 중앙에서 빌드 할 수있는 모든 것이 있으면 좋겠습니다.
이를 통해 겹치는 인터페이스를 디자인하려는 유혹이 종종 최소로 완화됩니다. ISP에 대해 걱정할 필요없이 C ++로 런타임에 의사 덕 타이핑의 유연성을 얻을 수있는 유연성을 얻을 수있는 매우 단일 한 책임 (때로는 하나의 멤버 함수 만 있음)을 갖는 인터페이스를 설계 할 수 있습니다. 객체가 특정 인터페이스를 지원하는지 확인하기 위해 객체를 쿼리하는 런타임 페널티의 상충 관계). 런타임 부분은 함수가 이러한 인터페이스를 구현하는 플러그인의 컴파일 타임 정보를 미리 가지고 있지 않은 소프트웨어 개발 키트를 사용한 설정에서 중요 할 수 있습니다.
템플릿
템플릿이 가능한 경우 (필요한 컴파일 타임 정보가 사전에 있으며, 개체를 잡을 때까지 손실되지 않습니다.) 다음과 같이하면됩니다.
// Returns the absolute position of an entity as the sum
// of its own position and the position of its ancestors.
// `obj` should have `position` and `parent` methods.
template <class Entity>
Vec2i abs_position(Entity& obj)
{
const Vec2i xy = obj.xy();
if (obj.parent())
{
// If the entity has a parent, return the sum of the parent
// position and the entity's local position.
return xy + abs_position(obj.parent());
}
return xy;
}
... 물론 그러한 경우 parent
메소드는 동일한 Entity
유형 을 반환해야합니다 .이 경우 인터페이스를 완전히 피하고 싶을 것입니다 (기본 포인터로 작업하기 위해 유형 정보를 종종 잃기를 원하기 때문에).
엔터티 구성 요소 시스템
유연성이나 성능 관점에서 COM 스타일 접근 방식을 추구하기 시작하면 업계에서 게임 엔진이 적용되는 것과 유사한 엔터티 구성 요소 시스템이 생길 수 있습니다. 이 시점에서 많은 객체 지향 접근 방식과 완전히 직각을 이룰 수 있지만 ECS는 GUI 디자인에 적용 할 수 있습니다. COM 스타일의 접근 방식을 사용하여 시도하십시오).
이 COM 스타일 솔루션은 GUI 툴킷 디자인이 진행되는 한 완전히 존재하며 ECS는 훨씬 더 많아 질 것이므로 많은 리소스가 뒷받침하는 것이 아닙니다. 그러나 겹치는 책임을 가진 인터페이스를 디자인하려는 유혹을 확실히 완화시켜 종종 비 관심으로 만듭니다.
실용적 접근
대안은, 물론, 가드 조금 휴식을 취하거나 세부적인 수준에서 인터페이스를 설계 한 다음 사용하는 것이 거친 인터페이스를 만들 수를 상속 시작입니다 같은 IPositionPlusParenting
어떤 모두에서 파생 IPosition
및IParenting
(바람직하게 그보다 나은 이름으로). 순수한 인터페이스를 사용하면 일반적으로 적용되는 모 놀리 식 심층 계층 적 접근 방식 (Qt, MFC 등)만큼 ISP를 위반해서는 안됩니다. 실용적인 접근 방식은 단순히 여기저기서 약간의 중복을 받아 들일 수 있습니다. 그러나 이러한 종류의 COM 스타일 방식은 사용할 모든 조합에 대해 통합 된 인터페이스를 만들 필요가 없습니다. 이러한 경우 "자급 자족"문제는 완전히 제거되며, SRP와 ISP 모두와 싸우고 자하는 책임이 겹치는 인터페이스를 설계하려는 궁극적 인 유혹의 원천을 종종 제거합니다.