간단한 도메인 객체를 나타내는 기본 대 클래스?


14

도메인 특정 객체를 사용할 때 일반 문자열이나 숫자를 사용하는 경우 일반적인 지침이나 경험 규칙은 무엇입니까?

예 :

  • 연령대 대 정수?
  • 이름 클래스 대 문자열?
  • UniqueID 및 문자열
  • PhoneNumber 클래스 대 문자열 대 Long?
  • DomainName 클래스 대 문자열?

대부분의 OOP 실무자는 PhoneNumber 및 DomainName에 대한 특정 클래스를 분명히 말할 것이라고 생각합니다. 그것들을 유효하게 만드는 것과 규칙을 비교하는 방법에 대한 규칙이 많을수록 간단한 수업을 더 쉽고 안전하게 처리 할 수 ​​있습니다. 그러나 처음 세 가지에 대해서는 더 많은 논쟁이 있습니다.

나는 "나이"클래스를 본 적이 없지만 음수가 아니어야한다는 점에서 말이되는 것이 합리적이라고 주장 할 수 있습니다.

문자열은 "이름"을 나타내는 것이 일반적이지만 빈 문자열은 유효한 문자열이지만 유효한 이름이 아니기 때문에 완벽하지 않습니다. 비교는 일반적으로 대소 문자를 무시하고 수행됩니다. 비어 있는지 확인하고 대소 문자를 구분하지 않는 비교 등을 수행하는 방법이 있지만 소비자는이 작업을 수행해야합니다.

답은 환경에 달려 있습니까? 저는 주로 10 년 이상 유지되고 유지 될 엔터프라이즈 / 고 가치 소프트웨어에 관심이 있습니다.

아마도 나는 이것을 지나치게 생각하고 있지만 누군가 클래스 대 원시를 선택할 때 규칙이 있는지 알고 싶습니다.


2
나이 클래스는 정수로 대체 될 수 있다면 좋지 않습니다. 생성자에 생년월일이 있고 getCurrentAge () 및 getAgeAt (날짜 날짜) 메소드가있는 경우 클래스에 의미가 있습니다. 그러나 정수로 대체 할 수 없습니다.
kiwiron

5
"문자열은 때로는 문자열이 아닙니다. 적절하게 모델링하십시오." Mark Seemann의 ' 기본적
Serhii Shushliapin

4
실제로 Age 클래스는 이상한 방식으로 의미가 있습니다. 나이에 대한 일반적인 서구의 정의는 출생시 0이고 다음 생일에 1을 더합니다. 동아시아 방법론은 출생시 1 세이고 다음 새해에는 1이 추가된다는 것입니다. 따라서 지역에 따라 나이를 다르게 계산할 수 있습니다. EG from en.wikipedia.org/wiki/East_Asian_age_reckoning people may be one or two years older in Asian reckoning than in the western age system
Peter M

@PeterM, 좋은 정보입니다! 날짜 / 시간과 현재 나이. 그것들은 단순한 개념이어야하지만, 이것은 우리가 다루는 것보다 세상에 더 많은 복잡성이 있음을 증명합니다.
Machado

6
이 질문 아래에 많은 글이 있지만 그 답은 그렇게 복잡하지는 않습니다. Age이러한 클래스가 제공하는 추가 동작이 필요할 때 정수 대신 클래스를 사용 합니다.
Robert Harvey

답변:


20

도메인 특정 객체를 사용할 때 일반 문자열이나 숫자를 사용하는 경우 일반적인 지침이나 경험 규칙은 무엇입니까?

일반적인 지침은 도메인 별 언어로 도메인을 모델링하려는 것입니다.

왜 우리는 정수를 사용합니까? 문자열로 모든 정수를 쉽게 표현할 수 있습니다. 또는 바이트.

정수 연령에 대한 기본 유형이 포함 된 도메인에 구애받지 않는 언어로 프로그래밍하는 경우 어떤 것을 선택 하시겠습니까?

실제로 어떤 유형의 "원시"특성은 구현을 위해 언어를 선택하는 실수입니다.

특히 숫자는 일반적으로 추가 컨텍스트가 필요합니다. 나이는 숫자 일뿐만 아니라 차원 (시간), 단위 (년?), 반올림 규칙도 있습니다! 연령을 합산하면 돈에 연령을 더하는 것이 의미가 없습니다.

종류는 별개 만드는 것은 우리가 모델링 할 수 있습니다 차이 사이에 확인되지 않은 이메일 주소 및 확인 이메일 주소를 .

이러한 값이 메모리에 어떻게 표현되는지에 대한 사고는 가장 흥미로운 부분 중 하나입니다. 도메인 모델은 CustomerIdint, String, UUID / GUID 또는 JSON 노드를 신경 쓰지 않습니다 . 그것은 단지 돈을 원합니다.

정수가 빅 엔디안인지 리틀 엔디안인지 실제로 걱정합니까? List우리가 전달 된 것이 배열 또는 그래프에 대한 추상화 인지 걱정 합니까? 배정 밀도 산술이 비효율적이며 부동 소수점 표현으로 변경해야한다는 것을 발견 하면 도메인 모델이주의 해야합니까?

1972 년 파르 나스 는

대신 어려운 설계 결정 또는 변경 될 수있는 설계 결정 목록으로 시작하는 것이 좋습니다. 각 모듈은 그런 결정을 다른 모듈로부터 숨기도록 설계되었습니다.

어떤 의미에서, 우리가 소개하는 도메인 특정 값 유형은 어떤 기본 데이터 표현을 사용해야하는지 결정하는 모듈입니다.

따라서 장점은 모듈성입니다. 변경 범위를보다 쉽게 ​​관리 할 수있는 디자인을 얻을 수 있습니다. 단점은 비용 입니다. 필요한 유형을 만들려면 더 많은 노력 을 기울여야합니다. 올바른 유형을 선택하려면 도메인을 더 깊이 이해해야합니다. 가치 모듈을 만드는 데 필요한 작업량은 로컬 Blub 방언에 따라 다릅니다 .

방정식의 다른 용어에는 솔루션의 예상 수명 (한 번 실행되는 스크립트웨어에 대한 신중한 모델링이 투자 수익률이 높지 않음), 도메인이 비즈니스의 핵심 역량에 얼마나 근접한지를 포함 할 수 있습니다.

우리가 고려할 수있는 특별한 경우 는 경계를 넘어서는 의사 소통 입니다. 우리는 하나의 배치 가능한 장치에 대한 변경이 다른 배치 가능한 장치와의 조정 된 변경을 요구하는 상황에 있지 않기를 원합니다. 따라서 메시지 는 불변 또는 도메인 특정 동작을 고려하지 않고 표현에 더 집중되는 경향이 있습니다. 우리는 메시지 형식으로 "이 값은 반드시 양수 여야합니다"라는 메시지를 전달하려고 시도하지 않고 오히려 그 표현을 유선으로 전달하고 도메인 경계에서 해당 표현에 유효성 검사를 적용하려고합니다.


어렵고 변경 될 수있는 것들을 숨기거나 추상화하는 것에 대한 훌륭한 지적.
user949300

4

당신은 추상화에 대해 생각하고 있습니다. 그러나 귀하의 목록이 나에게 보이는 것들은 대부분 더 큰 것의 개별 속성 인 것 같습니다 (물론 동일한 것은 아닙니다).

더 중요하게 생각하는 것은 속성을 그룹화하고 Person 클래스와 같은 적절한 홈을 제공하는 추상화를 제공하여 클라이언트 프로그래머가 여러 속성을 처리하지 않고 하나의 상위 레벨 추상화를 처리하는 것입니다.

이 추상화가 있으면 단순히 값인 속성에 대한 추가 추상화가 필요하지 않습니다. Person 클래스는 BirthDate를 (primitive) 날짜로, PhoneNumbers와 함께 (primitive) 문자열 목록을, Name을 (primitive) 문자열로 보유 할 수 있습니다.

서식 코드가있는 전화 번호가있는 경우 이제 두 가지 속성이 있으며 전화 번호 클래스를 사용해야합니다 (숫자 만 가져 오는 것과 형식이 지정된 전화 번호와 같은 일부 동작이있을 수 있음).

클래스에 가치가있는 여러 속성 (예 : 접두사 / 제목, 첫 번째, 마지막, 접미사)으로 이름이있는 경우 (현재 이름을 문자열로 얻는 것과 작은 조각, 제목 등을 얻는 것과 같은 동작이 있음) ).


1

필요에 따라 모델링해야합니다. 일부 데이터를 원할 경우 기본 유형을 사용할 수 있지만 이러한 데이터에 대해 더 많은 작업을 수행하려면 특정 클래스로 모델링해야합니다 (예 : 그의 의견에서 @kiwiron에 동의). 연령 클래스는 의미가 없지만 생년월일 클래스로 바꾸는 것이 좋으며 이러한 메소드를 거기에 넣습니다.

클래스 내에서 이러한 개념을 모델링하면 모델의 풍부함을 강화할 수 있다는 장점이 있습니다. 예를 들어 날짜를 받아 들일 수있는 방법을 사용하는 경우 사용자는 날짜, WW II 시작 날짜 또는 냉전 종료 날짜, 이 날짜 중 여전히 유효한 생년월일입니다. 생년월일로 대체 할 수 있으므로이 경우, 어떤 날짜도 수락하지 않는 생년월일을 수락하도록 메소드를 적용하십시오.

이름의 경우 고유 한 이점이 거의 없습니다. UniqueID, PhoneNumber도 약간 있습니다. 예를 들어 일반적인 PhoneNumber는 국가 및 지역을 나타내므로 일부 운영자는 사전 정의 된 번호 접미사를 사용하여 모바일을 분류하므로 , Office ... 숫자이므로 이러한 메소드를 DomainName과 동일하게 해당 클래스에 추가 할 수 있습니다.

자세한 내용은 DDD (Domain Driven Design)의 Value Objects를 참조하십시오.


1

의존하는 것은 해당 데이터와 관련된 동작 (유지 및 유지 관리가 필요한)이 있는지 여부입니다.

간단히 말해서, 데이터와 관련된 많은 동작없이 던져지는 데이터의 일부인 경우 기본 유형을 사용하거나 다소 복잡한 데이터의 경우 (거의 동작이없는) 데이터 구조 (예 : 게터 만있는 클래스 및 세터).

반면에, 결정을 내리기 위해 코드의 여러 곳에서이 데이터를 확인하거나 조작하는 경우가 종종 있지만 서로 가까이 있지 않은 장소를 찾은 다음 클래스를 정의하여 적절한 객체로 바꿉니다. 그 안에 관련 행동을 방법으로 넣습니다. 어떤 이유로 이러한 클래스를 정의하는 것이 타당하지 않다고 생각되면 대안으로이 동작을이 데이터에서 작동하는 일종의 "서비스"클래스로 통합하는 것입니다.


1

예제는 다른 속성이나 속성과 훨씬 비슷합니다. 그 의미는 프리미티브로 시작하는 것이 완벽하게 합리적이라는 것입니다. 어떤 시점에서는 프리미티브를 사용해야하므로 실제로 필요할 때 복잡성을 줄입니다.

예를 들어, 게임을하고 있는데 캐릭터 노화에 대해 걱정할 필요가 없다고 가정 해 봅시다. 정수를 사용하면 완벽하게 이해됩니다. 나이는 간단한 확인으로 캐릭터가 무언가를 할 수 있는지 여부를 확인할 수 있습니다.

다른 사람들이 지적했듯이, 나이는 지역에 따라 복잡한 주제가 될 수 있습니다. 다른 지역 등에서 연령을 조정 해야하는 경우 다음 과 같은 Age클래스 를 소개하는 것이 좋습니다.DateOfBirthLocale 속성으로합니다. 해당 연령 클래스는 주어진 타임 스탬프에서도 현재 연령을 계산할 수 있습니다.

따라서 프리미티브를 클래스로 승격시켜야하는 이유가 있지만 애플리케이션에 따라 다릅니다. 여기 몇 가지 예가 있어요.

  • 정보 유형이 사용되는 모든 곳 (예 : 전화 번호, 전자 메일 주소)에 적용해야하는 특별한 유효성 검사 논리가 있습니다.
  • 사람이 읽을 수 있도록 특수 포맷 로직이 있습니다 (예 : IP4 및 IP6 주소)
  • 해당 객체 유형과 관련된 함수 세트가 있습니다.
  • 유사한 값 유형을 비교하기위한 특별한 규칙이 있습니다.

기본적으로 프로젝트 전체에서 일관되게 사용하기 위해 값을 처리하는 방법에 대한 규칙이 많으면 클래스를 만듭니다. 기능에 실제로 중요하지 않기 때문에 간단한 값으로 살 수 있다면 프리미티브를 사용하십시오.


1

하루의 끝에서, 객체는 어떤 프로세스가 실제로 실행 된있어 것을 바로 증인이다 .

프리미티브 int는 일부 프로세스가 4 바이트의 메모리를 0으로 만든 다음 -2,147,483,648에서 2,147,483,647 범위의 정수를 나타내는 데 필요한 비트를 뒤집 었음을 의미합니다. 이것은 나이의 개념에 대한 강력한 진술이 아닙니다. 마찬가지로 (널이 아닌)System.String 은 일부 바이트가 할당되고 비트가 일부 인코딩과 관련하여 일련의 유니 코드 문자를 나타내도록 뒤집어 졌음을 의미합니다.

따라서 도메인 개체를 나타내는 방법을 결정할 때는 해당 개체를 감시자로 사용하는 프로세스를 고려해야합니다. A는 경우 FirstName정말 비어 있지 않은 문자열이어야한다, 그것은 시스템이 어떤 과정을 실행하는 증인으로 서 있어야하는 적어도 하나의 공백이 아닌 문자가 당신을 물려 된 문자의 순서에 만들어 졌는지 확인합니다.

마찬가지로, Age어떤 프로세스는 두 프로세스 사이의 차이를 계산 한 증거 일 것입니다. ints일반적으로 두 날짜의 차이를 계산 한 결과가 아니기 때문에 사실상 나이를 나타내는 데 충분하지 않습니다.

따라서 거의 항상 각 도메인 개체에 대한 클래스 (프로그램 일뿐)를 만들고자합니다. 그래서 무엇이 문제입니까? 문제는 C # (내가 좋아하는)과 Java가 클래스를 만들 때 너무 많은 의식을 요구한다는 것입니다. 그러나 도메인 객체를 훨씬 간단하게 정의 할 수있는 대체 구문을 상상할 수 있습니다.

//hypothetical syntax
class Age = |start:date - end:date|.Years

(* ML-like syntax *)
type Age(x, y) = datediff(year, x, y)

//C#-like syntax with primary constructors and expression-bodied classes
class Age(DateTime x, DateTime y) => implicit operator int (Age a) => Abs((y - x).Years);

예를 들어, F #은 실제로 Active Patterns 형식으로 좋은 구문을 가지고 있습니다 .

//Impossible to produce an `Age` without first computing the difference of two dates
//But, any pair (tuple) of dates is implicitly converted to an `Age` when needed.

let (|Age|) (x, y) =  (date_diff y x).Days / 365

요점은 프로그래밍 언어로 인해 도메인 객체를 정의하기가 번거 롭다고해서 실제로 그렇게하는 것은 결코 나쁜 생각이 아니라는 것입니다.


† 우리는 이러한 상황을 사후 조건 이라고 부릅니다 . 그러나 저는 일부 프로세스가 수행 할 수 있고 실행되었다는 증거로 사용되는 "증인"이라는 용어를 선호합니다.


0

클래스와 원시를 사용하여 얻는 것에 대해 생각하십시오. 수업을 이용할 수 있다면

  • 확인
  • 서식
  • 다른 유용한 방법
  • 유형 안전 (예 : PhoneNumber 클래스의 경우 전화 번호를 기대하는 문자열 또는 임의의 문자열 (예 : "cucumber")에 할당 할 수 없어야합니다)
  • 다른 혜택

그 혜택은 여러분의 인생을 더 쉽게, 코드 품질, 가독성을 향상하게 하고 그것을 할, 같은 것을 사용의 고통을보다 큽니다. 그렇지 않으면 기본 요소를 고수하십시오. 가까운 경우, 판결을 요청하십시오.

또한 때로는 좋은 이름 지정으로 처리 할 수 ​​있습니다 (즉 firstName, 문자열 속성을 감싸는 전체 클래스를 만드는 것과 비교하여 이름이 지정된 문자열). 수업이 문제를 해결하는 유일한 방법은 아닙니다.

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