W tym przypadku powiedziałbym, że idealną odpowiedzią jest to, że zależy to od tego, w jaki sposób używane są wyliczenia, ale w większości przypadków prawdopodobnie najlepiej jest zdefiniować wszystkie wyliczenia osobno, ale jeśli którakolwiek z nich jest już sprzężona projektowo, powinieneś podać środki wspólnego wprowadzania wspomnianych sprzężonych enum. W efekcie masz tolerancję sprzężenia do ilości już zamierzonego sprzężenia, ale już nie.
Biorąc to pod uwagę, najbardziej elastyczne rozwiązanie prawdopodobnie zdefiniuje każde wyliczenie w osobnym pliku, ale zapewni sprzężone pakiety, gdy będzie to uzasadnione (zgodnie z zamierzonym użyciem zaangażowanych wyliczeń).
Zdefiniowanie wszystkich wyliczeń w tym samym pliku powoduje ich połączenie, a przez rozszerzenie każdy kod zależny od jednego lub większej liczby wyliczeń zależy od wszystkich wyliczeń, niezależnie od tego, czy kod faktycznie używa innych wyliczeń.
#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()
wolałbym tylko wiedzieć o tym map_t
, ponieważ w przeciwnym razie wszelkie zmiany w innych wpłyną na to, nawet jeśli tak naprawdę nie wchodzi w interakcje z innymi.
#include "mapEnum.h" // Theoretical file defining map_t.
void renderMap(map_t, mapIndex);
Jednak w przypadku, gdy komponenty są już połączone ze sobą, zapewnienie wielu wyliczeń w jednym pakiecie może łatwo zapewnić dodatkową jasność i prostotę, pod warunkiem, że istnieje wyraźny logiczny powód, aby wyliczyć wyliczenia, że użycie tych wyliczeń jest również połączone, a zapewnienie ich nie wprowadza żadnych dodatkowych połączeń.
#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);
W takim przypadku nie ma bezpośredniego, logicznego połączenia między typem elementu a typem materiału (przy założeniu, że elementy nie są wykonane z jednego ze zdefiniowanych materiałów). Gdybyśmy jednak mieli przypadek, w którym np. Jedno wyliczenie jest wyraźnie zależne od drugiego, wówczas sensownym jest dostarczenie pojedynczego pakietu zawierającego wszystkie sprzężone wyliczenia (jak również wszelkie inne sprzężone elementy), aby połączenie mogło być izolowane do tego pakietu w jak największym stopniu.
// 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()];
}
Ale niestety ... nawet jeśli dwa wyliczenia są ze sobą wewnętrznie połączone, nawet jeśli jest to coś tak silnego, jak „drugi wylicznik zapewnia podkategorie dla pierwszego wyliczenia”, może jeszcze przyjść czas, w którym tylko jedna z wyliczeń jest konieczna.
#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;
}
Dlatego, ponieważ wiemy oba, że zawsze mogą wystąpić sytuacje, w których zdefiniowanie wielu wyliczeń w jednym pliku może dodać niepotrzebne sprzężenie, oraz że zapewnienie sprzężonych wyliczeń w jednym pakiecie może wyjaśnić zamierzone użycie i pozwolić nam wyodrębnić sam kod sprzężenia jako w miarę możliwości idealnym rozwiązaniem jest zdefiniowanie każdego wyliczenia osobno i zapewnienie wspólnych pakietów dla dowolnych wyliczeń, które mają być często używane razem. Jedynymi wyliczeniami zdefiniowanymi w tym samym pliku będą te, które są ze sobą wewnętrznie połączone, tak że użycie jednego z nich wymaga również użycia drugiego.
// 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"