OOP에서 동작이없는 객체-디자인 딜레마 클래스 가있는 세 번째

OOP의 기본 개념은 데이터와 동작 (데이터 위에 있음)을 분리 할 수 ​​없으며 클래스 객체의 아이디어와 연결되어 있다는 것입니다. 객체에는 그와 함께 작동하는 데이터 및 메소드가 있습니다. 분명히 OOP의 원칙에 따르면 데이터 (C 구조체와 같은) 인 객체는 반 패턴으로 간주됩니다.

여태까지는 그런대로 잘됐다.

문제는 요즘 내 코드 가이 안티 패턴의 방향으로 점점 더 많이 가고있는 것으로 나타났습니다. 클래스와 느슨하게 결합 된 디자인 사이에 정보 숨기기를 더 많이 시도할수록 내 클래스는 순수한 데이터 동작 클래스가 아닌 모든 동작 데이터 클래스가 아닌 혼합 클래스가됩니다.

나는 일반적으로 다른 클래스의 존재에 대한 인식을 최소화하고 다른 클래스의 인터페이스에 대한 지식을 최소화하는 방식으로 클래스를 디자인합니다. 나는 특히 이것을 하향식으로 시행합니다. 저급 수업은 높은 수준의 수업에 대해 알지 못합니다. 예 :

일반적인 카드 게임 API가 있다고 가정하십시오. 수업이 Card있습니다. 이제이 Card클래스는 플레이어의 가시성을 결정해야합니다.

한 가지 방법은 수업 boolean isVisible(Player p)Card듣는 것입니다.

또 다른 것입니다 boolean isVisible(Card c)Player클래스입니다.

나는 특히 높은 수준의 Player클래스 에 대한 지식 을 낮은 수준의 Card클래스 에 부여하기 때문에 첫 번째 접근법을 싫어합니다 .

대신, 우리는 Viewport클래스가 있고 Player카드 목록에 따라 어떤 카드가 보이는지 결정 하는 클래스 가있는 세 번째 옵션을 선택했습니다 .

그러나이 방법은 가능한 멤버 함수의 클래스 CardPlayer클래스를 모두 빼앗습니다 . 당신이 카드의 가시성 이외의 물건에 대해이 작업을 수행하면, 당신은 남아 있습니다 CardPlayer모든 기능이 대부분 같은 데이터가없는, 단지 방법과 클래스를 다른 클래스에서 구현 될 때 순수하게 데이터를 포함하는 클래스 Viewport위.

이것은 OOP의 주요 아이디어와는 분명히 반대입니다.

올바른 방법은 무엇입니까? 클래스 상호 종속성을 최소화하고 가정 지식과 결합을 최소화하는 작업을 어떻게 수행해야합니까? 그러나 모든 하위 레벨 클래스에 데이터 만 포함되고 상위 레벨 클래스에 모든 메소드가 포함되는 이상한 디자인으로 마무리하지 마십시오. 누구든지 전체 문제를 피하는 클래스 디자인에 대한 세 번째 해결책이나 관점이 있습니까?

추신 : 여기 또 다른 예가 있습니다 :

DocumentId변경할 수없는 클래스 가 있고이 BigDecimal id멤버에 대한 단일 멤버와 getter 만 있다고 가정하십시오 . 이제 어딘가에 메소드가 필요합니다.이 메소드 는 데이터베이스 에서이 ID에 대한 DocumentId리턴 Document을 제공 합니다.

당신은 :

  • 클래스에 Document getDocument(SqlSession)메소드를 추가 DocumentId하고 갑자기 퍼시스턴스 ( "we're using a database and this query is used to retrieve document by id"), DB에 액세스하는 데 사용되는 API 등에 대한 지식을 소개합니다 . 또한이 클래스는 이제 컴파일하기 위해 지속성 JAR 파일이 필요합니다.
  • method가있는 다른 클래스를 추가하여 클래스를 죽은 Document getDocument(DocumentId id)상태로두고 DocumentId동작이없고 구조체와 유사한 클래스를 유지하십시오.


답변

당신이 설명하는 것을 빈혈 도메인 모델이라고 합니다. 많은 OOP 디자인 원칙 (Delaw of Demeter 등)과 마찬가지로 규칙을 만족시키기 위해 거꾸로 구부릴 가치가 없습니다.

그들이 전체 풍경을 어지럽히 지 않고 자신을 위해수있는 하우스 키핑을하기 위해 다른 물건에 의존하지 않는 한 가치있는 가방을 갖는 것에 대해 잘못된 것은 없습니다 .

속성을 수정하기 위해 별도의 클래스가있는 경우 분명히 코드 냄새 Card일 것입니다. 합리적으로 처리 할 것으로 예상되는 경우.

그러나 실제로 Card어떤 Player것이 보이는지 아는 것이 직업 입니까?

왜 구현 Card.isVisibleTo(Player p)하지는 Player.isVisibleTo(Card c)않습니까? 혹은 그 반대로도?

그렇습니다 Player. Card(?) 보다 더 높은 수준을 유지하는 것과 같은 규칙을 생각해 볼 수는 있지만 추측하기가 쉽지 않으며 두 곳 이상을 살펴 봐야합니다. 방법을 찾으십시오.

그것을 구현하는 썩은 디자인을 손상시킬 수있는 시간이 지남 isVisibleTo모두 CardPlayer내가 생각하는 클래스, 노 전혀 없다. 왜 그래? 내가 생각 player1.isVisibleTo(card1)했던 것과는 다른 가치를 돌려 줄 부끄러운 날을 이미 상상하기 때문에 card1.isVisibleTo(player1).-주관적이다-이것은 계획적으로 불가능 해져야 한다 .

카드와 플레이어의 상호 가시성은 일종의 컨텍스트 객체 ( 또는 그 Viewport와 같은)에 의해 더 잘 관리되어야합니다 .DealGame

전역 기능을 갖는 것과 다릅니다. 결국, 많은 동시 게임이있을 수 있습니다. 많은 테이블에서 동일한 카드를 동시에 사용할 수 있습니다. Card스페이드 에이스마다 많은 인스턴스를 생성할까요 ?

나는 여전히 isVisibleToon을 구현할 수 Card있지만 컨텍스트 객체를 전달 Card하고 쿼리를 위임합니다. 높은 커플 링을 피하기 위해 인터페이스 프로그램.

두 번째 예에서-문서 ID가로만 구성된 경우 BigDecimal래퍼 클래스를 작성하는 이유는 무엇입니까?

나는 당신이 필요로하는 것은 DocumentRepository.getDocument(BigDecimal documentID);

그런데 Java는 없지만 structC # 에는 s 가 있습니다 .

보다

참고로. 고도로 객체 지향적 인 언어이지만 아무도 그것을 많이 사용하지 않습니다.


답변

OOP의 기본 개념은 데이터와 동작 (데이터 위에 있음)을 분리 할 수 ​​없으며 클래스 객체의 아이디어와 연결되어 있다는 것입니다.

클래스가 OOP 의 기본 개념 이라고 가정하면 일반적인 오류가 발생 합니다. 클래스는 캡슐화를 달성하는 데 특히 널리 사용되는 방법 중 하나입니다. 그러나 우리는 그것을 미끄러지게 할 수 있습니다.

일반적인 카드 게임 API가 있다고 가정하십시오. 수업 카드가 있습니다. 이제이 카드 클래스는 플레이어의 가시성을 결정해야합니다.

좋은 소식이 없습니다. Bridge를 플레이 할 때 더미의 손을 오직 비밀로 알려진 더미에서 모두에게 알려진 것으로 바꿀 때가되면 7 개의 하트요청 하십니까? 당연히 아니지. 그것은 전혀 카드의 문제가 아닙니다.

한 가지 방법은 카드 클래스에 부울 isVisible (Player p)을 두는 것입니다. 또 다른 방법은 Player 클래스에 부울 isVisible (Card c)을 두는 것입니다.

둘 다 무섭다. 그 중 어느 것도하지 마십시오. 어느 쪽도 아니 플레이어카드 브리지의 규칙을 구현하기위한 책임이 없습니다!

대신 플레이어와 카드 목록이 표시되는 카드를 결정하는 Viewport 클래스가있는 세 번째 옵션을 선택했습니다.

나는 “뷰포트”로 카드를 한 번도 본 적이 없기 때문에이 클래스가 무엇을 캡슐화해야하는지 전혀 모른다. 나는 카드의 두 데크, 몇몇 선수, 테이블, 그리고 호일의 사본 카드를했다. 뷰포트는 어떤 것을 나타 냅니까?

그러나이 방법은 가능한 멤버 함수의 카드 및 플레이어 클래스를 모두 빼앗습니다.

좋은!

카드의 가시성 이외의 다른 작업을 위해이 작업을 수행하면 모든 기능이 데이터가없는 클래스 인 위의 뷰포트와 같은 메서드 인 다른 클래스에서 구현되므로 순수한 데이터를 포함하는 카드 및 플레이어 클래스가 남습니다. 이것은 OOP의 주요 아이디어와는 분명히 반대입니다.

아니; OOP의 기본 아이디어는 객체가 자신의 관심사를 캡슐화 한다는 입니다. 시스템에서 카드는 그다지 걱정하지 않습니다. 플레이어도 아닙니다. 세상을 정확하게 모델링하기 때문입니다 . 실제 세계에서 게임과 관련된 카드는 매우 간단합니다. 게임의 플레이를 크게 바꾸지 않고도 카드의 그림을 1에서 52까지의 숫자로 바꿀 수 있습니다. 우리는이 게임의 플레이를 크게 바꾸지 않고도 네 사람을 북쪽, 남쪽, 동쪽 및 서쪽으로 표시된 마네킹으로 바꿀 수있었습니다. 플레이어와 카드는 카드 게임 세계에서 가장 간단한 것들입니다. 규칙은 복잡하기 때문에 규칙을 나타내는 클래스는 합병증이있는 곳입니다.

이제 플레이어 중 하나가 AI 인 경우 내부 상태가 매우 복잡 할 수 있습니다. 그러나 AI는 카드를 볼 수 있는지 여부를 결정하지 않습니다. 규칙에 따라 결정 됩니다.

시스템을 설계하는 방법은 다음과 같습니다.

우선, 덱이 2 개 이상인 게임이 있으면 카드는 놀랍도록 복잡합니다. 당신은 질문을 고려해야합니다 : 플레이어는 같은 순위의 두 카드를 구별 할 수 있습니까? 플레이어 1이 하트 7 개 중 하나를 연주 한 후 어떤 일이 발생하고, 플레이어 2가 하트 7 개 중 하나를 연주하는 경우, 플레이어 3 이 하트 7이 같은 것으로 판단 할 수 있습니까? 이것을 신중하게 고려하십시오. 그러나 이러한 우려 외에도 카드는 매우 단순해야합니다. 그들은 단지 데이터 일뿐입니다.

다음으로 플레이어의 본질은 무엇입니까? 플레이어는 소비 의 순서 보이는 행동을 하고 생산 조치를 .

규칙 객체는이 모든 것을 조정하는 것입니다. 이 규칙은 일련의 눈에 보이는 행동을 만들어 플레이어에게 알려줍니다.

  • 플레이어 1, 하트 10이 플레이어 3에게 전달되었습니다.
  • 플레이어 2, 플레이어 3이 카드를 플레이어 1에게 건네 주었다.

그런 다음 플레이어에게 조치를 요청합니다.

  • 플레이어 하나, 당신은 무엇을하고 싶습니까?
  • 플레이어 1의 말 : Fromp를 고음.
  • 플레이어 1은 트레블 링 된 fromp가 방어 할 수없는 bit 빗을 생성하기 때문에 불법적 인 행동입니다.
  • 플레이어 하나, 당신은 무엇을하고 싶습니까?
  • 플레이어 1의 말 : 스페이드의 여왕을 버린다.
  • 2 번 선수, 1 번 선수는 스페이드의 여왕을 버렸습니다.

등등.

정책과 메커니즘을 분리하십시오 . 게임 의 정책 은 카드가 아닌 정책 객체에 캡슐화되어야합니다 . 카드는 메커니즘 일뿐입니다.


답변

데이터와 행동의 결합이 OOP의 중심 아이디어라는 것이 맞지만 그 이상이 있습니다. 예를 들어, 캡슐화 : OOP / 모듈 식 프로그래밍을 사용하면 구현 인터페이스와 공용 인터페이스를 분리 할 수 ​​있습니다. OOP에서 이는 데이터에 공개적으로 액세스 할 수 없으며 접근자를 통해서만 사용해야한다는 것을 의미합니다. 이 정의에 따르면 메소드가없는 객체는 실제로 쓸모가 없습니다.

접근 자 이외의 메서드를 제공하지 않는 클래스는 본질적으로 너무 복잡한 구조체입니다. 그러나 OOP는 내부 세부 사항을 변경할 수있는 유연성을 제공하기 때문에 나쁘지 않습니다. 예를 들어, 멤버 필드에 값을 저장하는 대신 매번 다시 계산할 수 있습니다. 또는 백업 알고리즘이 변경되어 추적해야하는 상태가됩니다.

OOP는 분명한 장점이 있지만 (일반적인 절차 적 프로그래밍에 비해), “순수한”OOP를 추구하는 것은 순진합니다. 일부 문제는 객체 지향 접근 방식에 잘 맞지 않으며 다른 패러다임에 의해 더 쉽게 해결됩니다. 그러한 문제가 발생하면 열등한 접근법을 고집하지 마십시오.

  • 객체 지향 방식으로 피보나치 시퀀스 계산해보십시오 . 나는 그것을하는 건전한 방법을 생각할 수 없다. 간단한 구조화 된 프로그래밍은이 문제에 대한 최상의 솔루션을 제공합니다.

  • 귀하의 isVisible관계는 두 클래스 모두에 속하거나 어느 쪽도 또는 실제로는 컨텍스트 에 속하지 않습니다 . 동작없는 레코드는 일반적으로 기능적 또는 절차 적 프로그래밍 방식으로, 문제에 가장 적합한 것으로 보입니다. 아무 문제가 없습니다

    static boolean isVisible(Card c, Player p);
    

    접근 자 와 접근 Card자를 넘어서는 방법 이 없다는 것은 아무 문제가 없습니다 .ranksuit


답변

OOP의 기본 개념은 데이터와 동작 (데이터 위에 있음)을 분리 할 수 ​​없으며 클래스 객체의 아이디어와 연결되어 있다는 것입니다. 객체에는 그와 함께 작동하는 데이터 및 메소드가 있습니다. 분명히 OOP의 원칙에 따르면 데이터 (C 구조체와 같은) 인 객체는 반 패턴으로 간주됩니다. (…) 이것은 OOP의 주요 아이디어와는 분명히 반대입니다.

이것은 상당히 잘못된 건물을 기반으로하기 때문에 어려운 질문입니다.

  1. OOP가 코드를 작성하는 유일한 유효한 방법이라는 아이디어.
  2. OOP가 잘 정의 된 개념이라는 생각. OOP가 무엇인지에 대해 동의 할 수있는 두 사람을 찾기가 어려워지는 화두가되었습니다.
  3. OOP는 데이터와 행동을 묶는 것입니다.
  4. 모든 것이 추상화되어야한다는 생각.

나는 각자 자신의 답변을 낼 수 있기 때문에 # 1-3을 많이 다루지 않을 것이며 많은 의견 기반 토론을 불러 일으킨다. 그러나 “OOP는 데이터와 행동의 결합에 관한 것”이라는 아이디어가 특히 문제가된다는 것을 알게되었습니다. 그것은 # 4로 이어질뿐만 아니라 모든 것이 방법이어야한다는 생각으로 이어집니다.

유형을 정의하는 작업과 해당 유형을 사용하는 방법에는 차이가 있습니다. i배열의 개념 에는 th 요소 를 검색 할 수 있어야 하지만 정렬은 하나를 사용하여 선택할 수있는 많은 것 중 하나 일뿐입니다. 정렬은 “짝수 요소 만 포함하는 새 배열을 생성”하는 것 이상의 방법 일 필요는 없습니다.

OOP는 객체 사용에 관한 것입니다. 객체는 추상화를 달성하는 한 가지 방법 일뿐 입니다. 추상화는 코드에서 불필요한 결합을 피하는 수단이며, 그 자체가 목적은 아닙니다. 카드의 개념이 그 스위트와 등급의 가치에 의해서만 정의된다면, 간단한 튜플이나 레코드로 구현하는 것이 좋습니다. 코드의 다른 부분이 의존성을 형성 할 수있는 필수적이지 않은 세부 사항은 없습니다. 때로는 숨길 것이 없습니다.

(반투명하거나 불투명하게 변할 수있는 매우 특별한 카드가 없다면) 카드의 개념에 꼭 필요한 것은 아니기 때문에 유형 isVisible의 방법을 만들지 않을 것 Card입니다. Player유형 의 방법이어야 합니까? 음, 그것은 아마도 선수의 질을 정의하는 것이 아닐 수도 있습니다. 어떤 Viewport유형 의 일부 여야합니까 ? 다시 한번 이것은 뷰포트를 정의하는 것과 카드의 가시성을 확인하는 개념이 뷰포트를 정의하는 데 필수적인지 여부에 달려 있습니다.

그것은 매우 가능성이 isVisible그냥 무료로 기능해야한다.


답변

분명히 OOP의 원칙에 따르면 데이터 (C 구조체와 같은) 인 객체는 반 패턴으로 간주됩니다.

아니요, 그렇지 않습니다. Plain-Old-Data 객체는 완벽하게 유효한 패턴이며, 프로그램의 개별 영역간에 유지되거나 통신되어야하는 데이터를 다루는 모든 프로그램에서 예상됩니다.

데이터 계층 테이블 Player에서 읽을 때 전체 클래스를 스풀링 할 있지만 Players대신 테이블의 필드와 함께 POD를 반환하는 일반 데이터 라이브러리 일 수 있습니다. 구체적인 Player수업에 플레이어 POD .

유형이 지정되었거나 유형이 지정되지 않은 데이터 개체를 사용하면 프로그램에서 의미가 없을 수 있지만 반 패턴이되지는 않습니다. 이해가된다면 사용하고 그렇지 않으면 사용하지 마십시오.


답변

개인적으로 Domain Driven Design이이 문제를 명확하게하는 데 도움이된다고 생각합니다. 내가 묻는 질문은 카드 게임을 인간에게 어떻게 설명 하는가입니다. 다시 말해, 나는 무엇을 모델링하고 있는가? 모델링하는 것에 “viewport”라는 단어와 그 동작과 일치하는 개념이 실제로 포함되어 있으면 뷰포트 객체를 만들어 논리적으로 수행해야합니다.

그러나 내 게임에 뷰포트 개념이 없으면 코드가 잘못 느껴지기 때문에 필요한 것으로 생각됩니다. 내 도메인 모델을 추가하는 것에 대해 두 번 생각합니다.

모델이라는 단어는 무언가를 표현하고 있음을 의미합니다. 나는 당신이 대표하는 것 이상으로 추상적 인 것을 나타내는 클래스에 넣지 않도록주의합니다.

디스플레이와 인터페이스 해야하는 경우 코드의 다른 부분에 뷰포트 개념이 필요할 수 있음을 추가하도록 편집 할 것입니다. 그러나 DDD 용어에서 이는 인프라 문제 일 수 있으며 도메인 모델 외부에 존재할 수 있습니다.


답변

나는 일반적으로 자체 홍보를하지 않지만 사실은 내 블로그 에 OOP 디자인 문제에 대해 많이 썼습니다 . 여러 페이지를 요약하면 클래스로 디자인을 시작해서는 안됩니다. 인터페이스 또는 API 및 형태 코드부터 시작하여 의미있는 추상화를 제공하고 사양을 충족하며 재사용 할 수없는 코드로 콘크리트 클래스를 부 풀릴 수있는 가능성이 높아집니다.

이 적용 방법 CardPlayer문제 : 만들기 ViewPort당신이 생각하는 경우 추상화하는 것은 의미가 CardPlayer두 개의 독립적 인 라이브러리를 (을 암시하는 것으로 Player때로는없이 사용 Card). 그러나 나는 Player보류 를 생각 하고 그들에게 접근자를 Cards제공 해야하는 경향 Collection<Card> getVisibleCards ()이 있습니다. 이 두 솔루션 ( ViewPort및 광산)가 제공하는 것보다 더 나은 isVisible방법으로 Card또는 Player이해할 수있는 코드 관계를 만드는 측면에서.

외부 솔루션은 훨씬 더 좋습니다 DocumentId. 복잡한 데이터베이스 라이브러리에 의존하도록 (기본적으로 정수) 동기가 거의 없습니다.