하나의 파일에 모든 열거 형을 포함시키고 여러 클래스에서 사용하는 것은 나쁜 습관입니까?


12

나는 야심 찬 게임 개발자이고, 가끔 인디 게임을하고 있으며, 처음에는 나쁜 연습처럼 보였던 일을하고 있지만, 경험이 풍부한 프로그래머로부터 답변을 얻고 싶습니다.

enumList.h게임에서 사용하려는 모든 열거 형을 선언 하는 파일이 있다고 가정 해 보겠습니다 .

// 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
};

주요 아이디어는 게임의 모든 열거 형을 1 파일로 선언 한 다음 사용해야 할 파일에서 선언하지 않고 특정 열거 형을 사용해야 할 때 해당 파일을 가져 오는 것입니다. 나는 일을 깨끗하게하기 때문에이 작업을 수행합니다 . 한 열거 형에만 액세스하기 위해 페이지를 열지 않고 한 에서 모든 열거 형에 액세스 할 수 있습니다 .

이것은 나쁜 습관이며 어떤 식 으로든 성능에 영향을 줄 수 있습니까?


1
소스 구조는 성능에 영향을 줄 수 없으며 열거 형의 위치에 관계없이 여전히 동일하게 컴파일됩니다. 따라서 실제로 이것은 가장 좋은 위치에 대한 질문이며 작은 열거 형의 경우 하나의 파일이 너무 소리 나지 않습니다.
Steffan Donal

좀 더 극단적 인 경우에, 게임은 많은 열거 형을 가질 수 있고 꽤

4
응용 프로그램 성능에는 영향을 미치지 않지만 컴파일 시간에는 부정적인 영향을 미칩니다. 예를 들어, 재료를 materials_t다루지 않는 파일에 재료를 추가하는 경우 다시 빌드해야합니다.
로봇 고 르트

14
마치 집에있는 모든 의자를 의자 방에 두는 것과 같습니다. 앉고 싶다면 어디로 가야하는지 알고 있습니다.
kevin cline

따로, 각 열거 형을 자체 파일에 넣고 해당 파일의 모음으로 사용 함으로써 두 가지를 쉽게 수행 할 수 있습니다 . 이를 통해 하나의 열거 형 만 필요한 파일을 직접 가져올 수 있으며 실제로 모든 것을 원하는 것에는 단일 패키지가 제공됩니다. enumList.h#include
저스틴 타임-복원 모니카

답변:


35

나는 그것이 나쁜 습관이라고 생각합니다. 코드 품질을 측정 할 때 "세분성"이라고하는 것이 있습니다. 모든 열거 형을 하나의 파일에 넣으면 세분성이 심각하게 유지되므로 유지 관리 성도 저하됩니다.

열거 형당 하나의 파일을 사용하여 신속하게 찾아 특정 기능의 동작 코드 (예 : 재료 동작이있는 폴더의 재료 열거)와 그룹화합니다.

주요 아이디어는 게임의 모든 열거 형을 1 파일로 선언 한 다음 사용해야 할 파일에 선언하지 않고 특정 열거 형을 사용해야 할 때 해당 파일을 가져 오는 것입니다. 나는 일을 깨끗하게하기 때문에이 작업을 수행합니다. 한 열거 형에만 액세스하기 위해 페이지를 열지 않고 한 곳에서 모든 열거 형에 액세스 할 수 있습니다.

깨끗하다고 ​​생각할 수도 있지만 실제로는 그렇지 않습니다. 기능 및 모듈 방식에 속하지 않은 것들을 결합하여 응용 프로그램의 모듈성을 줄입니다. 코드베이스의 크기와 코드 구성 방식에 따라 시스템의 다른 부분에서 더 큰 문제가 발생하고 코드 / 종속성이 더러워 질 수 있습니다. 그러나 작은 모 놀리 식 시스템을 작성하는 경우 반드시 적용되는 것은 아닙니다. 그러나 작은 모 놀리 식 시스템의 경우에도 이렇게하지 않습니다.


2
세분성을 언급 한 +1, 다른 답변을 고려했지만 좋은 지적입니다.
Bugster December

열거 당 단일 파일의 경우 +1 하나는 찾기가 더 쉽습니다.
mauris

11
한 곳에서 사용되는 단일 열거 형 값을 추가하면 전체 프로젝트의 모든 파일을 다시 작성해야합니다.
로봇 고트

좋은 조언. 당신은 어쨌든 내 마음을 바꿨다. 나는 항상 CompanyNamespace.Enums ...를 가고 쉬운 목록을 얻는 것을 좋아했지만 코드 구조가 규율된다면 당신의 접근 방식이 더 좋다
Matt Evans

21

예, 성능 때문이 아니라 유지 관리 성 때문입니다.

그것은 OCD에서만 "사물을 모아서" "유사한 것들을 모아서"만들어줍니다. 그러나 그것은 실제로 유용하고 좋은 종류의 "청결도" 가 아닙니다 .

코드 엔티티 극대화하기 위해 그룹화해야 응집력을 최소화 커플 링 가장 크게 독립적 인 기능 모듈로 그룹화하여 달성되는을. 기술적 인 기준 (예 : 모든 열거 형을 모으는 것과 같이)으로 그룹화하면 반대의 결과를 얻습니다. 기능적 관련성이 전혀없는 코드를 결합하고 한 곳에서만 사용할 수있는 열거 형을 다른 파일에 넣습니다.


3
진실; 'ODC에서만 "유사한 것들을 모아서"방식. 내가 100면 공감할 수있어
Dogweather

가능한 경우 맞춤법을 편집하는 데 도움이되지만 SE 'edit'임계 값을 초과 할만큼 오류가 발생하지 않았습니다. - P
Dogweather

흥미로운 점은 거의 모든 웹 프레임 워크가 기능 대신 유형별로 파일을 수집해야한다는 점입니다.
케빈 클라인

@kevincline : "거의 모든 것"이라고 말하지는 않겠습니다. 컨피규레이션 오버 컨벤션을 기반으로하는 것뿐 아니라 일반적으로 코드를 기능적으로 그룹화 할 수있는 모듈 개념도 있습니다.
Michael Borgwardt

8

글쎄, 이것은 단지 데이터 (행동이 아님)입니다. 가능한 / 이론적으로 일어날 수있는 최악의 경우는 동일한 코드가 포함되어 상대적으로 큰 프로그램을 두 번 이상 생성한다는 것입니다.

내부에 동작 / 절차 코드가없는 경우 (루프, if 등 없음) 이러한 포함으로 작업에 사이클을 더 추가 할 수는 없습니다.

(상대적으로) 더 큰 프로그램은 성능 (실행 속도)에 거의 영향을 미치지 않으며 어쨌든 원격 이론적 관심사 일뿐입니다. 대부분의 컴파일러는 이러한 문제를 방지하는 방식으로 포함을 관리합니다.

IMHO, 단일 파일 (더 읽기 쉽고 관리하기 쉬운 코드)로 얻을 수있는 이점은 가능한 모든 단점을 능가합니다.


5
가장 인기있는 답변은 모두 무시하고 있다고 생각합니다. 불변의 데이터에는 전혀 커플 링이 없습니다.
Michael Shaw

3
컴파일하는 데 30 분 이상 걸리는 프로젝트를 수행 한 적이 없었을 것 같습니다. 하나의 파일에 모든 열거 형이 있고 단일 열거 형을 변경하면 오랜 시간이 걸릴 때입니다. 도대체 1 분 밖에 걸리지 않았지만 여전히 너무 깁니다.
Dunk

2
버전 제어 하의 모듈 X에서 작업하는 사람은 원하는대로 작업 할 때마다 모듈 X 및 열거를 가져와야합니다. 또한 열거 형 파일을 변경할 때마다 변경 사항이 잠재적으로 프로젝트의 모든 단일 모듈에 영향을 미치는 경우 커플 링의 형태로 생각납니다. 프로젝트 규모가 크거나 전체 또는 대부분의 팀 구성원이 프로젝트의 모든 부분을 이해하지 못하는 경우 글로벌 열거 형은 매우 위험합니다. 전역 불변 변수는 전역 가변 변수만큼 가깝지는 않지만 여전히 이상적이지는 않습니다. 즉, 글로벌 열거 형은 아마도 10 명 미만의 팀이 있으면 괜찮을 것입니다.
Brian

3

나에게 그것은 모두 프로젝트의 범위에 달려 있습니다. 10 개의 구조체가있는 파일이 하나만 있고 그것이 유일하게 사용 된 파일이라면, 하나의 .h 파일을 갖는 것이 완벽합니다. 단위, 경제, 건물 등 여러 가지 유형의 기능이 있다면 분명히 일을 나눌 것입니다. 유닛을 다루는 모든 구조체가있는 units.h를 만듭니다. 어딘가에 단위로 무언가를하고 싶다면 units.h를 포함시켜야하지만, 단위가있는 무언가 가이 파일에서 수행 될 수있는 좋은 "식별자"이기도합니다.

코카콜라가 필요하기 때문에 슈퍼마켓을 사지 마십시오.)


3

나는 C ++ 개발자가 아니므 로이 답변은 OOA & D에 더 일반적입니다.

일반적으로 코드 객체는 언어 별 구문과 관련하여 기능적 관련성으로 그룹화해야합니다. 항상 물어봐야 할 중요한 예는 "최종 코더가 라이브러리를 소비 할 때 얻을 수있는 대부분 또는 모든 객체를 사용해야합니까?"입니다. 그렇다면 그룹을 멀리하십시오. 그렇지 않은 경우 코드 객체를 분리하여 필요한 다른 객체에 더 가깝게 배치하십시오 (따라서 소비자가 실제로 액세스하는 모든 것이 필요할 가능성이 높아짐).

기본 개념은 "높은 응집력"입니다. 코드 멤버 (클래스의 메소드에서 네임 스페이스 또는 DLL의 클래스 및 DLL 자체까지)는 코더가 필요한 모든 것을 포함 할 수 있도록 구성해야합니다. 이로 인해 전체 설계가 더 견딜 수 있습니다. 변경할 필요가없는 다른 코드 객체에 영향을주지 않으면 서 변경해야 할 사항이있을 수 있습니다. 많은 상황에서 응용 프로그램의 메모리 효율성을 향상시킵니다. 프로세스에 의해 실행되는 명령의 양에 관계없이 DLL은 전체 메모리에로드됩니다. 따라서 "린 (lean)"응용 프로그램을 설계하려면 메모리로 가져 오는 코드 양에주의를 기울여야합니다.

이 개념은 유지 관리 성, 메모리 효율성, 성능, 빌드 속도 등에 영향을 미치는 거의 모든 수준의 코드 구성에 적용됩니다. 코더가 단일 개체에 액세스하기 위해 단일체 크기의 헤더 / DLL을 참조해야하는 경우, 해당 DLL의 다른 개체에 의존하지 않는 경우 해당 개체가 DLL에 포함되었는지 다시 생각해야합니다. 그러나 다른 방법으로도 너무 멀리 갈 수 있습니다. 모든 클래스에 대한 DLL은 빌드 속도를 늦추고 (관련된 오버 헤드로 다시 빌드 할 DLL이 더 많음) 버전 관리를 악몽으로 만들기 때문에 나쁜 생각입니다.

실제 사례 : 코드 라이브러리를 실제로 사용하는 데이 단일 "enumerations.h"파일에 넣는 열거의 대부분 또는 전부를 사용하는 것이 필요한 경우에는 반드시 그룹화하십시오. 어디서 찾을 수 있는지 알 수 있습니다. 그러나 소비하는 코더가 헤더에 제공하는 수십 개의 열거 중 하나 또는 두 개만 필요할 수 있다면, 별도의 라이브러리에 배치하고 나머지 열거와 함께 더 큰 라이브러리의 종속성을 만드는 것이 좋습니다. . 이를 통해 코더는보다 모 놀리 식 DLL에 연결하지 않고도 원하는 하나 또는 두 개만 얻을 수 있습니다.


2

여러 개발자가 동일한 코드 기반으로 작업하는 경우 이런 종류의 문제가 발생합니다.

필자는 비슷한 전역 파일이 병합 충돌과 (큰) 프로젝트에 대한 모든 종류의 슬픔의 넥서스가되는 것을 보았습니다.

그러나 프로젝트에서 일하는 유일한 사람이라면 가장 편한 것을하고 그 뒤에 동기를 이해하고 동의 할 경우 "모범 사례"만 채택해야합니다.

평생 동안화물 컬트 프로그래밍 관행에 걸릴 위험을 감수하는 것보다 실수를 저지르고 배우는 것이 좋습니다.


1

그렇습니다. 큰 프로젝트에서는 그렇게하는 것이 좋지 않습니다. 키스.

젊은 동료는 핵심 .h 파일에서 간단한 변수의 이름을 바꾸 었으며 100 명의 엔지니어가 모든 파일이 다시 작성 될 때까지 45 분 동안 기다렸으며 이는 모든 사람의 성능에 영향을 미쳤습니다. ;)

모든 프로젝트는 수년에 걸쳐 소규모로 시작되어 기술 부채를 만들기 위해 조기 지름길을 가진 사람들을 저주합니다. 모범 사례, 글로벌 .h 콘텐츠를 반드시 글로벌로 제한하십시오.


누군가 (젊거나 나이가 많거나 같은 사람)가 모두 재미있게 프로젝트를 재건 할 수 있다면 검토 시스템을 사용하지 않습니까?
Sanctus

1

이 경우, 나는 이상적인 대답은 그것이 열거 소비 방식에 의존하지만, 대부분의 경우에이 있다고 말할 것입니다 아마 별도로 모든 열거 형을 정의하는 것이 가장 좋습니다,하지만 그 중 하나가 이미 설계에 의해 결합하는 경우, 당신은을 제공해야 상기 결합 된 열거 형을 집합 적으로 도입하는 수단. 실제로 의도적 인 커플 링 양까지 커플 링 공차가 있지만 더 이상 없습니다.

이를 고려할 때 가장 유연한 솔루션은 각 열거 형을 별도의 파일로 정의 할 수 있지만 합리적 인 경우 결합 된 패키지를 제공합니다 (관련 된 열거 형의 의도 된 사용에 의해 결정됨).


동일한 열거 형에서 모든 열거 형을 정의하면 이들을 함께 결합하고 확장에 의해 코드가 실제로 다른 열거 형을 사용 하는지 여부에 관계없이 하나 이상의 열거 형에 의존하는 코드가 모든 열거 형에 종속됩니다 .

#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()map_t다른 사람과의 변경은 실제로 다른 사람과 상호 작용하지 않더라도 영향을 미치기 때문에 에 대해 많이 알고 있을 것입니다.

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

void renderMap(map_t, mapIndex);

그러나 구성 요소가 이미 함께 연결된 경우 단일 패키지에 여러 열거 형을 제공하면 추가 명확성 및 단순성을 쉽게 제공 할 수 있습니다. 그것들을 제공한다고해서 추가적인 커플 링이 도입되지는 않습니다.

#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);

이 경우 엔터티 유형과 재료 유형간에 직접적인 논리적 연결이 없습니다 (엔터티가 정의 된 재료 중 하나로 구성되지 않은 것으로 가정). 그러나 예를 들어 하나의 열거 형이 다른 열거 형에 명시 적으로 종속 된 경우 모든 결합 된 열거 형 (및 다른 결합 된 구성 요소)을 포함하는 단일 패키지를 제공하는 것이 합리적입니다. 합리적으로 가능한 한 해당 패키지에 격리됩니다.

// 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()];
}

그러나 아아 .... 두 열거 형이 본질적으로 함께 결합 된 경우에도 "두 번째 열거 형이 첫 번째 열거 형에 하위 범주를 제공합니다"와 같이 강력한 경우에도 열거 형 중 하나만 필요한 경우가 있습니다.

#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;
}

따라서 단일 파일에 여러 열거를 정의하면 불필요한 커플 링이 추가 될 수있는 상황이 항상있을 수 있으며 단일 패키지에 커플 링 된 열거를 제공하면 의도 된 사용법을 명확하게하고 실제 커플 링 코드 자체를 분리 할 수 ​​있습니다. 가능한 가장 이상적인 솔루션은 각 열거를 개별적으로 정의하고 자주 함께 사용되는 열거에 대한 공동 패키지를 제공하는 것입니다. 같은 파일에 정의 된 유일한 열거 형은 본질적으로 서로 연결된 것이므로 하나를 사용하면 다른 하나의 사용법도 필요합니다.

// 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"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.