관계를 반대로해서 타원 문제를 해결할 수 있습니까?


13

갖는 Circle확장Ellipse 휴식에게 Liskov Substition 원리 즉, 당신은 X와 Y는 독립적으로 타원을 그리는 설정할 수 있지만 X는 항상 원 대한 Y와 동일해야합니다 : 그것은 사후 조건을 수정하기 때문에.

그러나 Circle이 타원의 하위 유형이 됨으로써 발생하는 문제가 아닌가? 우리는 관계를 되돌릴 수 없었습니까?

따라서 Circle은 수퍼 타입 setRadius입니다. 단일 방법이 있습니다.

그리고, 타원 추가하여 원형을 확장 setX하고 setY. setRadiusEllipse를 호출 하면 X와 Y가 모두 설정됩니다. 즉 setRadius의 사후 조건이 유지되지만 확장 인터페이스를 통해 X와 Y를 독립적으로 설정할 수 있습니다.


1
Wikipedia를 먼저 보았습니까 ( en.wikipedia.org/wiki/Circle-ellipse_problem )?
Doc Brown

1
예-나는 심지어 내 질문에 그것을 연결합니다 ...
HorusKol

6
그리고이 정확한 요점은이 기사에서 다룰 것이므로, 당신이 무엇을 요구하는지 잘 모르겠습니다.
Philip Kendall

6
"일부 저자는 타원이 추가 기능이있는 원이라는 이유로 원과 타원 간의 관계를 반전시킬 것을 제안했습니다. 불행히도 타원은 많은 원의 불변을 만족시키지 못합니다. "제공하기 위해."
Philip Kendall

3
: 내가 발견 한 것은이 문제가 잘못된 전제는 위키 피 디아 문서의 맨 아래에 누워 이유에 대한 명확한 설명 할 수 en.wikipedia.org/wiki/...을 . 상황 따라 여러 깨끗한 디자인이 있지만,이 두 클래스가에서 당신이 필요로 따라 어떻게 하지, .
Arthur Havlicek

답변:


38

그러나 Circle이 타원의 하위 유형이 됨으로써 발생하는 문제가 아닌가? 우리는 관계를 되돌릴 수 없었습니까?

이 문제 (및 사각형 / 사각형 문제)는 한 도메인 (형상)의 관계가 다른 도메인 (행동)과의 관계를 가지고 있다고 잘못 가정하고 있습니다.

당신이 기하학적 이론의 프리즘을 통해 그들을 볼 경우 원과 타원은 관련이 있습니다. 그러나 이것이 당신이 볼 수있는 유일한 도메인은 아닙니다.

객체 지향 디자인은 동작을 처리 합니다.

객체의 정의 특성은 객체가 담당하는 동작입니다. 그리고 행동의 영역에서, 원과 타원은 다른 행동을 가지므로 그것들을 전혀 관련이 없다고 생각하지 않는 것이 좋습니다. 이 영역에서 타원과 원은 큰 관계가 없습니다.

여기서의 교훈은 OOD에 가장 적합한 도메인을 선택하는 것입니다. 단순히 다른 도메인에 존재하기 때문에 관계를 맺고 시도하지 않는 것이 좋습니다.

이 실수의 가장 일반적인 실제 예는 객체가 동작이 매우 다르더라도 유사한 데이터 를 가지고 있기 때문에 객체가 관련되어 있거나 심지어 동일한 클래스라고 가정하는 것 입니다. 이것은 데이터가 어디로 가는지를 정의하여 "데이터 우선"개체 생성을 시작할 때 일반적으로 발생하는 문제입니다. 완전히 다른 행동을 가진 데이터를 통해 관련된 클래스로 끝날 수 있습니다. 예를 들어, 급여 명세서와 직원 개체 모두에 "총 급여"속성이있을 수 있지만 직원은 급여 명세서 유형이 아니며 급여 명세서는 직원 유형이 아닙니다.


(애플리케이션) 도메인과 OOD의 행동 및 책임 기능에 대한 우려를 분리하는 것이 매우 중요합니다. 예를 들어, 드로잉 응용 프로그램에서는 원을 사각형으로 변형 할 수 있지만 대부분의 언어에서 클래스 / 객체를 사용하여 쉽게 모델링 할 수는 없습니다 (객체는 일반적으로 클래스를 변경할 수 없음). 따라서 애플리케이션 도메인이 항상 주어진 OOP 언어의 상속 계층 구조에 잘 매핑되는 것은 아니며 강제로 시도해서는 안됩니다. 많은 경우에 구성이 더 좋습니다.
Erik Eidt

3
이 답변은 전체 이슈에서 내가 본 것 중 가장 좋은 것이며,보다 일반적인 경우에 디자인 실수가 발생할 수있는 가능성에 대해 설명합니다. 감사합니다
HorusKol

1
@ErikEidt 객체 변경 동작의 문제는 분해를 통해 OOD에서 해결할 수 있습니다. 예를 들어 모핑 가능한 모양이 원으로 바뀌면 클래스를 변경할 필요가 없습니다. 대신 클래스는 모핑 할 때 다른 동작으로 교체 할 수있는 현재 기하학적 동작 객체를 사용합니다. 이 다른 클래스에는 현재 모델링중인 기하학적 모양의 규칙이 포함되며 변형 가능한 모양 클래스는 기하학적 동작을 위해이 클래스를 연기합니다. 객체가 다른 클래스로 변형되면 비헤이비어 클래스를 다른 것으로 변경합니다.
Cormac Mulhall

2
@Cormac, 맞아! 일반적으로 전략 패턴 또는 무언가를 식별 할 수는 있지만 언급 한대로 구성 형식이라고합니다. 본질적으로, 당신은 변형되지 않는 정체성과 그다음에 바꿀 수있는 다른 것들을 가지고 있습니다. 애플리케이션 도메인 개념과 특정 언어의 OOP의 세부 사항 간의 차이점과 이들 사이에 매핑 (예 : 아키텍처, 디자인 및 프로그래밍)의 필요성을 강조합니다.
Erik Eidt

1
그러나 직업은 월급이 될 수 있습니다.

8

원은 타원의 특별한 경우, 즉 타원의 두 축이 동일하다는 것입니다. 타원이 일종의 원일 수 있다고 말하는 문제 영역 (형상)에서는 근본적으로 거짓입니다. 이 결함이있는 모델을 사용하면 원에 대한 많은 보증을 위반하게됩니다 (예 : "원의 모든 점이 중심과 동일한 거리를 가짐"). 그것도 Liskov 대체 원칙 위반이 될 것입니다. 타원의 반지름은 어떻게됩니까? ( setRadius()더 중요 하지는 않지만 getRadius())

타원의 하위 유형으로 원을 모델링하는 것은 근본적으로 잘못은 아니지만이 모델을 손상시키는 것은 가변성의 도입입니다. 포함하지 않는 setX()setY()방법, 어떤 LSP 위반이 없습니다. 차원이 다른 객체가 필요한 경우 새 인스턴스를 만드는 것이 더 나은 솔루션입니다.

class Ellipse {
  final double x;
  final double y;
  ...
  Ellipse withX(double newX) {
    return new Ellipse(x: newX, y: y);
  }
}

1
좋아- Ellipse그리고 Circle(와 같은 getArea) 유형으로 추상화되는 공통 인터페이스가 있다면 LSP 와 별도로 하위 유형을 Shape가질 수 Ellipse있고 LSP를 만족시킬 수 있습니까? CircleShape
HorusKol

1
@HorusKol 예. 둘 다 실제로 올바르게 구현하는 인터페이스를 상속받는 두 클래스는 완전히 괜찮습니다.
Ixrec

7

Cormac은 정말 훌륭한 답변을 얻었지만 처음에는 혼란의 이유에 대해 조금만 설명하고 싶습니다.

OO의 상속은 종종 "사과와 오렌지는 과일의 하위 클래스입니다"와 같은 실제 은유를 사용하여 가르칩니다. 불행하게도 이것은 OO의 유형이 프로그램과 독립적으로 존재하는 일부 분류 학적 계층에 따라 모델링되어야한다는 잘못된 믿음으로 이어진다.

그러나 소프트웨어 디자인에서 응용 프로그램의 요구 사항에 따라 유형을 모델링해야합니다. 다른 영역에서의 분류는 일반적으로 관련이 없습니다. "Apple"및 "Orange"개체가있는 실제 응용 프로그램 (예 : 슈퍼마켓의 재고 관리 시스템)에서는 아마도 별개의 클래스가 아니며 "Fruit"과 같은 범주는 수퍼 타입이 아닌 속성이됩니다.

원형 타원 문제는 붉은 청어입니다. 형상에서 원은 타원의 특수화이지만 예제의 클래스는 기하학적 도형이 아닙니다. 결정적으로 기하학적 수치는 변경할 수 없습니다. 그래도 변형 될 수 있지만 원 줄임표로 변형 될 있습니다. 따라서 원이 반지름을 변경할 수 있지만 줄임표로 변경할 수없는 모델은 형상에 해당하지 않습니다. 이러한 모델은 특정 응용 프로그램 (예 : 그리기 도구)에서 의미가 있지만 기하학적 분류는 클래스 계층 구조를 설계하는 방법과 관련이 없습니다.

그렇다면 Circle은 Ellipse의 하위 클래스 여야합니까? 이 객체를 사용하는 특정 응용 프로그램의 요구 사항에 전적으로 의존합니다. 그림 응용 프로그램은 원과 타원을 처리하는 방법에서 다른 선택을 할 수 있습니다.

  1. 원과 타원을 UI가 다른 고유 한 유형의 모양으로 취급합니다 (예 : 줄임표에 두 개의 크기 조정 핸들, 원에 하나의 핸들). 즉, 응용 프로그램의 관점에서 기하학적으로 원이지만 원이 아닌 타원을 가질 수 있습니다.

  2. 원을 포함한 모든 타원은 동일하게 취급하지만 x와 y를 같은 값으로 "잠그는"옵션이 있습니다.

  3. 타원은 스케일링 변환이 적용된 원일뿐입니다.

각각의 가능한 디자인은 다른 객체 모델로 이어질 것입니다-

첫 번째 경우 Circle 및 Ellipses는 형제 수업이됩니다.

두 번째 것에는 별개의 서클 클래스가 전혀 없습니다.

세 번째 클래스에는 별개의 타원 클래스가 없습니다. 따라서 소위 원형 타원 문제는 이들 중 어느 것도 그림에 들어 가지 않습니다.

따라서 제기 된 질문에 대답하려면 원이 타원을 확장해야합니까? 답은 : 당신이하고 싶은 것에 달려 있습니다. 그러나 아마 아닐 것입니다.


1
아주 좋은 답변입니다!
Utsav T

6

"타원"과 "원"클래스 중 하나가 다른 하나의 서브 클래스 인 것을 주장하는 것은 처음부터 실수입니다. 두 가지 현실적인 선택이 있습니다. 하나는 별도의 수업을받는 것입니다. 색상, 객체의 채움 여부, 그리기의 선 너비 등과 같은 일반적인 수퍼 클래스를 가질 수 있습니다.

다른 하나는 "Ellipse"라는 클래스를 하나만 갖는 것입니다. 해당 클래스가있는 경우 원을 나타내는 데 사용하기 쉽습니다 (구현 세부 사항에 따라 트랩이있을 수 있습니다. 타원에는 각도가 있으며 해당 각도 계산은 원 모양의 타원에 문제가되지 않아야 함). 원형 타원에 대한 특수한 방법을 사용할 수도 있지만 이러한 "원형 타원"은 여전히 ​​완전한 "타원"개체입니다.


Ellipse 클래스의 특정 개체에 실제로 두 축이 동일한 지 확인하는 IsCircle 메서드가있을 수 있습니다. 각도 문제도 지적했습니다. 서클은 '회전'할 수 없습니다.

3

LSP 포인트에 이어이 문제에 대한 하나의 '적절한'해결책은 @HorusKol과 @Ixrec가 등장한 것입니다. 두 가지 유형 모두 Shape에서 파생됩니다. 그러나 작업하는 모델에 따라 다르므로 항상 다시 돌아 가야합니다.

내가 배운 것은 :

하위 유형이 수퍼 유형과 동일한 동작을 수행 할 수없는 경우 IS-A 전제에서 관계가 유지되지 않으므로 변경해야합니다.

  • 하위 유형은 수퍼 유형의 SUPERSET입니다.
  • 수퍼 유형은 하위 유형의 하위 집합입니다.

영어로:

  • 파생 유형은 기본 유형의 SUPERSET입니다.
  • 기본 유형은 파생 유형의 하위 집합입니다.

(예:

  • 나쁜 소년 배기가있는 자동차는 여전히 자동차입니다 (일부에 따르면).
  • 엔진, 바퀴, 스티어링 랙, 드라이브 트레인 및 쉘 만 남지 않은 자동차는 '자동차'가 아니며 단지 쉘입니다.)

이것이 분류가 작동하는 방식 (즉, 동물 세계에서)과 원칙적으로 OO에서 이루어지는 방식입니다.

이것을 상속과 다형성 (항상 함께 작성 됨)의 정의로 사용하면,이 원칙이 깨지면 모델링하려는 유형을 다시 생각해야합니다.

@HorusKul 및 @Ixrec에서 언급했듯이 수학에서는 유형을 명확하게 정의했습니다. 그러나 수학에서 원은 타원의 하위 집합이기 때문에 타원입니다. 그러나 OOP에서는 이것이 상속이 작동하는 방식이 아닙니다. 클래스는 기존 클래스의 SUPERSET (확장자) 인 경우에만 상속해야합니다. 즉, 모든 컨텍스트에서 여전히 기본 클래스입니다.

이를 바탕으로 솔루션을 약간 바꿔야한다고 생각합니다.

Shape 기본 유형을 가진 다음 RoundedShape (효과적으로 원이지만 여기에 다른 이름을 사용했습니다. DELIBERATELY ...)

... 타원.

그런 식으로:

  • RoundedShape는 모양입니다.
  • 타원은 RoundedShape입니다.

(이것은 이제 언어로 사람들에게 의미가 있습니다. 우리는 이미 마음 속에 '원'이라는 개념을 명확하게 정의했으며, 개념을 일반화 (집합)하여 우리가하려고하는 일을하고 있습니다.)


명확하게 정의 된 개념이 실제로 실제로 작동하지는 않습니다.

-1

OO 관점에서 타원은 원을 확장하고 일부 속성을 추가하여 전문화합니다. 원의 기존 속성은 여전히 ​​타원으로 유지되며 더 복잡하고 구체적입니다. 이 경우 Cormac처럼 동작에 문제가 없으며 모양에는 동작이 없습니다. 유일한 문제는 언어 적 또는 수학적 의미에서 "타원 IS 원"이라고 말하는 것이 옳지 않다는 것입니다. 언급되지 않았지만 암시적인 운동의 요점은 기하학적 모양을 분류하는 것이 었습니다. 그것은 원과 타원을 동료로 간주하고 상속으로 연결하지 않고 동일한 속성 중 일부가 발생한다는 사실을 인정하고 꼬인 OO 마음이 그 관찰에 방해가되지 않도록하는 좋은 이유 일 수 있습니다.

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