Tüm numaralandırmaları bir dosyaya dahil etmek ve birden fazla sınıfta kullanmak kötü bir uygulama mıdır?


12

Ben hevesli bir oyun geliştiriciyim, nadiren indie oyunlarında çalışıyorum ve bir süredir ilk başta kötü bir uygulama gibi görünen bir şey yapıyorum, ama gerçekten burada bazı deneyimli programcılardan cevap almak istiyorum.

Diyelim ki enumList.hoyunumda kullanmak istediğim tüm numaralandırmaları beyan ettiğim bir dosya var :

// enumList.h

enum materials_t { WOOD, STONE, ETC };
enum entity_t { PLAYER, MONSTER };
enum map_t { 2D, 3D };
// and so on.

// Tile.h
#include "enumList.h"
#include <vector>

class tile
{
    // stuff
};

Ana fikir, oyundaki tüm numaralandırmaları 1 dosyada beyan etmem ve daha sonra, kullanmam gereken dosyada bildirmek yerine, ondan belirli bir numaralandırma kullanmam gerektiğinde bu dosyayı içe aktarmamdır. Bunu bir şeyleri temizlediğinden yapıyorum, sayfaların yalnızca bir numaraya eriştiği için açık olması yerine her numaraya 1 yerde erişebiliyorum.

Bu kötü bir uygulama mıdır ve performansı herhangi bir şekilde etkileyebilir mi?


1
Kaynak yapısı performansı etkileyemez - numaralandırma nerede olursa olsun hala aynı şekilde derlenecektir. Bu gerçekten onları nereye koymanın en iyi olacağı hakkında bir soru ve küçük numaralandırmalar için bir dosya çok tabu gibi gelmiyor.
Steffan Donal

Daha aşırı durumlarda soruyordum, bir oyunun birçok numaralandırması olabilir ve oldukça büyük olabilirler, ancak yorumunuz için teşekkürler
Bugster

4
Uygulama performansını etkilemez, ancak derleme zamanı üzerinde olumsuz bir etkisi olacaktır. Örneğin, bir materyal eklerseniz, materyalle materials_tilgilenmeyen dosyalara yeniden inşa edilmesi gerekir.
Robotu

14
evinizdeki tüm sandalyeleri sandalye odasına koymak gibi, bu yüzden oturmak istiyorsanız nereye gideceğinizi biliyorsunuz.
kevin cline

Bir kenara, her bir numaralandırmayı kendi dosyasına koyarak ve bu dosyalar için bir s koleksiyonu olarak kolayca her ikisini de yapabilirsiniz . Bu, yalnızca bir numaraya ihtiyaç duyan dosyaların doğrudan almasını sağlarken, hepsini gerçekten isteyen her şey için tek bir paket sağlar. enumList.h#include
Justin Time - Monica'yı

Yanıtlar:


35

Gerçekten kötü bir uygulama olduğunu düşünüyorum. Kod kalitesini ölçtüğümüzde, "ayrıntı düzeyi" dediğimiz bir şey var. Ayrıntı düzeyiniz, tüm bu numaralandırmaları tek bir dosyaya koyarak ciddi şekilde acı çeker ve böylece sürdürülebilirlik de acı çeker.

Ben hızlı bir şekilde bulmak ve belirli işlevsellik davranış kodu (örneğin malzeme davranışı olduğu klasörde malzeme enum vb.) İle gruplandırma başına tek bir dosya olurdu;

Ana fikir, oyundaki tüm numaralandırmaları 1 dosyada beyan etmem ve daha sonra, kullanmak zorunda olduğum dosyada bildirmek yerine, ondan belirli bir numaralandırma kullanmam gerektiğinde bu dosyayı içe aktarmam. Bunu bir şeyleri temizlediğinden yapıyorum, sayfaların yalnızca bir numaraya eriştiği için açık olması yerine her numaraya 1 yerde erişebiliyorum.

Temiz olduğunu düşünebilirsiniz, ama aslında değil. İşlevsel ve modül bakımından birbirine ait olmayan şeyleri birleştirir ve uygulamanızın modülerliğini azaltır. Kod tabanınızın boyutuna ve kodunuzun ne kadar modüler olarak yapılandırılmasını istediğinize bağlı olarak, bu, sisteminizin diğer bölümlerinde daha büyük bir soruna ve belirsiz kod / bağımlılıklara dönüşebilir. Ancak, sadece küçük, yekpare bir sistem yazarsanız, bu mutlaka geçerli değildir. Yine de küçük bir monolitik sistem için bile böyle yapmazdım.


2
Ayrıntı düzeyini belirtmek için +1, diğer yanıtları dikkate aldım ama iyi bir noktaya değindiniz.
Bugster

Numara başına tek dosya için +1. biri için bulmak daha kolay.
mauris

11
Tek bir yerde kullanılan tek bir enum değeri eklemenin, projenizdeki her dosyanın yeniden oluşturulmasına neden olacağından bahsetmiyoruz.
Robotu

iyi tavsiye. sen yine de fikrimi değiştirdin .. ben her zaman CompanyNamespace.Enums gitmeyi ve kolay bir liste almak hoşuma gitti, ama kod yapısı disiplinli ise, o zaman yaklaşım daha iyi
Matt Evans

21

Evet, performanstan ziyade sürdürülebilirlik nedeniyle kötü uygulama.

Sadece OKB'de "benzer şeyleri bir araya toplar" şekilde "temiz" hale getirir. Ama bu aslında değil "temizlik" yararlı ve iyi bir tür.

Kod varlıkları, kohezyonu en üst düzeye çıkarmak ve kuplajı en aza indirgemek için gruplandırılmalıdır ; bu en iyi şekilde büyük ölçüde bağımsız fonksiyonel modüllere gruplanarak elde edilir. Bunları teknik kriterlere göre gruplamak (tüm numaralandırmaların bir araya getirilmesi gibi) tam tersini sağlar - kesinlikle işlevsel bir ilişkisi olmayan kodu çiftler ve yalnızca tek bir yerde kullanılabilecek sıralamaları farklı bir dosyaya koyar.


3
Doğru; 'sadece OKB'de' benzer şeyleri bir araya topla 'yolu'. Mümkünse 100 upvotes.
Dogweather

Yazabilseydim yazımı düzenlemeye yardımcı olurdum, ama SE 'edit' eşiğini aşmak için yeterli hata yapmadınız. :-P
Dogweather

hemen hemen tüm web çerçevelerinin, özellik yerine dosya türüne göre toplanmasını gerektirmesi ilginçtir.
kevin cline

@kevincline: "Neredeyse hepsi" demezdim - sadece konfigürasyon üzerinde konvansiyona dayananlar ve tipik olarak, kodların işlevsel olarak gruplandırılmasına izin veren bir modül konseptine de sahipler.
Michael Borgwardt

8

Bu sadece veri (davranış değil). Muhtemelen / teorik olarak olabilecek en kötü şey, aynı kodun göreceli olarak daha büyük bir program üretmek için birden fazla kez dahil edilmesi ve derlenmesidir.

İçinde herhangi bir davranışsal / prosedürel kod olmadığı (döngüler yok, ifs vb. Yok) göz önüne alındığında, bu tür işlemlerin operasyonlarınıza daha fazla döngü ekleyebilmesi imkansızdır.

(Nispeten) daha büyük bir program performansları (yürütme hızını) zorlukla etkileyebilir ve her durumda, sadece uzak bir teorik endişe kaynağıdır. Çoğu (belki de tüm) derleyiciler bu tür sorunları önleyecek şekilde içerir.

IMHO, tek bir dosya (daha okunabilir ve daha yönetilebilir kod) ile elde edeceğiniz avantajlar büyük olasılıkla olası dezavantajları aşmaktadır.


5
Tüm popüler cevapların görmezden geldiğini düşünüyorum - değişmez verilerin hiç bir bağlantısı yok.
Michael Shaw

3
Sanırım derlemek için yarım saat veya daha uzun sürecek projeler üzerinde çalışmak zorunda kalmadınız. Tüm numaralandırmalarınız tek bir dosyada varsa ve tek bir numaralandırmayı değiştirirseniz, uzun bir mola zamanı. Heck, sadece bir dakika sürse bile, bu hala çok uzun.
Smaç

2
Modül X altında sürüm kontrolü altında çalışan herkes, her çalışmak istediklerinde X modülünü ve numaralandırmayı almalıdır. Ayrıca, enum dosyasını her değiştirdiğinizde, değişikliğiniz projenizdeki her bir modülü potansiyel olarak etkiliyorsa bana bir bağlantı şekli olarak dikkat çekiyor. Projeniz, ekip üyelerinin tamamının veya çoğunun projenin her parçasını anlamadığı kadar büyükse, küresel bir numaralandırma oldukça tehlikelidir. Küresel değişmez bir değişken, küresel değişebilir bir değişken kadar kötü değildir, ancak yine de ideal değildir. Bununla birlikte, küresel bir numaralandırma muhtemelen 10'dan az üyesi olan bir ekipte iyi durumda.
Brian

3

Benim için her şey projenizin kapsamına bağlıdır. 10 yapıları olan bir dosya varsa ve şimdiye kadar kullanılan tek dosya varsa, bunun için bir .h dosyasına sahip olmak çok rahat olurdu. Eğer bir birim, ekonomi, binalar, vb çeşitli işlevsellik türleri varsa, ben kesinlikle şeyler bölmek istiyorum. Birimlerle ilgilenen tüm yapıların bulunduğu bir units.h dosyası oluşturun. Bir yerde birimlerle bir şey yapmak istiyorsanız, units.h emin olmanız gerekir, ancak aynı zamanda birimleri olan bir şeyin muhtemelen bu dosyada yapılması hoş bir "tanımlayıcı" dır.

Bu şekilde bak, bir süpermarket almıyorsun çünkü bir kola kutusuna ihtiyacın var;)


3

Ben bir C ++ dev değilim, bu yüzden bu cevap OOA & D için daha genel olacak:

Tipik olarak, kod nesneleri mutlaka dile özgü yapılar açısından değil, işlevsel alaka düzeyi açısından gruplandırılmalıdır. Her zaman sorulması gereken anahtar evet-hayır sorusu, "son kodlayıcıdan bir kütüphane tüketirken aldıkları nesnelerin çoğunu veya tamamını kullanması beklenecek mi?" Evet ise, gruplandırın. Değilse, kod nesnelerini bölmeyi ve bunları gerektiren diğer nesnelere daha yakın yerleştirmeyi düşünün (böylece tüketicinin gerçekten eriştikleri her şeye ihtiyaç duyma olasılığını artırın).

Temel kavram "yüksek uyum" dur; kod üyeleri (bir sınıf yöntemlerinden bir ad alanındaki veya DLL'deki sınıflara ve DLL'lerin kendilerine kadar), bir kodlayıcının ihtiyaç duydukları her şeyi içerebilecekleri ve hiçbir şey içermeyecek şekilde düzenlenmelidir. Bu, genel tasarımı değişime daha toleranslı kılar; değişmesi gereken şeyler, değiştirmek zorunda olmayan diğer kod nesnelerini etkilemeden olabilir. Birçok durumda uygulamayı bellekte daha verimli hale getirir; bu komutların ne kadarının işlem tarafından yürütüldüğüne bakılmaksızın, bir DLL'nin tamamı belleğe yüklenir. Bu nedenle "yalın" uygulamalar tasarlamak, belleğe ne kadar kod çekildiğine dikkat etmeyi gerektirir.

Bu kavram, sürdürülebilirlik, bellek verimliliği, performans, oluşturma hızı, vb. Üzerinde değişen etki derecelerine sahip neredeyse tüm kod organizasyon düzeylerinde geçerlidir. ki bu DLL'deki başka herhangi bir nesneye bağlı değilse, o nesnenin DLL'e eklenmesi muhtemelen yeniden düşünülmelidir. Ancak başka yoldan da ileri gitmek mümkündür; her sınıf için bir DLL kötü bir fikir, çünkü inşa hızını yavaşlatır (ilişkili ek yük ile yeniden oluşturmak için daha fazla DLL) ve sürüm kabus yapar.

Durumda: kod kitaplığınızın gerçek dünya kullanımı bu tek "enumerations.h" dosyasına koyduğunuz numaralandırmaların çoğunu veya tamamını kullanmayı içeriyorsa, bunları elbette bir arada gruplandırın; onları nerede bulacağınızı bileceksiniz. Ancak tüketen kodlayıcınız, başlıkta sağladığınız düzine numaralandırmadan yalnızca bir veya ikisine ihtiyaç duyabiliyorsa, bunları ayrı bir kütüphaneye koymanızı ve sayımların geri kalanıyla daha büyük bir bağımlılığa dönüştürmenizi öneririm. . Bu, kodlayıcılarınızın daha monolitik DLL'ye bağlanmadan istedikleri bir veya iki tanesini almasını sağlar.


2

Aynı kod tabanında çalışan birden fazla geliştiriciniz olduğunda bu tür bir sorun haline gelir.

Kesinlikle benzer küresel dosyaların birleşme çarpışmaları ve (daha büyük) projelerde her türlü keder için bir bağ olduğunu gördüm.

Bununla birlikte, proje üzerinde çalışan tek kişi sizseniz, o zaman en rahat olduğunuz şeyi yapmalı ve sadece arkasındaki motivasyonu anlıyorsanız (ve kabul ediyorsanız) "en iyi uygulamaları" benimsemelisiniz.

Bazı hatalar yapmak ve onlardan öğrenmek, hayatınızın geri kalanı için kargo kült programlama uygulamalarına takılma riskinden daha iyidir.


1

Evet, büyük bir projede bunu yapmak kötü bir uygulamadır. ÖPMEK.

Genç iş arkadaşı, bir çekirdek .h dosyasında basit bir değişkeni yeniden adlandırdı ve 100 mühendis, tüm dosyalarının yeniden oluşturulması için 45 dakika bekledi, bu da herkesin performansını etkiledi. ;)

Tüm projeler küçük başlar, yıllar içinde balonlaşır ve teknik borç yaratmak için erken kısayollar alanlara lanet ediyoruz. En iyi uygulamalar, global .h içeriğini zorunlu olarak küresel olanlarla sınırlı tutun.


Birisi (genç veya yaşlı, aynı) herkesin projeyi yeniden inşa etmesini sağlayabiliyorsa, gözden geçirme sistemini kullanmıyorsunuz, öyle değil mi?
Sanctus

1

Bu durumda, ben İdeal cevabım enums tüketilen nasıl bağlı olduğunu, ancak çoğu durumda bu kadar söyleyebilirim muhtemelen ayrı ayrı tüm Çeteleler tanımlamak için en iyi, ama bunların hiçbirini zaten tasarımdan birleştiğinde ise, bir sağlamalıdır adı geçen birleştirilmiş numaralandırmaların birlikte sunulması için araçlar. Aslında, zaten mevcut olan kasıtlı kuplaj miktarına kadar bir kuplaj toleransınız var, ancak artık yok.

Bunu göz önünde bulundurarak, en esnek çözümün her bir numaralandırmayı ayrı bir dosyada tanımlaması muhtemeldir, ancak bunun mantıklı olduğu durumlarda (ilgili numaralandırmaların amaçlanan kullanımına göre belirlendiği şekilde) birleştirilmiş paketler sağlar.


Tüm numaralandırmalarınızı aynı dosyada tanımlamak, onları bir araya getirir ve uzantı tarafından, kodun başka numaralandırmalar kullanıp kullanmadığına bakılmaksızın, bir veya daha fazla numaralandırmaya bağlı olan kodların tüm numaralandırmalara bağlı olmasına neden olur .

#include "enumList.h"

// Draw map texture.  Requires map_t.
// Not responsible for rendering entities, so doesn't require other enums.
// Introduces two unnecessary couplings.
void renderMap(map_t, mapIndex);

renderMap()daha çok bilmek isterdim map_t, çünkü aksi takdirde diğerleriyle herhangi bir değişiklik, aslında diğerleriyle etkileşime girmese bile onu etkileyecektir.

#include "mapEnum.h" // Theoretical file defining map_t.

void renderMap(map_t, mapIndex);

Bununla birlikte, bileşenlerin zaten bir araya getirildiği durumda, tek bir pakette birden fazla numaralandırma sağlamak, numaralandırmaların birleştirilmesi için açık bir mantıklı neden olması koşuluyla, bu numaralandırmaların kullanımının da bağlanması durumunda kolayca ek netlik ve basitlik sağlayabilir, ve bunları sağlamanın ek bir bağlantı da getirmediğini.

#include "entityEnum.h"    // Theoretical file defining entity_t.
#include "materialsEnum.h" // Theoretical file defining materials_t.

// Can entity break the specified material?
bool canBreakMaterial(entity_t, materials_t);

Bu durumda, varlık türü ile malzeme türü arasında doğrudan, mantıksal bir bağlantı yoktur (varlıkların tanımlanan malzemelerden birinden yapılmadığı varsayılarak). Bununla birlikte, örneğin bir enum'un açıkça diğerine bağımlı olduğu bir vakamız varsa, o zaman tüm bağlı numaralandırmaları (ve diğer herhangi bir bağlı bileşeni) içeren tek bir paket sağlamak mantıklıdır, böylece kaplin mümkün olduğu kadar bu pakete izole edilmiştir.

// File: "actionEnums.h"

enum action_t { ATTACK, DEFEND, SKILL, ITEM };               // Action type.
enum skill_t  { DAMAGE, HEAL, BUFF, DEBUFF, INFLICT, NONE }; // Skill subtype.

// -----

#include "actionTypes.h" // Provides action_t & skill_t from "actionEnums.h", and class Action (which couples them).
#include "entityEnum.h"  // Theoretical file defining entity_t.

// Assume ActFlags is or acts as a table of flags indicating what is and isn't allowable, based on entity_t and Action.
ImplementationDetail ActFlags;

// Indicate whether a given type of entity can perform the specified action type.
// Assume class Action provides members type() and subtype(), corresponding to action_t and skill_t respectively.
// Is only slightly aware of the coupling; knows type() and subtype() are coupled, but not how or why they're coupled.
bool canAct(entity_t e, const Action& act) {
    return ActFlags[e][act.type()][act.subtype()];
}

Ama ne yazık ki ... iki numara kendiliğinden birleştiğinde bile, "ikinci numaralandırma ilk numaralandırma için alt kategoriler sağlar" kadar güçlü bir şey olsa bile, sadece numaralardan sadece birinin gerekli olduğu bir zaman gelebilir.

#include "actionEnums.h"

// Indicates whether a skill can be used from the menu screen, based on the skill's type.
// Isn't concerned with other action types, thus doesn't need to be coupled to them.
bool skillUsableOnMenu(skill_t);

// -----
// Or...
// -----

#include "actionEnums.h"
#include "gameModeEnum.h" // Defines enum gameMode_t, which includes MENU, CUTSCENE, FIELD, and BATTLE.

// Used to grey out blocked actions types, and render them unselectable.
// All actions are blocked in cutscene, or allowed in battle/on field.
// Skill and item usage is allowed in menu.  Individual skills will be checked on attempted use.
// Isn't concerned with specific types of skills, only with broad categories.
bool actionBlockedByGameMode(gameMode_t mode, action_t act) {
    if (mode == CUTSCENE) { return true; }
    if (mode == MENU) { return (act == SKILL || act == ITEM); }

    //assert(mode == BATTLE || mode == FIELD);
    return false;
}

Bu nedenle, her ikimizin de, her zaman tek bir dosyada birden çok numaralandırma tanımlamanın gereksiz bağlantı ekleyebileceği ve tekli pakette birleştirilmiş numaralandırmalar sağlamanın amaçlanan kullanımı netleştirebileceği ve gerçek bağlantı kodunun kendisini izole etmemize izin verebileceği durumlar olabileceğini biliyoruz. ideal çözüm, her bir numaralandırmayı ayrı ayrı tanımlamak ve sık sık birlikte kullanılması amaçlanan numaralandırmalar için ortak paketler sağlamaktır. Aynı dosyada tanımlanan tek numaralandırmalar, içsel olarak birbirine bağlı olanlar olacaktır, böylece birinin kullanımı diğerinin kullanımını da gerektirir.

// File: "materialsEnum.h"
enum materials_t { WOOD, STONE, ETC };

// -----

// File: "entityEnum.h"
enum entity_t { PLAYER, MONSTER };

// -----

// File: "mapEnum.h"
enum map_t { 2D, 3D };

// -----

// File: "actionTypesEnum.h"
enum action_t { ATTACK, DEFEND, SKILL, ITEM };

// -----

// File: "skillTypesEnum.h"
enum skill_t  { DAMAGE, HEAL, BUFF, DEBUFF, INFLICT, NONE };

// -----

// File: "actionEnums.h"
#include "actionTypesEnum.h"
#include "skillTypesEnum.h"
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.