OOP에서 동작이없는 객체-디자인 딜레마


94

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동작이없고 구조체와 유사한 클래스를 유지하십시오.

21
여기에있는 당신의 전제 중 일부는 완전히 잘못되어있어 근본적인 질문에 대답하기가 매우 어려울 것입니다. 가능한 한 간결하고 의견이없는 질문을 유지하면 더 나은 답변을 얻을 수 있습니다.
pdr

31
"이것은 OOP의 주된 아이디어와는 분명히 반대입니다."-아닙니다. 그러나 일반적인 오류입니다.
Doc Brown

5
문제는 과거에 "오브젝트 오리엔테이션"에 대해 다른 학교가 있었다는 사실에 있습니다. 원래 Alan Kay와 같은 사람들이 의도했던 방식입니다 ( geekswithblogs.net/theArchitectsNapkin/archive/2013/09/08/ … ), 합법적 인 사람들 ( en.wikipedia.org/wiki/Object-oriented_analysis_and_design ) 이 OOA / OOD와 관련하여 방법을 가르쳤습니다 .
Doc Brown

21
이것은 매우 좋은 질문이며 잘 게시되어 있습니다-다른 의견 들과는 달리, 나는 말할 것입니다. 프로그램을 구성하는 방법에 대한 대부분의 조언이 얼마나 순진하거나 불완전한 지, 그리고 프로그램을 수행하는 것이 얼마나 어려우며, 많은 상황에서 올바른 디자인을 달성하기 위해 얼마나 많은 노력을 기울이고 있는지에 대해 알 수 없습니다. 그리고 특정 질문에 대한 명백한 대답은 다중 방법이지만 디자인의 근본적인 문제는 지속됩니다.
Thiago Silva

5
무자비한 수업은 반 패턴이라고 누가 말합니까?
James Anderson

답변:


42

당신이 설명하는 것을 빈혈 도메인 모델이라고 합니다. 많은 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 가 있습니다 .

보다

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


1
C #의 구조에 대한 참고 사항 : C에서 아는 것처럼 일반적인 구조는 아닙니다. 실제로 상속, 캡슐화 및 다형성으로 OOP를 지원합니다. 몇 가지 특징 외에 주요 차이점은 런타임이 다른 객체에 전달 될 때 인스턴스가 처리하는 방식입니다. 구조는 값 유형이고 클래스는 참조 유형입니다!
Aschratt

3
@Aschratt : 구조체는 상속을 지원하지 않습니다. 구조체는 인터페이스를 구현할 수 있지만 인터페이스를 구현하는 구조체는 이와 유사한 클래스 객체와 다르게 동작합니다. 구조체가 객체처럼 동작하도록 만들 수는 있지만 C 구조체처럼 동작하는 것을 원할 때 구조체에 가장 적합한 사용 사례이며 캡슐화하는 것은 기본 또는 변경 불가능한 클래스 유형입니다.
supercat

1
"전역 기능을 갖는 것과 같지 않은 이유"+1 이것은 다른 사람들이 많이 다루지 않았습니다. (데크가 여러 개인 경우에도 전역 함수는 여전히 동일한 카드의 개별 인스턴스에 대해 다른 값을 반환합니다).
Alexis

@supercat 이것은 별도의 질문이나 채팅 세션에 적합하지만 현재 다음 중 어느 것에도 관심이 없습니다 :-( C #에서 "인터페이스를 구현하는 구조는 클래스 객체와 다르게 작동합니다."라고 동의합니다. 고려해야 할 다른 행동상의 차이가 있지만, 코드 다음 Interface iObj = (Interface)obj;에 나오는 AFAIK 의 행동은의 상태 나 상태에 iObj영향을받지 않습니다 (이 경우 해당 과제에서 박스 사본 이 될 경우 제외 )structclassobjstruct
Mark Hurd

150

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 번 선수는 스페이드의 여왕을 버렸습니다.

등등.

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


41
@gnat : Alan Kay의 반대 의견은 "실제로"객체 지향 "이라는 용어를 만들었고 C ++을 염두에두고 있지 않다는 것을 알 수 있습니다." 클래스가없는 OO 언어가 있습니다. JavaScript가 떠 오릅니다.
Eric Lippert

19
@ gnat : 오늘날 JS가 OOP 언어의 훌륭한 예는 아니지만 클래스가없는 OO 언어를 쉽게 만들 수 있음을 동의합니다. 나는 Eiffel과 C ++에서 OO-ness의 기본 단위가 클래스라는 것에 동의한다. 내가 동의하지 않는 곳은 클래스가 OO 의 사인 이 아니라는 개념이다 . OO 의 사인 은 동작을 캡슐화하고 잘 정의 된 공용 인터페이스를 통해 서로 통신하는 객체입니다.
Eric Lippert

16
나는 @EricLippert에 동의합니다. 주류가 말하는 것이 무엇이든 클래스는 OO의 기본이 아니며 상속도 아닙니다. 그러나 데이터, 행동 및 책임의 캡슐화는 달성됩니다. 있다 프로토 타입 기반 언어 OO하지만 계급입니다 자바 스크립트 이상은. 이러한 개념에 대한 상속에 중점을 두는 것은 특히 실수입니다. 즉, 클래스는 행동 캡슐화를 구성하는 데 매우 유용한 수단입니다. 클래스를 객체로 취급 할 수 있고 프로토 타입 언어로도 선을 흐리게 처리 할 수 ​​있습니다.
Schwern

6
이런 식으로 생각하십시오 : 실제 세계에서 실제 카드는 어떤 행동을 하는가? 대답은 "없음"이라고 생각합니다. 다른 것들이 카드에 작용합니다. 실제 세계에서 카드 자체 문자 그대로 정보 (클럽 중 4 개)이며 어떤 종류의 본질적인 행동도 없습니다. 그 정보 (일명 "카드")가 사용되는 방식은 "규칙"과 "플레이어"라고 불리는 다른 사람 / 다른 사람에게 100 %까지입니다. 동일한 카드를 다양한 플레이어가 사용할 수있는 무한한 (다양한) 다양한 게임에 사용할 수 있습니다. 카드는 카드 일 뿐이며 속성 만 있으면됩니다.
Craig

5
@Montagist : 그때 좀 명확하게하겠습니다. C를 생각해보십시오. C에는 수업이 없다는 데 동의 할 것입니다. 그러나 구조체는 "클래스"라고 말할 수 있고 함수 포인터 유형의 필드를 만들 수 있고 vtable을 만들 수 있으며 vstruct를 설정하는 "constructors"라는 메서드를 만들어 일부 구조체가 서로 "상속"되도록 할 수 있습니다. 등등. 당신은 할 수 에뮬레이트 C에서 클래스 기반 상속을 그리고 당신은 할 수 에뮬레이션 JS에 있습니다. 그러나 그렇게하는 것은 아직 존재하지 않는 언어 위에 무언가를 구축하는 것을 의미합니다.
Eric Lippert

29

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

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

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

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

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

    static boolean isVisible(Card c, Player p);
    

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


11
@ UMad 예, 그것은 정확히 내 요점이며 그에 아무런 문제가 없습니다. 작업에 맞는 올바른 <del> 언어 </ del> 패러다임 을 사용하십시오. (Smalltalk 이외의 대부분의 언어는 순수한 객체 지향이 아닙니다. 예를 들어 Java, C # 및 C ++는 명령형, 구조적, 절차 적, 모듈 식, 기능적 및 객체 지향 프로그래밍을 지원합니다. 모든 비 OO 패러다임은 이유가 있습니다. : 당신이 그들을 사용할 수 있도록)
amon

1
Fibonacci를 수행하는 합리적인 OO 방법 fibonacciinteger있습니다. 의 인스턴스 에서 메소드를 호출하십시오 . OO가 캡슐화에 관한 것, 심지어 작은 장소에서도 그렇다는 점을 강조하고 싶습니다. 정수가 작업을 수행하는 방법을 알아 내도록하십시오. 나중에 구현을 개선하고 캐싱을 추가하여 성능을 향상시킬 수 있습니다. 함수와 달리 메서드는 데이터를 따르므로 모든 호출자가 향상된 구현의 이점을 얻습니다. 임의의 정밀 정수는 나중에 추가 될 수 있으며, 일반 정수처럼 투명하게 처리 될 수 있으며 자체 성능 조정 fibonacci방법을 가질 수 있습니다 .
Schwern

2
@Schwern 어떤 Fibonacci것이 추상 클래스의 서브 클래스 Sequence라면, 시퀀스는 임의의 숫자 세트에 의해 활용되며 시드, 상태, 캐싱 및 반복자를 저장하는 역할을합니다.
George Reith

2
나는 "순수한 OOP 피보나치"가 얼간이 저격에 효과적 일 것으로 기대하지 않았다 . 특정 엔터테인먼트 가치가 있었음에도 불구하고 이러한 의견에 대한 순환 토론을 중단하십시오. 이제 모두 변화를위한 건설적인 일을합시다!
amon

3
피보나치를 정수 방법으로 만드는 것은 어리석은 일입니다. 그래서 OOP라고 말할 수 있습니다. 함수이므로 함수처럼 취급해야합니다.
immibis

19

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

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

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

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

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

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

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

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


1
마음이없는 droning 대신 상식 +1.
rightfold

내가 읽은 줄들에서, 당신이 연결 한 에세이는 내가 한동안 읽지 않은 하나의 확실한 읽기처럼 보입니다.
Arthur Havlicek

@ArthurHavlicek 코드 샘플에 사용 된 언어를 이해하지 못하면 따라 가기가 더 어려워 지지만 상당히 밝습니다.
Doval

9

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

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

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

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


5
당신이 한 말에 동의하지 않지만, 이것은 전혀 질문에 대한 답이 아닙니다. 즉, 나는 대답보다 그 질문을 더 비난합니다.
pdr

2
사실, 카드와 문서는 실제 환경에서도 정보의 컨테이너 일 뿐이며이를 처리 할 수없는 "패턴"은 무시해야합니다.
JeffO

1
Plain-Old-Data objects are a perfectly valid pattern 나는 그들이 그렇지 않다고 말하지 않았고, 그들이 응용 프로그램의 전체 절반을 채울 때 잘못되었다고 말하고 있습니다.
RokL

8

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

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

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

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


위의 Lippert의 답변은이 개념의 더 좋은 예입니다.
RibaldEddie

5

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

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

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


나는 당신의 블로그를 좋아합니다.
RokL

3

본인의 질문이 올바른 수준으로 답변되고 있는지 잘 모르겠습니다. 나는 포럼에서 현명한 질문에 대한 핵심을 적극적으로 생각할 것을 촉구했다.

U Mad는 OOP에 대한 자신의 이해에 따라 프로그래밍하면 일반적으로 많은 리프 노드가 데이터 홀더가되고 상위 API는 대부분의 동작으로 구성된다고 생각하는 상황을 제기하고 있습니다.

이 주제는 isVisible이 Card vs Player에서 정의 될지 여부에 약간의 문제가 있다고 생각합니다. 순진하지만 예시 된 단순한 예였습니다.

나는 여기에 경험이 풍부한 사람들이 당면한 문제를 보도록 강요했다. U Mad가 추진 한 좋은 질문이 있다고 생각합니다. 본인은 규칙과 관련 논리를 자신의 목표에 적용 할 것임을 이해합니다. 하지만 질문을 이해하면

  1. 실제로 많은 기능을 제공하지 않는 간단한 데이터 홀더 구성 (클래스 / 구조체;이 질문에 대해 모델링 된 것에 대해서는 신경 쓰지 않습니다)을 갖는 것이 괜찮습니까?
  2. 그렇다면, 모델링하는 가장 좋은 방법은 무엇입니까?
  3. 그렇지 않은 경우이 데이터 카운터 파트를 상위 API 클래스 (동작 포함)에 어떻게 통합합니까?

내 견해 :

객체 지향 프로그래밍에서 올바르게 구하기 어려운 세분성에 대한 질문을하고 있다고 생각합니다. 내 작은 경험에서, 나는 행동을 포함하지 않는 엔티티를 모델에 포함시키지 않을 것입니다. 내가해야한다면 데이터와 행동을 캡슐화하려는 아이디어와는 달리 추상화를 유지하도록 설계된 구조체를 사용했을 것입니다.


3
문제는 "일반적인"일을하는 방법에 관한 질문입니다. 사실, 우리는 결코 "일반적인"일 을 하지 않습니다 . 우리는 항상 특정한 일을합니다. 우리의 구체적인 사물이 상황에 맞는 것인지 판단하기 위해 우리의 구체적인 사물을 조사하고 우리의 요구 사항과 비교하여이를 측정해야합니다.
John Saunders

@JohnSaunders 나는 당신의 지혜를 여기에서 인식하고 어느 정도 동의하지만 문제를 해결하기 전에 단지 개념적 접근이 필요합니다. 결국, 여기의 질문은 생각보다 개방적이지 않습니다. OOO의 초기 사용법에서 모든 OO 디자이너가 직면하는 유효한 OOD 질문이라고 생각합니다. 당신의 테이크는 무엇입니까? 결심이 도움이된다면, 우리는 당신이 선택한 모범을 세우는 것에 관해 이야기 할 수 있습니다.
Harsha

나는 35 년 넘게 학교를 다녔습니다. 실제 세계에서는 "개념적 접근 방식"에서 가치가 거의 없습니다. 이 경우 Meyers보다 더 나은 교사가되는 경험이 있습니다.
John Saunders

데이터와 클래스의 행동 구분에 대한 클래스를 실제로 이해하지 못합니다. 객체를 올바르게 추상화하면 구별이 없습니다. Pointwith getX()함수를 상상해보십시오 . 속성 중 하나를 얻는다고 상상할 수 있지만 디스크 또는 인터넷에서 읽을 수도 있습니다. 얻고 설정하는 것은 행동이며, 그렇게하는 수업은 완전히 좋습니다. 데이터베이스는 데이터를 가져 와서 만 설정
Arthur Havlicek

@ArthurHavlicek : 클래스 가하지 않는 것을 아는 것은 클래스 하는 것을 아는 것 만큼이나 유용합니다. 계약에서 공유 할 수없는 변경 불가능한 데이터 홀더 이상의 역할을하거나 공유 할 수없는 변경 가능한 데이터 홀더 이상의 역할을하도록 지정하는 것이 유용합니다.
supercat

2

OOP의 일반적인 혼동의 원인 중 하나는 많은 객체가 상태의 두 가지 측면, 즉 그들이 알고있는 것과 그들이 알고있는 것을 캡슐화한다는 사실에서 비롯됩니다. 객체의 상태에 대한 논의는 후자의 측면을 종종 무시합니다. 객체 참조가 무차별 한 프레임 워크에서는 외부 세계에 참조가 노출 된 객체에 대해 무엇을 알고 있을지 결정할 수있는 일반적인 방법이 없기 때문입니다.

CardEntity카드의 이러한 측면을 별도의 구성 요소로 캡슐화 하는 객체 를 갖는 것이 도움이 될 것입니다 . 하나의 구성 요소는 카드의 표시와 관련이 있습니다 (예 : "다이아몬드 킹"또는 "라바 블래스트; 플레이어는 AC-3이 피하거나 다른 2D6 피해를 입을 수 있습니다"). 위치와 같은 상태의 고유 한 측면과 관련이있을 수 있습니다 (예 : 갑판, Joe의 손 또는 Larry 앞의 테이블에 있음). 세 번째는 그것을 볼 수있는 것과 관련이있을 수 있습니다 (아마도 아무도, 한 명의 플레이어 또는 많은 플레이어). 모든 것이 동기화되도록하기 위해 카드가 단순한 필드가 아닌 CardSpace객체 로 캡슐화 될 수있는 장소 . 카드를 공간으로 옮기려면 적절한 카드를 참조하십시오CardSpace목적; 그런 다음 이전 공간에서 자신을 제거하고 새 공간에 넣습니다).

"X에 대해 알고있는 사람"을 "X에 대해 알고있는 것"과 별도로 캡슐화하면 많은 혼란을 피하는 데 도움이됩니다. 메모리 누수를 피하기 위해 때로는 많은 수의 관계에서주의를 기울여야합니다 (예 : 새 카드가 존재하고 오래된 카드가 사라지면 버려야하는 카드가 오래 지속되는 물체에 영구적으로 부착되지 않도록해야합니다) ) 그러나 객체에 대한 참조가 존재하면 상태의 관련 부분을 형성하는 경우 객체 자체가 그러한 정보를 명시 적으로 캡슐화하는 것이 좋습니다 (실제로 다른 클래스에 실제로 관리하는 작업을 위임하더라도).


0

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

그리고 그것은 어떻게 나쁜 / 잘못 조언되어 있습니까?

카드 예제와 유사한 비유를 사용하려면 Car, a를 고려하고을 구동 할 수 Driver있는지 확인해야 Driver합니다 Car.

자, 당신은 당신이 올바른 자동차 키를 가지고 Car있는지 여부를 알기를 원하지 않기로 결정했고 Driver, 알 수없는 이유로 당신 DriverCar클래스 에 대해 알기를 원하지 않기로 결정했습니다 (완전한 육체는 아니 었습니다) 이것은 원래 질문에서도 마찬가지입니다). 따라서 위의 질문에 대한 값 을 반환하기 위해 비즈니스 규칙Utils 이있는 메소드가 포함 된 클래스 라인을 따라 중개 클래스 가 있습니다 .boolean

나는 이것이 좋다고 생각한다. 중급 클래스는 이제 자동차 키만 확인해야하지만 운전자가 유효한 운전 면허증이 있는지, 알코올의 영향을 받거나 디스토피아의 미래에 DNA 생체 인식을 확인하기 위해 리팩토링 할 수 있습니다. 캡슐화를 통해이 세 클래스가 공존하는 데 큰 문제는 없습니다.

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