내 코드에서 "관리자"를 피하는 방법


26

현재 C ++ 용 Entity System을 다시 디자인하고 있으며 많은 관리자가 있습니다. 내 디자인에는 라이브러리를 함께 묶기 위해 이러한 클래스가 있습니다. "관리자"수업과 관련하여 많은 나쁜 소식을 들었습니다. 아마도 수업 이름을 적절하게 지정하지 않았을 것입니다. 그러나 나는 그것들의 이름을 무엇으로 할 지 모른다.

내 라이브러리의 대부분의 관리자는 다음과 같은 클래스로 구성됩니다 (약간 다를 수 있음).

  • 컨테이너-관리자의 객체에 대한 컨테이너
  • 속성-관리자의 객체에 대한 속성

도서관을위한 새로운 디자인에는 도서관을 연결하기 위해 이러한 특정 클래스가 있습니다.

  • ComponentManager-엔티티 시스템에서 컴포넌트를 관리합니다

    • ComponentContainer
    • ComponentAttributes
    • 장면 *-장면에 대한 참조 (아래 참조)
  • SystemManager-엔티티 시스템의 시스템을 관리합니다

    • 시스템 컨테이너
    • 장면 *-장면에 대한 참조 (아래 참조)
  • EntityManager-엔티티 시스템에서 엔티티를 관리합니다

    • EntityPool-엔티티 풀
    • EntityAttributes-엔티티의 속성 (ComponentContainer 및 System 클래스에만 액세스 할 수 있음)
    • 장면 *-장면에 대한 참조 (아래 참조)
  • 장면-모든 관리자를 하나로 묶습니다

    • ComponentManager
    • SystemManager
    • EntityManager

모든 컨테이너 / 풀을 Scene 클래스 자체에 넣을 생각이었습니다.

이 대신에 :

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

이것은 다음과 같습니다.

Scene scene; // create a Scene

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

그러나 확실하지 않습니다. 후자를 할 경우 Scene 클래스 내부에 많은 객체와 메소드가 선언되어 있음을 의미합니다.

노트:

  1. 엔터티 시스템은 단순히 게임에 사용되는 디자인입니다. 구성 요소, 엔티티 및 시스템의 3 가지 주요 부분으로 구성됩니다. 구성 요소는 단순히 데이터이며, 엔터티를 구별하기 위해 엔터티에 "추가"될 수 있습니다. 엔터티는 정수로 표시됩니다. 시스템에는 특정 구성 요소가있는 엔터티의 논리가 포함되어 있습니다.
  2. 내 라이브러리의 디자인을 변경하는 이유는 그것이 상당히 많이 변경 될 수 있다고 생각하기 때문입니다. 지금은 느낌 / 흐름이 마음에 들지 않습니다.

관리자에 대해 들었던 나쁜 점과 관리자와의 관계에 대해 자세히 설명해 주시겠습니까?
MrFox


@MrFox 기본적으로 나는 Dunk가 언급 한 것을 들었고 라이브러리에 * Manager 클래스가 많이 있습니다.
miguel.martin

도움이 될 수도 있습니다 : medium.com/@wrong.about/…
Zapadlo

작동중인 응용 프로그램에서 유용한 프레임 워크를 고려하십시오. 상상되는 응용 프로그램에 유용한 프레임 워크를 작성하는 것은 거의 불가능합니다.
케빈 클라인

답변:


19

DeadMG는 코드의 특성에 주목 하지만 설명이 누락 된 것 같습니다. 또한 나는 고성능 비디오 게임 개발과 같은 특정 상황에서 다루지 않는 그의 추천에 동의하지 않습니다. 그러나 그는 현재 대부분의 코드가 유용하지 않다는 것이 전 세계적으로 옳습니다.

Dunk가 말했듯이 Manager 클래스는 "관리"하기 때문에 이와 같이 호출됩니다. "관리"는 "데이터"또는 "do"와 비슷하며 거의 모든 것을 포함 할 수있는 추상적 인 단어입니다.

나는 7 년 전쯤에 대부분 당신과 같은 곳에 있었으며, 아직 아무것도하지 않기 위해 코딩하려는 노력이 너무 많았 기 때문에 내 사고 방식에 문제가 있다고 생각하기 시작했습니다.

이 문제를 해결하기 위해 변경 한 것은 코드에서 사용 하는 단어변경하는 것 입니다. 나는 일반적인 단어를 피합니다 (일반 코드가 아니라면 일반 라이브러리를 만들지 않는 경우는 드 rare니다). 나는 모든 종류의 "관리자"의 이름을 피 하거나 "개체를".

그 영향은 코드에 직접적으로 영향을 미칩니다. 즉, 해당 유형의 실제 책임에 해당하는 올바른 단어를 찾아야합니다. 유형이 여러 가지 일을하는 것처럼 느껴지면 (책 색인을 유지하고 책을 유지하고 책을 만들거나 파괴하십시오) 각각 하나의 책임이있는 다른 유형이 필요합니다. 그런 다음 그것들을 사용하는 코드에서 결합하십시오. 언젠가는 공장이 필요하지만 언젠가는 필요하지 않습니다. 때로는 레지스트리가 필요하므로 표준 컨테이너와 스마트 포인터를 사용하여 레지스트리를 설정하십시오. 때로는 여러 개의 다른 서브 시스템으로 구성된 시스템이 필요하므로 가능한 한 모든 것을 분리하여 각 부분에서 유용한 것을 수행하십시오.

유형을 "관리자"로 지정하지 말고 모든 유형에 고유 한 단일 역할있는지 확인하십시오 . 언젠가는 이름을 찾기 어려울 수 있지만 일반적으로 프로그래밍에서 가장 어려운 일 중 하나입니다.


커플 dynamic_cast이 성능을 저하시키는 경우 정적 유형 시스템을 사용하십시오. 이것이 바로 그 목적입니다. LLVM처럼 RTTI를 롤링해야하는 것은 일반적인 상황이 아니라 특수한 상황입니다. 그럼에도 불구하고 그들은 이것을하지 않습니다.
DeadMG

@DeadMG 특히이 부분에 대해서는 이야기하지 않았지만, 대부분의 고성능 게임은 예를 들어 일반적으로 프로그래밍하기에 좋은 새롭거나 다른 것들을 통한 동적 할당을 감당할 수 없습니다. 그것들은 당신이 일반화 할 수없는 특정 하드웨어를위한 특별한 야수입니다. 이것이 "종종"이 더 일반적인 대답, 특히 C ++의 이유입니다. (게임 개발자 중에는 동적 캐스트를 사용하는 사람이 없으며 플러그인을 사용할 때까지는 유용하지 않으며 드문 경우입니다).
Klaim

@DeadMG dynamic_cast를 사용하고 있지 않으며 dynamic_cast의 대안을 사용하고 있지 않습니다. 나는 그것을 도서관에서 구현했지만 그것을 꺼내는 데 방해가되지 않았다. template <typename T> class Class { ... };실제로 다른 클래스 (사용자 지정 구성 요소 및 사용자 지정 시스템)에 ID를 할당하기위한 것입니다. 지도에 게임에 필요한 성능을 제공하지 못할 수 있으므로 ID 할당은 구성 요소 / 시스템을 벡터에 저장하는 데 사용됩니다.
miguel.martin

글쎄, 실제로 dynamic_cast에 대한 대안을 구현하지 않았으며 가짜 type_id입니다. 그러나 여전히 type_id가 달성 한 것을 원하지 않았습니다.
miguel.martin 02/27/11

"귀하의 유형의 실제 책임에 해당하는 올바른 단어를 찾으십시오."-이것에 완전히 동의하지만 개발자 커뮤니티가 특정 유형의 책임에 대해 동의 한 단일 단어가있는 경우가 종종 있습니다.
bytedev

23

글쎄, 나는 당신이 링크 한 코드와 게시물을 읽었으며, 솔직히 요약하면 그 코드의 대부분은 기본적으로 완전히 가치가 없다는 것입니다. 죄송합니다. 내 말은,이 코드를 모두 가지고 있지만 아무것도 달성 하지 못했습니다 . 조금도. 나는 여기서 좀 더 깊이 들어가야하므로 나와 함께 참 아라.

ObjectFactory로 시작하자 . 먼저 함수 포인터. 이것들은 상태 비 저장 (stateless)이며, 객체를 생성하는 유일하게 유용한 상태 비 저장 방법 new이며 팩토리가 필요하지 않습니다. 상태 적으로 객체를 생성하는 것이 유용하며 함수 포인터는이 작업에 유용하지 않습니다. 둘째, 모든 객체는 객체를 파괴 하는 정확한 생성 인스턴스를 찾을 필요없이 자신 을 파괴하는 방법을 알아야합니다 . 또한 사용자의 예외 및 리소스 안전을 위해 원시 포인터가 아닌 스마트 포인터를 반환해야합니다. 전체 ClassRegistryData 클래스는 단지 std::function<std::unique_ptr<Base, std::function<void(Base*)>>()>이지만 앞서 언급 한 구멍이 있습니다. 전혀 존재할 필요는 없습니다.

그리고 우리가 AllocatorFunctor-이 표준 할당 인터페이스는 말할 것도없고 provided- 이미 거기에 그들이있는 거 단지에 래퍼 그 delete obj;return new T;- 다시, 이미 제공되고, 그렇지 않은 상호 작용 존재하는 상태 또는 사용자 정의 메모리 할당 자와 것입니다.

그리고 ClassRegistry는 간단 std::map<std::string, std::function<std::unique_ptr<Base, std::function<void(Base*)>>()>>하지만 기존 기능을 사용하는 대신 클래스를 작성했습니다. 크 래피 매크로가 많지만 완전히 불필요하게 전역 변경 가능한 상태입니다.

여기서 말하는 것은 전체 클래스를 표준 클래스로 작성된 기능으로 대체 할 수있는 클래스를 만든 다음 전역 변경 가능 상태로 만들고 매크로를 포함하여 악화시킵니다. 당신이 할 수있는 최악의 일.

그럼 우리는 식별 할 수 있습니다 . 여기서도 같은 이야기가 많이 있습니다. std::type_info이미 각 유형을 런타임 값으로 식별하는 작업을 수행하고 있으며 사용자 측에 과도한 상용구가 필요하지 않습니다.

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

문제는 해결되었지만 이전에 200 줄의 매크로가 나오지 않았습니다. 이것은 또한 IdentifiableArray 를 아무것도 아닌 것으로 표시 std::vector하지만 고유 소유권 또는 비 소유권이 더 나은 경우에도 참조 계산을 사용해야합니다.

오, 그리고 Types전혀 쓸모없는 typedef 가 포함되어 있다고 언급 했 습니까? 원시 유형을 직접 사용하는 것보다 문자 그대로 이점이 없으며 가독성이 떨어집니다.

다음은 ComponentContainer 입니다. 소유권을 선택할 수 없으며 다양한 구성 요소의 파생 클래스에 대한 별도의 컨테이너를 가질 수 없으며 자신의 평생 시맨틱을 사용할 수 없습니다. 변심없이 삭제하십시오.

이제 리팩토링을 많이 하면 ComponentFilter 가 실제로 약간 가치가있을 수 있습니다. 같은 것

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

이것은 당신이 다루려고하는 클래스에서 파생 된 클래스는 다루지 않지만 귀하의 클래스는 아닙니다.

이것의 나머지는 거의 같은 이야기입니다. 라이브러리를 사용하지 않고는 더 잘 할 수없는 것은 없습니다.

이 코드에서 관리자를 어떻게 피할 수 있습니까? 기본적으로 모든 것을 삭제하십시오.


14
배우는 데 시간이 걸렸지 만 가장 신뢰할 수 있고 버그가없는 코드는 존재하지 않는 것 입니다.
Tacroy 2019

1
std :: type_info를 사용하지 않은 이유는 RTTI 를 하려고했기 때문 입니다. 나는 프레임 워크는 내가 사용하지 않는 이유는 기본적으로있는 게임을 목표로 한 언급 typeid하거나 dynamic_cast. 피드백에 감사드립니다. 감사합니다.
miguel.martin

이 비평가는 구성 요소 엔터티 시스템 게임 엔진에 대한 지식을 기반으로합니까, 아니면 일반적인 C ++ 코드 검토입니까?
Den

1
@Den 다른 것보다 디자인 문제가 더 많다고 생각합니다.
miguel.martin

10

관리자 클래스의 문제점은 관리자가 무엇이든 할 수 있다는 것입니다. 수업 이름을 읽을 수없고 수업 내용을 알 수 없습니다. EntityManager .... 엔티티로 무언가를하는 것을 알고 있지만 누가 아는가. SystemManager ... 예, 시스템을 관리합니다. 매우 도움이됩니다.

내 생각에 당신의 관리자 클래스는 아마도 많은 일을 할 것입니다. 그러한 것들을 밀접하게 관련된 기능적 조각으로 세분화한다면, 아마도 클래스 이름을 보면서 누구나 무엇을 할 수 있는지 기능적 조각과 관련된 클래스 이름을 생각해 낼 수있을 것입니다. 이를 통해 설계를 훨씬 쉽게 유지 관리하고 이해할 수 있습니다.


1
+1하고 그들이 할 수있을만큼 충분한 시간 내에 무엇이든 할 수 있습니다. 사람들은 "관리자"설명자를 주방 싱크 / 스위스-아미-나이프 클래스를 만드는 초대로 본다.
Erik Dietrich

@Erik-아, 그래요. 좋은 클래스 이름을 갖는 것이 누군가에게 클래스가 무엇을 할 수 있는지 알려주기 때문에 중요 할뿐 아니라고 덧붙였습니다. 그러나 클래스 이름도 클래스가하지 말아야 할 것을 말합니다.
Dunk

3

나는 실제로이 Manager맥락에서 그렇게 나쁘지 않다고 생각 합니다. 내가 Manager용서할 수 있다고 생각되는 곳이 있다면 , 구성 요소, 엔티티 및 시스템의 수명을 실제로 " 관리 " 하기 때문에 엔티티 구성 요소 시스템을위한 것 입니다. FactoryECS가 실제로 물건을 만드는 것보다 조금 더 많은 일을하기 때문에이 맥락에서 의미가없는 말이 여기에서 가장 의미있는 이름 입니다.

나는 당신이 사용할 수있는 가정 Database처럼 ComponentDatabase. 아니면 그냥 사용할 수 Components, Systems그리고 Entities(이것은 내가 개인적으로 사용하는 것입니다 주로 단지 때문에 간결한 식별자와 같은 그것은 틀림없이 전달한다 더 적은 정보, 아마도, "관리자"이상하지만).

약간 어색한 부분은 다음과 같습니다.

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

get엔터티를 생성하기 전에는 많은 것들이 있으며 엔터티 풀을 생성하기 위해 엔터티 풀과 "관리자"에 대해 알아야 할 경우 디자인에서 구현 세부 정보가 유출되는 것처럼 느껴집니다. "풀"및 "관리자"(이 이름을 고수하는 경우)와 같은 것은 클라이언트에 노출 된 것이 아니라 ECS의 구현 세부 사항이어야한다고 생각합니다.

그러나 몰라, 나는 몇 가지 매우 상상력 및 일반 용도 보았다 Manager, Handler, Adapter이런 종류의, 그리고 이름을,하지만 난이 "관리자"라는 것은 "제어 ("관리 "에 대한 실제 책임이 때 합리적으로 용서 사용 사례라고 생각? ","handling? ") 개체의 수명은 개체를 만들고 파괴하고 개별적으로 액세스 할 수 있도록합니다. 그것은 적어도 "관리자"를 가장 간단하고 직관적으로 사용하는 것입니다.

즉, 귀하의 이름에서 "관리자"를 피하는 일반적인 전략 중 하나는 "관리자"부분을 꺼내서 -s끝에 추가하는 것 입니다. :-D 예를 들어, ThreadManager스레드를 만들고 스레드에 대한 정보를 제공하고 액세스하는 등의 책임이 있지만 스레드를 풀링하지 않아 호출 할 수 없다고 가정 해 봅시다 ThreadPool. 이 경우에는 그냥 호출하면 Threads됩니다. 어쨌든 내가 상상할 수없는 일입니다.

"Windows 탐색기"와 비슷한 것을 "파일 관리자"라고 불렀을 때,

여기에 이미지 설명을 입력하십시오

그리고 실제로이 경우 "파일 관리자"는 "관리자"와 관련이없는 더 나은 이름이 있더라도 "Windows 탐색기"보다 설명 적이라고 생각합니다. 따라서 적어도 당신의 이름으로 너무 화려하지 말고 "ComponentExplorer"와 같은 것들의 이름을 시작하십시오. "ComponentManager"는 적어도 누군가 ECS 엔진 작업에 익숙했던 것이 무엇인지에 대한 합리적인 아이디어를 제공합니다.


동의했다. 클래스가 일반적인 관리 업무에 사용되는 경우 (() "발사"감독을 파괴, () "고용"을 생성하고 "직원"/ higherup 인터페이스, 그 이름은 Manager클래스가있을 수 있습니다. 적절한 설계 문제를,하지만 이름 자리 -에 있습니다.
저스틴 시간이 분석 재개 모니카
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.