계층을 강요하지 않고 개체가 서로 상호 작용하고 통신하도록하려면 어떻게해야합니까?


9

이 문제가 내 질문을 명확하게 해주길 바랍니다. 그래도 이해가 안된다면 완전히 이해하고 싶습니다. 그럴 경우 알려주세요. 더 명확하게 해보도록하겠습니다.

객체 지향 게임 개발에 익숙해 지도록 만든 매우 간단한 게임 인 BoxPong을 만나보십시오 . 상자를 드래그하여 공을 제어하고 노란색 물건을 수집하십시오.
BoxPong을 만드는 것은 무엇보다도 근본적인 질문을 공식화하는 데 도움이되었습니다. 서로 "연장 "하지 않고 서로 상호 작용하는 객체를 어떻게 가질 수 있습니까? 즉, 객체가 계층 적이 지 않고 공존 할 수있는 방법이 있습니까? (아래에서 더 자세히 설명하겠습니다.)

개체 공존 문제가 일반적인 문제인 것 같아서이를 해결할 수있는 확실한 방법이 있기를 바랍니다. 나는 사각형 휠을 재발 명하고 싶지 않기 때문에 내가 찾는 이상적인 대답은 "여기서 당신의 문제를 해결하는 데 일반적으로 사용되는 디자인 패턴"이라고 생각합니다.

특히 BoxPong과 같은 간단한 게임에서는 같은 수준에 소수의 개체가 공존하거나 존재해야한다는 것이 분명합니다. 상자가 있고, 공이 있고, 소장품이 있습니다. 객체 지향 언어로 표현할 수있는 모든 것은 엄격한 HAS-A 관계입니다. 멤버 변수를 통해 이루어집니다. 나는 단지 시작해서 그것을 할 수 없으며 다른 객체 ball에 영구적으로 속해야 합니다. 그래서 주요 게임 객체가 있다는을 설정 한 상자를 차례로 상자는 공을, 그리고 점수 카운터. 각 개체에는update()나는 모든 아이들의 업데이트 메소드를 호출 주요 게임 객체의 update 메소드를 호출하고, 그들은 차례로 모두의 업데이트 방법 호출 등의 위치, 방향을 계산하고 내가 거기에 비슷한 길을 갈 방법, 자신의 아이를. 이것이 객체 지향 게임을 만드는 유일한 방법이지만 이것이 이상적인 방법은 아니라고 생각합니다. 결국, 나는 공이 상자에 속하는 것이 아니라 같은 레벨에 있고 상호 작용하는 것으로 생각합니다. 나는 모든 게임 객체를 메인 게임 객체의 멤버 변수로 바꾸어 달성 할 수 있다고 생각하지만 아무것도 해결하는 것을 보지 못합니다. 내 말은 ... 명백한 혼란을 버리고 공과 상자가 서로알아 갈 수있는 방법 , 즉 상호 작용하는 방법이 있을까요?

서로간에 정보를 전달해야하는 개체 문제도 있습니다. SNES 용 코드를 작성하는 데 약간의 경험이있어서 거의 모든 RAM에 항상 액세스 할 수 있습니다. 수퍼 마리오 월드를 위해 커스텀 적을 만들고 있다고 가정 해 봅시다. 마리오의 모든 코인을 제거하고 문제없이 $ 0DBF를 처리하기 위해 0을 저장하기 만하면됩니다. 적들이 플레이어의 상태에 접근 할 수 없다는 제한은 없습니다. C ++ 등을 사용하면 종종 다른 객체 (또는 전역)에 값을 액세스 할 수있는 방법을 궁금해하기 때문에이 자유에 흠뻑 빠졌다고 생각합니다.
BoxPong의 예를 사용하여 공이 화면 가장자리에서 튀어 오도록하려면 어떻게해야합니까? widthheight의 속성입니다 Game클래스는,ball그들에게 접근 할 수 있습니다. 이러한 종류의 값을 (생성자 또는 필요한 메소드를 통해) 전달할 수는 있지만 나쁜 연습을 비명을 지 릅니다.

내 주요 문제는 서로를 알기 위해 객체가 필요하다는 것입니다.하지만 내가 볼 수있는 유일한 방법은 추악하고 비실용적 인 엄격한 계층 구조입니다.

C ++에서 "친구 클래스"에 대해 들어 보았는데 어떻게 작동하는지 잘 알고 있지만 최종 솔루션이라면 friend모든 C ++ 프로젝트마다 키워드가 부어 지지 않고 어떻게 나오는지 알 수 없습니다 . 모든 OOP 언어에 개념이 존재하지 않습니까? (방금 전에 배운 함수 포인터도 마찬가지입니다.)

어떤 종류의 답변이라도 미리 감사드립니다. 다시 말하지만 이해가되지 않는 부분이 있으면 알려주십시오.


2
많은 게임 산업이 Entity-Component-System 아키텍처와 그 변형으로 이동했습니다. 전통적인 OO 접근 방식과는 다른 사고 방식이지만 개념이 일단 적용되면 잘 작동하고 의미가 있습니다. Unity가 사용합니다. 실제로 Unity는 Entity-Component 부분 만 사용하지만 ECS를 기반으로합니다.
Dunk

클래스가 서로에 대한 지식없이 서로 협력 할 수있게하는 문제는 중재자 디자인 패턴으로 해결됩니다. 당신은 그것을 보았습니까?
Fuhrmanator 2016 년

답변:


13

일반적으로 같은 수준의 객체가 서로를 알고 있으면 매우 나쁘게 나타납니다. 일단 객체가 서로에 대해 알게되면 서로 연결되거나 연결 됩니다. 이로 인해 변경, 테스트 및 유지 관리가 어렵습니다.

둘에 대해 알고 있고 둘 사이의 상호 작용을 설정할 수있는 "위"개체가 있으면 훨씬 더 잘 작동합니다. 두 피어에 대해 알고있는 개체는 종속성 주입이나 이벤트 또는 메시지 전달 (또는 다른 분리 메커니즘)을 통해 함께 연결할 수 있습니다. 예, 약간 인공적인 계층 구조로 이어지지 만, 사물이 단지 상호 작용할 때 얻는 스파게티 혼란보다 훨씬 낫습니다. C ++에서는 객체의 수명을 소유 할 무언가가 필요하기 때문에 더 중요합니다.

요컨대, ad hoc 액세스로 묶여있는 객체를 나란히두면됩니다.하지만 나쁜 생각입니다. 계층은 순서와 명확한 소유권을 제공합니다. 기억해야 할 것은 코드의 객체가 반드시 실생활 (또는 게임)의 객체 일 필요는 없다는 것입니다. 게임의 객체가 좋은 계층 구조를 만들지 않으면 다른 추상화가 더 나을 수 있습니다.


2

BoxPong의 예를 사용하여 공이 화면 가장자리에서 튀어 오도록하려면 어떻게해야합니까? 너비와 높이는 게임 클래스의 속성이며 볼에 액세스하려면 볼이 필요합니다.

아니!

나는 당신이 가지고있는 주요한 문제는, 당신이 문자 적으로 "Object Oriented Programming"을 조금 복용하고 있다는 것입니다. OOP에서 개체는 "사물"이 아니라 "공", "게임", "물리", "수학", "날짜"등을 의미하는 "아이디어"를 나타냅니다. 모두 유효한 개체입니다. 객체가 무엇이든 "알아야"할 필요는 없습니다. 예를 들어, Date.Now().getTommorrow()오늘의 요일을 컴퓨터에 묻고, 비밀 날짜 규칙을 적용하여 내일의 날짜를 파악한 다음 발신자에게 반환합니다. Date시스템에서 필요에 따라 다른 것, 그것은 단지 요청 정보를 필요에 대한 목적은 알 수 없습니다. 또한, Math.SquareRoot(number)제곱근을 계산하는 방법에 대한 논리 외에는 아무것도 알 필요가 없습니다.

따라서 귀하의 예에서 인용 한 "Ball"은 "Box"에 대해 아무 것도 알 수 없습니다. 박스와 볼은 완전히 다른 아이디어이며 서로 대화 할 권리가 없습니다. 그러나 물리 엔진은 Box and Ball이 무엇인지 (또는 적어도 ThreeDShape) 알고 있으며, 현재 위치와 발생해야하는 부분을 알고 있습니다. 따라서 공이 차갑기 때문에 공이 줄어들면 물리 엔진은 공 인스턴스에 공이 작다는 것을 알 수 있습니다.

차를 만드는 것과 비슷합니다. 컴퓨터 칩은 자동차 엔진에 대해 아무것도 모르지만 자동차는 컴퓨터 칩을 사용하여 엔진을 제어 할 수 있습니다. 작고 간단한 것을 함께 사용하여 조금 더 크고 복잡한 것을 만드는 간단한 아이디어는 다른 복잡한 부분의 구성 요소로 재사용 할 수 있습니다.

그리고 마리오 예에서, 적을 만져도 마리오 동전을 빼지 않고 그 방에서 꺼내는 도전 방에 있다면 어떨까요? 마리오 나 적의 아이디어 공간 밖에서는 마리오가 적을 만질 때 동전을 잃어야합니다 (사실 마리오가 무적의 별을 가지고 있다면 그는 대신 적을 죽입니다). 마리오가 적에게 닿을 때 발생하는 일을 담당하는 모든 대상 (도메인 / 아이디어)은 그 중 하나에 대해 알아야 할 대상 중 하나만 수행해야합니다. ).

또한 children을 호출하는 객체에 대한 진술 로 다른 부모로부터 프레임 당 여러 번 호출 Update()되는 것처럼 버그가 발생하기 쉽습니다 Update. (이것을 잡더라도 CPU 시간이 낭비되어 게임 속도가 느려질 수 있습니다.) 모든 사람은 필요할 때 필요한 부분 만 만져야합니다. Update ()를 사용하는 경우 일정 형식의 구독 패턴을 사용하여 모든 업데이트가 프레임 당 한 번만 호출되도록해야합니다 (Unity 에서처럼 처리되지 않는 경우)

OOP를 얼마나 잘 활용할 수 있는지에 대한 가장 중요한 요소는 도메인 아이디어를 명확하고 격리 된 잘 정의되고 사용하기 쉬운 블록으로 정의하는 방법을 배우는 것입니다.


1

객체 지향 게임 개발에 익숙해 지도록 만든 매우 간단한 게임 인 BoxPong을 만나십시오.

BoxPong을 만드는 것은 무엇보다도 근본적인 질문을 공식화하는 데 도움이되었습니다. 서로 "연장"하지 않고 서로 상호 작용하는 객체를 어떻게 가질 수 있습니까?

SNES를위한 코드를 작성해 본 경험이 거의 있습니다. 여기서 여러분은 거의 모든 RAM에 액세스 할 수 있습니다. 수퍼 마리오 월드의 커스텀 적을 만들고 있다고해서 마리오의 모든 코인을 제거하고 0을 저장하여 $ 0DBF에 문제를 일으키지 않기를 원한다고 가정하십시오.

객체 지향 프로그래밍의 요점이 빠져있는 것 같습니다.

객체 지향 프로그래밍은 아키텍처에서 특정 주요 종속성을 선택적으로 반전시켜 종속성을 관리하여 강성, 취약성 및 재사용 불가능 성을 방지합니다.

의존이란 무엇입니까? 의존성은 다른 것에 의존합니다. $ 0DBF를 처리하기 위해 0을 저장하면 해당 주소는 Mario의 동전이 위치하고 동전이 정수로 표시된다는 사실에 의존합니다. Custom Enemy 코드는 Mario와 그의 동전을 구현하는 코드에 의존합니다. Mario가 동전을 메모리에 저장하는 위치를 변경하는 경우 메모리 위치를 참조하는 모든 코드를 수동으로 업데이트해야합니다.

객체 지향 코드는 코드가 세부 사항이 아닌 추상화에 의존하도록 만드는 것입니다. 그래서 대신

class Mario
{
    public:
        int coins;
}

당신은 쓸 것입니다

class Mario
{
    public:
        void LoseCoins();

    private:
        int coins;
}

이제 Mario가 동전을 int에서 long 또는 double로 저장하는 방법을 변경하거나 네트워크에 저장하거나 데이터베이스에 저장하거나 다른 긴 프로세스를 시작하려면 한 곳에서 변경하십시오. Mario 클래스 및 기타 모든 코드는 변경없이 계속 작동합니다.

그러므로 물어 보면

서로 "연장"하지 않고 서로 상호 작용하는 개체를 어떻게 가질 수 있습니까?

당신은 정말로 묻고 있습니다 :

추상화없이 서로 직접 의존하는 코드를 어떻게 가질 수 있습니까?

이것은 객체 지향 프로그래밍이 아닙니다.

http://objectmentor.com/omSolutions/oops_what.html에서 모든 것을 읽고 시작한 다음 Robert Martin의 모든 것을 YouTube에서 검색하여 모든 것을보십시오.

내 대답은 그에게서 파생되며 그 중 일부는 그에게서 직접 인용됩니다.


답변 (및 링크 된 페이지; 감사합니다)에 감사드립니다. 실제로 추상화와 재사용 가능성에 대해 알고 있지만 내 대답에 잘 넣지 않은 것 같습니다. 그러나 제공 한 예제 코드에서 지금 내 요점을 더 잘 설명 할 수 있습니다! 당신은 기본적으로 적의 대상이해서는 안된다고 말하고 있습니다 mario.coins = 0;. 그러나 mario.loseCoins();그것은 옳고 진실한 것입니다. 그러나 제 요점은, 어떻게 적이 그 mario대상에 접근 할 수 있습니까? mario의 멤버 변수는 enemy나에게 옳지 않은 것 같습니다.
vvye

간단한 대답은 마리오를 적의 함수에 대한 인수로 전달하는 것입니다. marioNearby () 또는 attackMario ()와 같은 함수를 사용하여 Mario를 인수로 사용할 수 있습니다. 따라서 적과 마리오가 상호 작용해야 할 논리가 트리거 될 때마다 mario.loseCoins ()를 호출하는 enemy.marioNearby (mario)를 호출합니다. 나중에 길을 따라 마리오가 하나의 동전 만 잃거나 심지어 동전을 얻는 원인이 될 수 있습니다. 이제 다른 코드에 부작용을 일으키지 않는 변경을 수행 할 수있는 한 곳이 있습니다.
마크 머핀

마리오를 적에게 넘겨주었습니다. 마리오와 적은 다른 사람조차도 아무 것도 알지 못합니다. 그렇기 때문에 간단한 객체를 결합하는 방법을 관리하기 위해 고차원 객체를 생성하는 이유입니다.
Tezra

@Tezra 그러나 그렇다면이 고차 객체는 전혀 재사용 할 수 없습니까? 이러한 객체는 기능의 역할을하는 것처럼 느껴지며 이들이 보여주는 절차 일뿐입니다.
Steve Chamaillard

@SteveChamaillard 모든 프로그램은 다른 프로그램에서는 의미가없는 최소한의 특정 로직을 갖지만이 로직을 몇 개의 고차원 클래스에 격리시키는 것이 좋습니다. 마리오, 적 및 레벨 클래스가있는 경우 다른 게임에서 마리오와 적을 재사용 할 수 있습니다. 적과 마리오를 서로 직접 연결하는 경우, 하나의 게임이 필요한 게임보다 다른 게임을 끌어 와야합니다.
Tezra

0

중개자 패턴을 적용하여 느슨한 결합을 사용할 수 있습니다. 이를 구현하려면 모든 수신 구성 요소를 알고있는 중개자 구성 요소에 대한 참조가 필요합니다.

"게임 마스터, 이런 일이 일어나게 해주세요"라는 생각을 깨달았습니다.

일반화 된 패턴은 발행-구독 패턴입니다. 중재자가 많은 논리를 포함하지 않아야하는 경우에 적합합니다. 그렇지 않으면 모든 통화를 라우팅하고 수정해야 할 위치를 알고있는 수작업으로 조정 된 중재자를 사용하십시오.

일반적으로 이벤트 버스, 메시지 버스 또는 메시지 큐라는 동기 및 비동기 변형이 있습니다. 그들이 당신의 특정 경우에 적합한 지 확인하기 위해 그들을 찾으십시오.

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