시맨틱 도메인이 IS-A 관계인 것처럼 들리지만, 특히 런타임 유형 반영으로 인해 하위 유형 / 상속을 사용하여이를 모델링하는 데 약간 조심해야합니다. 그러나 나는 당신이 틀린 것에 대해 무서워한다고 생각합니다. 서브 타이핑에는 실제로 위험이 따르지만 런타임에 객체를 쿼리한다는 사실은 문제가 아닙니다. 당신은 내가 무슨 뜻인지 알 수 있습니다.
객체 지향 프로그래밍은 IS-A 관계라는 개념에 크게 의존하고 있으며, 너무 많이 의존하여 두 가지 중요한 개념을 이끌어 냈습니다.
그러나 저는 이러한 어려움이없는 IS-A 관계를 살펴볼 수있는보다 기능적인 프로그래밍 기반의 다른 방법이 있다고 생각합니다. 먼저, 프로그램에서 말과 유니콘을 모델링하고 싶습니다. 그래서 우리는 a Horse와 Unicorntype 을 가질 것 입니다. 이 유형의 가치는 무엇입니까? 글쎄, 나는 이것을 말할 것이다.
- 이 유형의 값은 말과 유니콘에 대한 표현 또는 설명 입니다 (각각).
- 그것들은 스키마 화 된 표현 또는 설명입니다. 그것들은 자유 형식이 아니며 매우 엄격한 규칙에 따라 구성됩니다.
그것은 분명하게 들릴지 모르지만, 사람들이 원 타원 문제와 같은 문제를 일으키는 방법 중 하나는 그 점을 충분히 신경 쓰지 않는 것입니다. 모든 원이 타원이지만 원에 대한 모든 스키마 화 된 설명이 다른 스키마에 따라 자동으로 타원에 대한 스키마 화 된 설명임을 의미하지는 않습니다. 즉, 원이다해서 타원은이 것을 의미하지 않는다 Circle입니다 Ellipse말하자면,. 그러나 그것은 다음을 의미합니다.
- 모든 (도식화 된 원 설명)을 동일한 원을 설명 하는 (다른 유형의 설명) 으로 변환 하는 총 함수 가 있습니다 .
CircleEllipse
- 그리고 원을 설명하는 경우 해당을 반환 하는 부분 함수 가 있습니다.
EllipseCircle
따라서 함수형 프로그래밍 용어에서 Unicorn유형은 전혀 하위 유형일 필요가 없으며 Horse다음과 같은 작업 만하면됩니다.
-- Convert any unicorn-description of into a horse-description that
-- describes the same unicorns.
toHorse :: Unicorn -> Horse
-- If the horse described by the given horse-description is a unicorn,
-- then return a unicorn-description of that unicorn, otherwise return
-- nothing.
toUnicorn :: Horse -> Maybe Unicorn
그리고 toUnicorn올바른 역수 여야합니다 toHorse.
toUnicorn (toHorse x) = Just x
하스켈 Maybe타입은 다른 언어를 "옵션"타입이라고 부릅니다. 예를 들어, Java 8 Optional<Unicorn>유형은 Unicorn아무것도 아니거나 아무것도 아닙니다. 예외를 던지거나 "기본 또는 마법 값"을 반환하는 두 가지 대안은 옵션 유형과 매우 유사합니다.
따라서 기본적으로 내가 한 것은 하위 유형이나 상속을 사용하지 않고 IS-A 관계 개념을 유형과 기능 측면에서 재구성하는 것입니다. 내가 이것에서 빼앗아 갈 것은 :
- 모델에는
Horse유형 이 있어야합니다 .
- 이
Horse유형은 어떤 값이 유니콘을 묘사하는지 명확하게 결정하기에 충분한 정보를 인코딩해야합니다.
Horse유형 의 일부 작업은 해당 유형의 클라이언트가 주어진 정보 Horse가 유니콘 인지 여부를 관찰 할 수 있도록 해당 정보를 노출해야합니다 .
- 이
Horse유형 의 클라이언트는 유니콘과 말을 구별하기 위해 런타임에 후자의 작업을 사용해야합니다.
따라서 이것은 기본적으로 " Horse유니콘이든 모든 것을 묻습니다 "모델입니다. 당신은 그 모델을 조심하지만, 나는 그렇게 잘못 생각합니다. Horses 목록을 제공 할 경우 , 유형이 보증하는 것은 목록의 항목이 설명하는 것이 말이므로 불가피하게 런타임 중에 어떤 것이 유니콘인지 알려야합니다. 따라서이 문제를 해결할 수는 없습니다. 귀하를 대신 할 작업을 구현해야합니다.
객체 지향 프로그래밍에서 익숙한 방법은 다음과 같습니다.
- 이
Horse유형;
- 이
Unicorn의 하위 유형으로 Horse;
- 주어진 유형
Horse이 인지 여부를 식별하는 클라이언트 액세스 가능 조작으로 런타임 유형 반영을 사용하십시오 Unicorn.
위에서 설명한 "thing vs. description"각도에서 볼 때 이것은 큰 약점을 가지고 있습니다.
- 당신은 어떤 경우
Horse유니콘을 설명하지만,없는 경우 Unicorn인스턴스를?
처음으로 돌아가서, 이것은 IS-A 관계를 모델링하기 위해 서브 타이핑 및 다운 캐스트를 사용하는 데있어 정말 무서운 부분이라고 생각합니다. 런타임 검사를 수행 할 필요는 없습니다. 묻는 타이포그래피를 조금 남용 Horse그것이 여부 Unicorn예를하는 것은 요구와 동의어가 아니다 Horse그것이 유니콘 (그것이인지 여부 Horse도 유니콘하는 말의 -description). Horses클라이언트가 Horse유니콘을 설명하는 코드를 만들 려고 할 때마다 Unicorn클래스가 인스턴스화 되도록 구성하는 코드를 캡슐화하기 위해 프로그램이 많은 시간을 투자하지 않았다면 . 내 경험상 프로그래머가이 일을 신중하게하는 일은 거의 없다.
따라서 Horses를 Unicorns 로 변환하는 명시적이고 비 다운 캐스트 작업이있는 접근법 을 사용 합니다. 이것은 다음 Horse유형 의 메소드 일 수 있습니다 .
interface Horse {
// ...
Optional<Unicorn> toUnicorn();
}
또는 외부 대상 일 수도 있습니다 ( "말이 유니콘인지 아닌지를 알려주는 말의 분리 된 개체").
class HorseToUnicornCoercion {
Optional<Unicorn> convert(Horse horse) {
// ...
}
}
이들 사이의 선택은 당신의 프로그램이 편성 된 방법 두 경우 모두, 당신이 내에 해당이의 문제입니다 Horse -> Maybe Unicorn운영 위에서, 당신은 단지 (다른 방법으로 그것을 포장하고 그 인정 하듯이 어떤 작업상의 파급 효과있을 것이다 Horse형의 요구를 고객에게 노출).