Döküm
Bu neredeyse alıntılanan kitabın yaklaşımına tam bir teğet olacaktır, ancak ISS'ye daha iyi uyum sağlamanın bir yolu, QueryInterface
COM-tarzı bir yaklaşım kullanarak kod tabanınızın bir merkezi alanında bir döküm zihniyetini benimsemektir.
Saf bir arayüz bağlamında örtüşen arayüzler tasarlama cazibesinin çoğu, arayüzleri kesin, keskin nişancı benzeri bir sorumluluk yerine getirmekten daha fazla "kendi kendine yeterli" yapma arzusundan gelir.
Örneğin, aşağıdaki gibi istemci işlevleri tasarlamak garip gelebilir:
// 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;
}
... ve bu arayüzleri kullanarak ve / veya aynı nesneyi bir argüman olarak aynı nesneyi birden çok parametreye birden çok kez geçirerek hataya açık döküm yapma sorumluluğunu kaçırdığımız göz önüne alındığında, oldukça çirkin / tehlikeli. işlevi. Biz sonuna kadar Yani genellikle daha sulandırılmış endişelerini birleştirir arayüz tasarlamayı isteyen IParenting
ve IPosition
benzeri tek bir yerde, IGuiElement
o zaman aynı şekilde daha üye işlevlere sahiptir için cazip olacak ortogonal arayüzleri kaygıları ile örtüşen duyarlı hale geldiği veya böyle bir şey aynı "kendi kendine yeterlilik" sebebi.
Döküm Sorumlulukları ve Döküm
Tamamen damıtılmış, ultra-tekil bir sorumluluğa sahip arayüzler tasarlarken, cazibe genellikle birden fazla sorumluluğu yerine getirmek için bazı downcasting'i kabul etmek veya arayüzleri birleştirmek olacaktır (ve bu nedenle hem ISP hem de SRP üzerinde yürümek).
COM tarzı bir yaklaşım (sadece bir QueryInterface
parça) kullanarak, indirgeme yaklaşımıyla oynarız, ancak dökümünü kod tabanında tek bir merkezi yere birleştiririz ve bunun gibi bir şey yapabiliriz:
// 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;
}
... tabii ki tip güvenli ambalajlar ve ham işaretçilerden daha güvenli bir şey elde etmek için merkezi olarak inşa edebileceğiniz her şeyle umarım.
Bununla, örtüşen arayüzler tasarlama cazibesi genellikle mutlak minimum seviyeye indirgenir. ISS hakkında endişelenmeden istediğinizi karıştırabileceğiniz ve eşleştirebileceğiniz ve C ++ 'da çalışma zamanında yalancı ördek yazmanın esnekliğini elde edebileceğiniz çok tekil sorumluluklara (bazen sadece bir üye işlevi) sahip arayüzler tasarlamanıza izin verir. belirli bir arabirimi destekleyip desteklemediklerini görmek için nesneleri sorgulamak için çalışma zamanı cezalarının takas edilmesi). Çalışma zamanı kısmı, işlevlerin bu arayüzleri uygulayan eklentilerin derleme zamanı bilgilerine sahip olmayacağı bir yazılım geliştirme kitine sahip bir ayarda önemli olabilir.
Şablonlar
Şablonlar bir olasılıksa (bir nesneyi ele geçirdiğimiz zaman kaybolmayan gerekli derleme zamanı bilgisine önceden sahibiz), o zaman bunu yapabiliriz:
// 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;
}
... elbette böyle bir durumda, parent
yöntem aynı Entity
türü döndürmek zorunda kalacaktı , bu durumda muhtemelen arayüzlerden açıkça kaçınmak istiyoruz (çünkü genellikle temel işaretçilerle çalışmak lehine tip bilgilerini kaybetmek isteyeceklerdir).
Varlık Bileşen Sistemi
COM tarzı yaklaşımı esneklik veya performans açısından daha ileriye götürmeye başlarsanız, genellikle oyun motorlarının endüstride uyguladığı şeye benzer bir varlık bileşeni sistemi elde edersiniz. Bu noktada, birçok nesne yönelimli yaklaşıma tamamen dik olacaksınız, ancak ECS, GUI tasarımına uygulanabilir (sahne odaklı bir odak dışında ECS'yi kullanmayı düşündüğüm bir yer, ancak daha sonra çok geç kabul edildi) orada denemek için COM tarzı bir yaklaşımı benimsemek).
Bu COM tarzı çözümün GUI araç seti tasarımları gittikçe tamamen dışarıda olduğunu ve ECS'nin daha da fazla olacağını unutmayın, bu yüzden birçok kaynak tarafından desteklenecek bir şey değildir. Yine de, üst üste gelen sorumlulukları mutlak minimumda olan, genellikle endişe duymayan arayüzler tasarlama cazibelerini azaltmanıza kesinlikle izin verecektir.
Pragmatik Yaklaşım
Alternatif, elbette, sizin bekçi biraz dinlenmek veya ayrıntılı düzeyde arayüzler tasarlayabilir ve sonra, kullandıkları kaba arayüzleri oluşturmak için bunları miras başlamak gibi IPositionPlusParenting
hangi ikisi türetilmiştir IPosition
veIParenting
(umarım bundan daha iyi bir isimle). Saf arabirimlerle, ISS'nin yaygın olarak uygulanan monolitik derin hiyerarşik yaklaşımlar kadar (Qt, MFC, vb.), Belgelerin genellikle bu türlerle aşırı ISP'yi ihlal ettiği göz önüne alındığında ilgisiz üyeleri gizleme ihtiyacı hissettiği kadar ihlal etmemelidir. pragmatik bir yaklaşım burada ve orada bir miktar örtüşmeyi kabul edebilir. Ancak bu tür COM tarzı yaklaşım, kullanacağınız her kombinasyon için birleştirilmiş arayüzler oluşturma ihtiyacını ortadan kaldırır. Bu tür durumlarda "kendi kendine yeterlilik" kaygısı tamamen ortadan kaldırılmıştır ve bu, hem SRP hem de ISS ile savaşmak isteyen örtüşen sorumluluklara sahip tasarım arayüzlerine yönelik nihai cazip kaynağını ortadan kaldıracaktır.