Kodumda “yöneticilerden” nasıl kaçınılır?


26

Şu anda Varlık Sistemimi C ++ için yeniden tasarlıyorum ve bir çok Müdürüm var. Tasarımımda, kütüphanemi birbirine bağlamak için bu sınıflara sahibim. "Yönetici" dersleri söz konusu olduğunda çok kötü şeyler duydum, belki de sınıflarımı uygun şekilde adlandırmıyorum. Ancak, onları başka ne adlandıracağım hakkında hiçbir fikrim yok.

Kütüphanemdeki yöneticilerin çoğu bu sınıflardan oluşuyor (biraz değişmesine rağmen):

  • Kapsayıcı - yöneticideki nesneler için bir kapsayıcı
  • Nitelikler - yöneticideki nesnelerin nitelikleri

Kütüphanem için yeni tasarımımda kütüphanemi bağlamak için bu özel sınıflara sahibim.

  • ComponentManager - Varlık Sistemindeki bileşenleri yönetir

    • ComponentContainer
    • ComponentAttributes
    • Sahne * - bir Sahneye referans (aşağıya bakınız)
  • SystemManager - Varlık Sistemindeki sistemleri yönetir

    • SystemContainer
    • Sahne * - bir Sahneye referans (aşağıya bakınız)
  • EntityManager - Varlık Sistemindeki varlıkları yönetir

    • EntityPool - bir varlık havuzu
    • EntityAttributes - Bir işletmenin özellikleri (bu, yalnızca ComponentContainer ve System sınıfları için erişilebilir olacak)
    • Sahne * - bir Sahneye referans (aşağıya bakınız)
  • Sahne - tüm yöneticileri bir araya getirir

    • ComponentManager
    • Sistem Müdürü
    • EntityManager

Tüm konteyner / havuzları sadece Scene sınıfına yerleştirmeyi düşünüyordum.

yani

Bunun yerine:

Scene scene; // create a Scene

// NOTE:
// I technically could wrap this line in a createEntity() call in the Scene class
Entity entity = scene.getEntityManager().getPool().create();

Bu olurdu:

Scene scene; // create a Scene

Entity entity = scene.getEntityPool().create();

Ancak, emin değilim. Eğer ikincisini yapacak olsaydım, bu, Scene sınıfımın içinde ilan ettiğim birçok nesne ve yöntem olacağı anlamına gelirdi.

NOTLAR:

  1. Bir varlık sistemi sadece oyunlar için kullanılan bir tasarımdır. Bileşenler, varlıklar ve sistemler: 3 ana bölümden oluşur. Bileşenler, varlıkların ayırt edici olabilmesi için varlıklara "eklenebilir" basit verilerdir. Bir varlık bir tamsayı ile temsil edilir. Sistemler, belirli bileşenlere sahip bir varlığın mantığını içerir.
  2. Tasarımımı kütüphanem için değiştirmemin sebebi, çok fazla değiştirilebileceğini düşünüyorum, şu anda ona olan his / akışı sevmiyorum.

Lütfen yöneticiler hakkında duydukları kötü şeyleri ve seninle nasıl ilgilendiklerini açıklayabilir misin?
MrFox


@MrFox Temelde Dunk'ın bahsettiğini duydum ve kütüphanemde kurtulmak istediğim birçok * Yönetici sınıfı var.
miguel.martin

Bu da yardımcı olabilir: medium.com/@wrong.about/…
Zapadlo

Çalışan bir uygulamadan yararlı bir çerçeve çarparsınız. Hayali uygulamalar için faydalı bir çerçeve yazmak neredeyse imkansız.
kevin cline

Yanıtlar:


19

DeadMG kodunuzun özellikleri üzerinde durmaktadır ancak bir açıklamayı özlüyor gibi hissediyorum. Ayrıca, örneğin, en yüksek performans-video oyunu geliştirme gibi belirli bağlamlarda bulunmayan bazı tavsiyelerine katılmıyorum. Ancak, şu anda kodunuzun çoğunun kullanışlı olmadığı konusunda küresel olarak haklı.

Dunk'un dediği gibi, Yönetici sınıfları böyle adlandırılır çünkü işleri “yönetir”. "yönetmek", "veri" veya "yapmak" gibidir, hemen hemen her şeyi içerebilen soyut bir kelimedir.

Yaklaşık 7 yıl önce sizinle aynı yerdeydim ve düşünme biçimimde yanlış bir şey olduğunu düşünmeye başladım, çünkü henüz hiçbir şey yapmama konusunda çok fazla çaba harcadım.

Bunu düzeltmek için değiştirdiğim şey, kodda kullandığım kelimeleri değiştirmek . Genel kelimelerden tamamen kaçınırım (genel kod olmadıkça, ancak genel kitaplıklar oluşturmadığınız zaman nadirdir). Ben her türlü "Yöneticisi" isim kaçınmak ya da "nesne".

Etkisi doğrudan koddadır: sizi türünüzün gerçek sorumluluğuna karşılık gelen doğru sözcüğü bulmaya zorlar. Türün birkaç şey yaptığını düşünüyorsanız (bir Kitap dizini tutun, onları canlı tutun, kitaplar oluşturun / yok edin), o zaman her birinin bir sorumluluğu olacak farklı türlere ihtiyacınız olur, sonra onları bunları kullanan kodda birleştirirsiniz. Bazen bir fabrikaya ihtiyacım yok, bazen değil. Bazen bir sicile ihtiyacım var, o yüzden bir tane hazırladım (standart kaplar ve akıllı işaretçiler kullanarak). Bazen birkaç farklı alt sistemden oluşan bir sisteme ihtiyacım var, böylece her şeyi elimden geldiğince ayırırım, böylece her bölüm yararlı bir şey yapabilir.

Bir tür "müdür" adını ve emin tüm tip tek bir benzersiz bir rol var olun asla benim tavsiye olmasıdır. Bazen isimleri bulmak zor olabilir, ama genel olarak programlamada yapılması en zor şeylerden biri.


Eğer bir çift dynamic_castperformansınızı kaybediyorsa, o zaman statik tip sistemi kullanın - bunun için var. LLVM'nin yaptığı gibi kendi RTTI'nuzu yuvarlamak zorunda olmak, genel değil, özel bir içeriktir. O zaman bile, bunu yapmıyorlar.
DeadMG

@DeadMG Özellikle bu kısımdan bahsetmiyordum, ancak çoğu yüksek performanslı oyun, örneğin, genel olarak programlama için iyi olan yeni veya hatta diğer şeyler yoluyla dinamik bir şekilde tahsis edemez. Bunlar genelleştiremediğiniz belirli donanımlar için belirli canavarlardır; bu nedenle "bağlıdır", özellikle C ++ ile daha genel bir cevaptır. (dev oyundaki hiç kimse bu arada dinamik oyuncular kullanmaz, eklentileri olana kadar asla faydalı olmaz ve o zaman bile nadirdir).
Klaim

@DeadMG dynamic_cast kullanmıyorum veya dynamic_cast'e bir alternatif kullanmıyorum. Kütüphanede uygulamıştım ama çıkarmaktan rahatsız olamadım. template <typename T> class Class { ... };aslında farklı sınıflar (yani özel bileşenler ve özel sistemler) için kimlik atamak içindir. Kimlik tayini, bir vektörde bileşenleri / sistemleri depolamak için kullanılır, çünkü bir harita bir oyun için ihtiyacım olan performansı getirmeyebilir.
miguel.martin

Aslında dynamic_cast için bir alternatif de uygulamadım, sadece sahte bir type_id. Ama yine de type_id'nin elde edilmesini istemedim.
miguel.martin

"türünüzün gerçek sorumluluğuna karşılık gelen doğru sözcüğü bulun" - buna tamamen katılıyorum, ancak geliştirici topluluğunun belirli bir sorumluluk türü için kararlaştırdığı tek bir kelime (varsa) yoktur.
bytedev

23

Bağlandığınız bazı kodları ve görevlerinizi okudum ve dürüst özetim, çoğunluğunun temelde tamamen değersiz olduğu. Üzgünüm. Demek istediğim, bu kodun hepsine sahipsin ama hiçbir şey elde etmedin. Hiç. Burada biraz derinliğe girmek zorunda kalacağım, o yüzden benimle ayı.

ObjectFactory ile başlayalım . İlk olarak, işlev işaretçileri. Bunlar vatansız, nesne yaratmanın tek kullanışlı vatansız yolu newve bunun için bir fabrikaya ihtiyacınız yok. Nihayetinde bir nesneyi oluşturmak yararlıdır ve işlev işaretçileri bu görev için kullanışlı değildir. İkincisi, her nesnenin kendini nasıl yok edeceğini bilmesi gerekir. , onu yok etmek için kesin yaratıcı örneği bulmak zorunda değil. Ayrıca, kullanıcınızın istisna ve kaynak güvenliği için, ham işaretçiyi değil, akıllı işaretçiyi döndürmeniz gerekir. Tüm ClassRegistryData sınıfınız sadece std::function<std::unique_ptr<Base, std::function<void(Base*)>>()>, ancak yukarıda belirtilen deliklerle. Hiç olması gerekmez.

Öyleyse AllocatorFunctor var - zaten standart Allocator arayüzleri sağlandı. delete obj; ve return new T;- yine zaten sağlanır ve bunlar değil etkileşim mevcut tüm durum bilgisi veya özel bellek allocators ile olacak.

Ve ClassRegistry basit std::map<std::string, std::function<std::unique_ptr<Base, std::function<void(Base*)>>()>> ama mevcut işlevselliği kullanmak yerine bunun için bir sınıf . Aynı, ama tamamen gereksiz yere bir sürü berbat makro ile küresel değişebilir durum.

Burada söylediğim şey, her şeyi Standart sınıflardan inşa edilen işlevsellik ile değiştirebileceğiniz bazı sınıflar yarattığınız ve daha sonra onları küresel değişebilir hale getirerek ve bir sürü makro içerek daha da kötüleştiren bazı sınıflar yarattığınızdır. yapabileceğin en kötü şey.

Öyleyse tanımlanabiliriz . Buradaki aynı hikayenin birçoğu - std::type_infozaten her bir türü çalışma zamanı değeri olarak tanımlama işini gerçekleştiriyor ve kullanıcının kısmında aşırı fazladan baskı gerektirmiyor.

    template<typename T, typename Y>
    bool isSameType(const X& x, const Y& y) { return typeid(x) == typeid(y); }
    template <class Type, class T>
    bool isType(const T& obj)
    {
            return dynamic_cast<const Type*>(std::addressof(obj));
    }

Sorun çözüldü, ancak önceki iki yüzlü makrolardan herhangi biri olmadan. Bu aynı zamanda Tanıtıcı Array'ınızı hiçbir şey olarak işaretlerstd::vector benzersiz mülkiyet veya sahip olmama durumu daha iyi olsa bile referans saymayı kullanmanız gerekir.

Oh, ve Çeşitlerin kesinlikle işe yaramaz bir demet içerdiğinden bahsettim mi? typedef miydim? Ham türleri doğrudan kullanmanın hiçbir anlamıyla avantajı elde edemezsiniz ve iyi bir okunurluk kaybı yaşarsınız.

Sıradaki ComponentContainer . Sahipliği seçemezsiniz, çeşitli bileşenlerin türetilmiş sınıfları için ayrı kaplara sahip olamazsınız, kendi ömür boyu semantiklerini kullanamazsınız. Pişmanlık duymadan sil.

Şimdi, ComponentFilter çok fazla değerlendiriyorsa , aslında biraz değer olabilir. Gibi bir şey

class ComponentFilter {
    std::unordered_map<std::type_info, unsigned> types;
public:
    template<typename T> void SetTypeRequirement(unsigned count = 1) {
        types[typeid(T)] = count;
    }
    void clear() { types.clear(); }
    template<typename Iterator> bool matches(Iterator begin, Iterator end) {
        std::for_each(begin, end, [this](const std::iterator_traits<Iterator>::value_type& t) {
            types[typeid(t)]--;
        });
        return types.empty();
    }
};

Bu, uğraşmaya çalıştığınız sınıflardan türetilmiş derslerle ilgilenmez, aynı zamanda sizinkileri de yapmaz.

Bunun geri kalanı hemen hemen aynı hikaye. Burada, kütüphanenizi kullanmadan daha iyi yapılamayacak hiçbir şey yoktur.

Bu kodda yöneticileri nasıl önlersiniz? Temel olarak hepsini silin.


14
Öğrenmem biraz zaman aldı, ancak en güvenilir, hatasız kod parçaları orada olmayanlardı .
13'te Tacroy

1
Deniyordum çünkü std :: Type_info is kullanmıyordu sebebi önlemek RTTI. Ben framework ben kullanmıyorum nedeni temelde olan oyunlarda amaçlı olduğunu belirtilen typeidveya dynamic_cast. Geri bildiriminiz için teşekkürler, ancak bunu takdir ediyorum.
miguel.martin

Bu eleştiri, bileşen varlık sistemi oyun motorları hakkındaki bilginize dayanıyor mu, yoksa genel bir C ++ kod incelemesi mi?
Den

1
@Den bir tasarım sorunu başka her şeyden daha fazla düşünüyorum.
miguel.martin

10

Yönetici sınıflarındaki problem, yöneticinin her şeyi yapabilmesidir. Sınıf adını okuyamaz ve sınıfın ne yaptığını bilemezsiniz. EntityManager .... Ben Varlıklar ile bir şeyler yaptığını biliyorum ama kim bilir. SystemManager ... evet, sistemi yönetir. Bu çok yardımcı oldu.

Tahminimce, menajer sınıfların muhtemelen birçok şey yapıyor. Bu şeyleri yakından ilişkili işlevsel parçalara ayırırsanız bahse girerim, o zaman muhtemelen herkesin basitçe sınıf adına bakarak ne yapabileceğini söyleyebileceği fonksiyonel parçalarla ilgili sınıf isimleri bulabileceksiniz. Bu, tasarımı korumak ve anlamak için çok daha kolay hale getirecektir.


1
+1 ve sadece bir şey yapamazlar, yapacakları uzun zaman çizelgesinde. İnsanlar "Yönetici" tanımlayıcısını, mutfak lavabosu / İsviçre ordusu bıçak sınıfları oluşturma daveti olarak görüyorlar.
Erik Dietrich

@Erik - Ah evet, iyi bir sınıf ismine sahip olmanın sadece önemli olmadığını çünkü birisinin sınıfın neler yapabileceğini söylediğini söylemeliydim; ama aynı şekilde sınıf ismi de sınıfın yapmaması gerekenleri söylüyor.
Dunk,

3

Aslında Managerbu bağlamda çok kötü olduğunu düşünmüyorum omuz silkme. Eğer Manageraffedilebileceğini düşündüğüm bir yer varsa , bileşenlerin, varlıkların ve sistemlerin yaşamlarını gerçekten " yönettiği " için varlık bileşen sistemi için olurdu . En azından buradakilerden daha anlamlı bir isim.Factory bu, bu bağlamda bir ECS'nin gerçekten bir şeyleri oluşturmaktan biraz daha fazlasını yapması nedeniyle anlam ifade etmeyen bir şeyden .

Kullanabileceğin varsayalım Databasegibi ComponentDatabase. Ya da sadece kullanabilirsiniz Components, Systemsve Entities(kişisel olarak kullandığım şey budur ve temelde sadece, belki de "Yönetici" den daha az bilgi aktarsa ​​da kısaca tanımlayıcıları severim).

Biraz sakar bulduğum bölüm bu:

Entity entity = scene.getEntityManager().getPool().create();

getBir varlık oluşturabilmeniz için önce yapılması gereken çok şey var ve varlık havuzları ve bunları oluşturmak için "yöneticiler" hakkında bilginiz varsa, tasarımın uygulama ayrıntılarını biraz sızdırıyormuş gibi hissettiriyor. Bence "havuzlar" ve "yöneticiler" gibi şeyler (eğer bu isme sadık kalırsanız) müşteriye açık bir şey değil, ECS'nin uygulama detayları olmalı.

Ama Bilmiyorum, ben bazıları çok yaratıcı olmayan ve jenerik kullanımlarını gördük Manager, Handler, Adapterbu tür ve isimlerin ama bu "Yönetici" olarak adlandırılan şey "kontrol ( "yönetmek" için aslında sorumlu olduğunda oldukça affedilebilir bir kullanım durumu olduğunu düşünüyorum? "," ele alma? ") nesnelerin yaşamları, bireysel olarak yaratma ve yok etme ve onlara erişim sağlama sorumluluğunu taşır. En azından benim için "Yönetici" nin en basit ve sezgisel kullanımı.

Bununla birlikte, adınızdaki “Yönetici” den kaçınmak için genel bir stratejinin “Yönetici” kısmını çıkarmanız ve -sen sonunda bir tane eklemeniz olduğunu söyledi . :-D Örneğin, bir ThreadManagerkonuya sahip olduğunuzu ve onunla ilgili konuların oluşturulmasından ve onlara bilgi vermekten ve bunlara erişmekten ve bunun gibi şeylerden sorumlu olduğunu varsayalım ThreadPool. Peki, bu durumda, sadece onu ara Threads. Yaratıcı olmayan biri olduğumda zaten yaptığım şey bu.

"Windows Gezgini" nin analog eşdeğeri "Dosya Yöneticisi" olarak adlandırıldığında şöyle hatırlıyorum:

görüntü tanımını buraya girin

Ve aslında bu durumda "Dosya Yöneticisi" nin "Yönetici" içermeyen daha iyi bir isim olsa bile "Windows Gezgini" nden daha açıklayıcı olduğunu düşünüyorum. Bu yüzden en azından lütfen isimlerinizle fazla eğlenme ve "ComponentExplorer" gibi şeyleri adlandırmaya başla. "ComponentManager" en azından ECS motorlarında çalışan biri olarak bu şeyin ne yaptığı hakkında bana makul bir fikir veriyor.


Kabul. Bir sınıf tipik yönetimsel görevleri için kullanılırsa (() "ateş" gözetim yok, () "işe" oluşturma ve "işçi" / higherup arayüzü, daha sonra adı Managersınıf sahip olabilir. Uygun olan tasarım sorunları, ancak ad yerinde değil
Justin Time 2 Reinstate Monica
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.