모든 유형을 정의해야합니까?


141

최근에 나는 코드의 가독성에 문제가 생겼다.
나는 작업을 수행하고 나중에 참조 할 수 있도록이 작업의 ID를 나타내는 문자열을 반환했습니다 (Windows에서 OpenFile과 비슷한 핸들을 반환하는). 사용자는 나중에이 ID를 사용하여 작업을 시작하고 완료를 모니터링합니다.

상호 운용성 문제로 인해 ID는 임의의 문자열이어야했습니다. 이것은 다음과 같이 매우 불명확 한 서명을 가진 메소드를 작성했습니다.

public string CreateNewThing()

이로 인해 리턴 유형의 의도가 명확하지 않습니다. 이 문자열을 다른 유형으로 감싸서 그 의미를 더 명확하게 만듭니다.

public OperationIdentifier CreateNewThing()

형식은 문자열 만 포함하며이 문자열을 사용할 때마다 사용됩니다.

이러한 작동 방식의 장점은보다 형식적인 안전성과 명확한 의도라는 것이 명백하지만, 관용적이지 않은 훨씬 더 많은 코드와 코드를 생성합니다. 한편으로 나는 추가 된 안전을 좋아하지만 많은 혼란을 야기합니다.

안전상의 이유로 클래스에서 단순 유형을 랩핑하는 것이 좋습니다.


4
Tiny Types에 대해 읽고 싶을 수도 있습니다. 나는 그것을 조금 가지고 놀고 그것을 추천하지는 않지만 생각하기에 재미있는 접근법입니다. darrenhobbs.com/2007/04/11/tiny-types
Jeroen Vannevel

40
Haskell과 같은 것을 사용하면 유형을 사용하는 것이 얼마나 도움이되는지 알 수 있습니다. 유형은 올바른 프로그램을 보장하는 데 도움이됩니다.
Nate Symer

3
Erlang과 같은 동적 언어조차도 유형 시스템의 이점을 얻으므로 동적 언어 임에도 불구하고 Haskellers에게 친숙한 유형 사양 시스템 및 유형 검사 도구가 있습니다. C / C ++와 같은 언어는 실제 유형의 것이 컴파일러가 특정 방식을 처리하기 위해 동의하는 정수이거나 클래스를 가장하기로 결정한 경우 거의 유형이있는 Java는 유형이 의미 론적 오버로드 문제입니다. 언어 ( "클래스"또는 "유형"입니까?) 또는 모호한 유형 ( "URL", "문자열"또는 "UPC"입니까?)입니다. 결국, 명확히 할 수있는 도구를 사용하십시오.
zxq9

7
C ++에서 아이디어가 어디에서 왔는지 잘 모르겠습니다. 유형에 오버 헤드가 있습니다. C ++ 11을 사용하면 컴파일러 제조업체 중 어느 것도 각 람다에 고유 한 유형을 부여하는 데 문제가 없었습니다. 그러나 TBH는 "C / C ++ 유형 시스템"에 대한 의견에서 두 사람이 유형 시스템을 공유하는 것처럼 많은 통찰력을 기대할 수는 없습니다.
MSalters

2
@JDB-아니요, 그렇지 않습니다. 비슷하지도 않습니다.
Davor Ždralo

답변:


152

같은 프리미티브, string또는 int, 비즈니스 도메인의 의미가 없습니다. 이로 인한 직접적인 결과는 제품 ID가 예상 될 때 실수로 URL을 사용하거나 가격을 기대할 때 수량을 사용할 수 있다는 것 입니다.

그렇기 때문에 Object Calisthenics 과제 는 다음과 같은 규칙 중 하나로 프리미티브 래핑을 제공합니다.

규칙 3 : 모든 기본 요소 및 문자열 랩

Java 언어에서 int는 실제 객체가 아닌 기본 요소이므로 객체와 다른 규칙을 따릅니다. 객체 지향이 아닌 구문과 함께 사용됩니다. 더 중요한 것은 int 자체는 스칼라 일 뿐이므로 의미가 없습니다. 메소드가 int를 매개 변수로 사용하는 경우 메소드 이름은 의도를 표현하는 모든 작업을 수행해야합니다. 동일한 방법으로 매개 변수로 시간이 걸리면 진행 상황을 훨씬 쉽게 확인할 수 있습니다.

같은 문서는 추가적인 이점이 있다고 설명합니다.

Hour 또는 Money와 같은 작은 물체는 다른 수업에서 흩어 졌을 행동을 넣을 수 있는 확실한 장소를 제공 합니다.

실제로 프리미티브가 사용되는 경우 일반적으로 해당 유형과 관련된 코드의 정확한 위치를 추적하는 것은 매우 어렵고, 종종 코드 중복 이 심 합니다 . Price: Money수업 이 있으면 내부에서 범위 검사를 찾는 것이 당연합니다. 대신, A는 경우 int(악화하는 double) 범위를 확인해야합니다 매장 제품 가격에 사용됩니까? 제품? 리베이트? 카트?

마지막으로, 문서에 언급되지 않은 세 번째 이점은 기본 유형을 비교적 쉽게 변경할 수 있다는 것입니다. 오늘 나의 경우 ProductIdshort그 기본 유형으로 나중에 내가 사용해야하는 int대신에, 기회는 전체 코드베이스를 확장 할 것입니다 변경할 수있는 코드이다.

단점은 Object Calisthenics 연습의 모든 규칙에 동일한 주장이 적용된다는 입니다. 경우 Product포함 ProductPrice에서 상속하는 PositivePrice상속되는 Price차례로에서 상속 Money이는 청소하지 아키텍처, 오히려 한 가지를 찾기 위해, 메인테이너는 수십 때마다 파일을 열어야합니다 완전히 엉망이다.

고려해야 할 또 다른 요점은 추가 클래스를 작성하는 비용 (코드 라인 측면에서)입니다. 래퍼가 변경 불가능한 경우 (일반적으로 있어야 함) C #을 가져 오면 래퍼 내에 최소한 있어야합니다.

  • 속성 getter,
  • 지원 분야
  • 백킹 필드에 값을 할당하는 생성자
  • 사용자 정의 ToString(),
  • XML 문서 주석 ( 많은 줄 을 만듭니다 ),
  • A EqualsGetHashCode재정의 (또한 많은 LOC).

그리고 결국, 관련이있을 때 :

  • DebuggerDisplay의 속성,
  • ==!=연산자 의 재정의
  • 결국 암시 적 변환 연산자에 과부하가 발생하여 캡슐화 된 유형과의 원활한 변환
  • 코드 계약 (세 가지 속성을 가진 다소 긴 방법 인 불변 포함)
  • XML 직렬화, JSON 직렬화 또는 데이터베이스와 값을 저장 /로드하는 동안 사용되는 여러 변환기.

간단한 래퍼의 백 LOC는 상당히 래퍼이므로 이러한 래퍼의 장기 수익성을 완전히 확신 할 수 있습니다. Thomas Junk설명하는 범위 개념 은 특히 ​​여기에 관련됩니다. ProductId코드베이스 전체 에서 사용되는 100 개의 LOC를 작성하면 매우 유용합니다. 단일 메서드 내에서 세 줄을 만드는 코드 조각에 대해이 크기의 클래스를 작성하는 것이 훨씬 더 의심됩니다.

결론:

  • (1) 실수를 줄이거 나 (2) 코드 중복의 위험을 줄이거 나 (3) 나중에 기본 유형을 변경하는 데 도움이되는 경우 응용 프로그램의 비즈니스 영역에서 의미가있는 클래스의 기본 랩핑을 수행하십시오.

  • 코드에 자동으로 찾을 수있는 모든 기본을 포장하지 마십시오 사용하는 많은 경우가있다 string거나 int완벽하게 괜찮가.

실제로에서에서 대신 클래스 public string CreateNewThing()인스턴스를 반환하면 도움이 될 수 있지만 다음을 수행 할 수도 있습니다.ThingIdstring

  • Id<string>클래스 의 인스턴스 , 즉 기본 유형이 문자열임을 나타내는 일반 유형의 객체를 리턴 합니다. 많은 유형을 유지 관리 할 필요없이 가독성을 얻을 수 있습니다.

  • Thing클래스 의 인스턴스를 반환합니다 . 사용자에게 ID 만 필요한 경우 다음을 사용하여 쉽게 수행 할 수 있습니다.

    var thing = this.CreateNewThing();
    var id = thing.Id;
    

33
기본 유형을 자유롭게 변경하는 힘이 중요하다고 생각합니다. 그것은 오늘날의 문자열이지만 아마도 GUID이거나 내일이 깁니다. 유형 랩퍼를 작성하면 해당 정보가 숨겨져 변경 사항이 줄어 듭니다. ++ 여기에 좋은 답변입니다.
RubberDuck

4
돈의 경우 통화가 Money 객체에 포함되어 있거나 전체 프로그램이 동일한 것을 사용하는지 확인하십시오 (그런 다음 문서화해야 함).
Paŭlo Ebermann

5
@Blrfl : 깊은 상속이 필요한 유효한 경우가 있지만 일반적으로 LOC에 의한 강박 관념으로 인해 가독성에주의를 기울이지 않고 과도하게 설계된 코드에서 이러한 상속을 봅니다. 따라서 내 대답 의이 부분의 부정적인 성격.
Arseni Mourzenko

3
@ArTs : 그것은 "어떤 사람들은 그것을 잘못된 방식으로 사용하기 때문에 도구를 비난하자"라는 주장입니다.
Blrfl

6
@MainMa 의미가 값 비싼 오류를 피할 수있는 예 : en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure
cbojar 15.12의

60

범위 를 경험의 원칙으로 사용합니다 . 그러한 범위를 생성하고 소비하는 범위가 좁을 values수록이 값을 나타내는 객체를 생성 할 가능성이 줄어 듭니다.

다음 의사 코드 가 있다고 가정 해보십시오.

id = generateProcessId();
doFancyOtherstuff();
job.do(id);

범위가 매우 제한되어 있으며 유형을 만드는 데 의미가 없습니다. 그러나 한 레이어에서이 값을 생성하여 다른 레이어 (또는 다른 객체)로 전달하면 해당 유형을 작성하는 것이 좋습니다.


14
아주 좋은 지적입니다. 명시 적 유형은 의도전달 하는 데 중요한 도구 이며 특히 객체 및 서비스 경계에서 중요합니다.
Avner Shahar-Kashtan

doFancyOtherstuff()서브 루틴 의 모든 코드를 아직 리팩토링하지 않은 경우 +1하지만 job.do(id)참조가 단순한 문자열로 유지하기에 충분하지 않은 로컬 이라고 생각할 수 있습니다 .
Mark Hurd

이것은 사실입니다! 외부 세계가 절대이 방법을 사용하지 않을 것이라는 개인적인 방법을 사용하는 경우입니다. 그 후에는 수업이 보호 될 때와 수업이 공개되었지만 공개 API의 일부가 아닌 경우 조금 더 생각할 수 있습니다. 스코프 스코프 스코프 스코프와 많은 예술은 선을 많은 기본 유형과 많은 사용자 정의 유형 사이의 좋은 위치에 배치합니다.
사무엘

34

정적 타입 시스템은 데이터의 부정확 한 사용을 막기위한 것입니다.

이를 수행하는 유형의 명백한 예가 있습니다.

  • 당신은 UUID의 달을 얻을 수 없습니다
  • 두 개의 문자열을 곱할 수 없습니다.

더 미묘한 예가 있습니다

  • 책상의 길이를 사용하여 비용을 지불 할 수 없습니다
  • 다른 사람의 이름을 URL로 사용하여 HTTP 요청을 할 수 없습니다.

double가격과 길이 모두 에 사용 하거나 string이름과 URL 모두에 사용하려는 유혹이있을 수 있습니다 . 그러나 그렇게하면 멋진 유형 시스템이 파괴되고 이러한 오용이 언어의 정적 검사를 통과 할 수 있습니다.

Newton-seconds와 혼동되는 파운드-초 는 런타임에 결과좋지 않을 수 있습니다 .


이것은 특히 문자열에 문제가 있습니다. 이들은 종종 "범용 데이터 유형"이됩니다.

주로 컴퓨터와의 텍스트 인터페이스에 익숙하며 이러한 휴먼 인터페이스 (UI)를 프로그래밍 인터페이스 (API)로 확장하는 경우가 많습니다 . 우리는 문자로 34.25 생각 34.25 . 우리는 날짜를 05-03-2015 문자로 생각 합니다. UUID를 75e945ee-f1e9-11e4-b9b2-1697f925ec7b 문자로 생각 합니다.

그러나이 정신 모델은 API 추상화에 해를 끼칩니다.

말이나 언어는 글로 쓰거나 말한 것처럼 내 생각의 메커니즘에서 아무런 역할을하지 않는 것 같습니다.

앨버트 아인슈타인

마찬가지로 텍스트 표현은 유형 및 API를 디자인하는 데 아무런 역할을해서는 안됩니다. 조심하십시오 string! (및 기타 지나치게 일반적인 "기본"유형)


유형은 "어떤 작업이 의미가 있는지"를 전달합니다.

예를 들어, 한 번 클라이언트에서 HTTP REST API로 작업했습니다. REST는 올바르게 완료되면 관련 엔티티를 가리키는 하이퍼 링크가있는 하이퍼 미디어 엔티티를 사용합니다. 이 클라이언트에서는 엔터티 유형 (예 : 사용자, 계정, 가입)뿐만 아니라 해당 엔터티에 대한 링크도 입력되었습니다 (UserLink, AccountLink, SubscriptionLink). 링크는 래퍼에 Uri지나지 않았지만 별도의 유형으로 인해 AccountLink를 사용하여 사용자를 가져올 수 없었습니다. 모든 것이 명백 Uri하거나 더 나빴다면 string이러한 실수는 런타임에만 발견됩니다.

마찬가지로 상황에 따라 한 가지 목적으로 만 사용되는 데이터가 Operation있습니다. 다른 용도로 사용해서는 안되며, Operation우리가 만든 임의의 문자열로 를 식별하려고 시도해서는 안됩니다 . 별도의 클래스를 만들면 코드에 가독성과 안전성이 추가됩니다.


물론 모든 좋은 것을 과도하게 사용할 수 있습니다. 치다

  1. 코드에 얼마나 선명하게 추가됩니까?

  2. 사용 빈도

데이터의 "유형"(추상적 인 의미에서)이 고유 한 목적으로 그리고 코드 인터페이스간에 자주 사용되는 경우, 자세한 정보 대신 별도의 클래스가 될 수 있습니다.


21
"문자열은 종종 '유니버설'데이터 유형이됩니다."-이것은 혀에서 "문자열 유형 언어"모니 커의 기원입니다.
MSalters

@MSalters, 하하, 아주 좋은.
Paul Draper

4
물론 날짜로 해석 될 때의 05-03-2015 의미 는 무엇 입니까?
CVn

10

안전상의 이유로 클래스에서 단순 유형을 랩핑하는 것이 좋습니다.

때때로.

이것은 string보다 구체적인 대신 사용하여 발생할 수있는 문제의 무게를 측정해야하는 경우 중 하나입니다 OperationIdentifier. 그들의 심각성은 무엇입니까? 그들의 가능성은 무엇입니까?

그런 다음 다른 유형을 사용하는 비용을 고려해야합니다. 사용하기가 얼마나 고통 스럽습니까? 얼마나 많은 일을해야합니까?

경우에 따라 멋진 콘크리트 유형을 사용하면 시간과 노력을 절약 할 수 있습니다. 다른 경우에는 문제가되지 않습니다.

일반적으로 이런 종류의 일은 오늘날보다 더 많이 이루어져야한다고 생각합니다. 도메인에 무언가를 의미하는 엔터티가있는 경우 해당 엔터티가 비즈니스와 함께 변경 / 성장할 가능성이 높으므로 해당 유형을 자체 유형으로 사용하는 것이 좋습니다.


10

나는 일반적으로 프리미티브와 문자열에 대한 유형을 여러 번 만들어야한다는 데 동의하지만 위의 답변은 대부분의 경우 유형을 만드는 것이 좋습니다.

  • 공연. 여기서 실제 언어를 참조해야합니다. C #에서 클라이언트 ID가 짧고 클래스로 래핑하면 메모리가 많이 생깁니다. 메모리는 64 비트 시스템에서 8 바이트이므로 이제 힙에 할당되므로 속도가 빨라집니다.
  • 유형에 대해 가정 할 때. 클라이언트 ID가 짧고 어떤 방식으로 그것을 포장하는 논리가있는 경우 일반적으로 이미 유형을 가정합니다. 모든 곳에서 이제 추상화를 해제해야합니다. 그것이 하나의 장소라면, 별 문제가되지 않습니다. 만약 그것이 모든 곳에 있다면, 당신은 당신이 기본을 사용하는 시간의 절반을 찾을 수 있습니다.
  • 모든 언어에 typedef가있는 것은 아닙니다. 그리고 아직 작성되지 않은 코드와 이미 작성된 언어의 경우, 그러한 변경을하는 것은 버그를 유발할 수있는 큰 작업이 될 수 있습니다.
  • 경우에 따라 가독성 이 떨어 집니다. 이것을 어떻게 인쇄합니까? 이것을 어떻게 확인합니까? null을 확인해야합니까? 유형의 정의를 자세히 설명해야하는 모든 질문이 있습니까?

3
래퍼 유형이 클래스가 아닌 구조체라면 short(예를 들어) 같은 비용이 랩핑되거나 랩핑되지 않습니다. (?)
MathematicalOrchid

1
형식이 자체 유효성 검사를 캡슐화하는 것이 좋지 않은 디자인입니까? 또는 헬퍼 유형을 작성하여 유효성을 검증 하시겠습니까?
Nick Udell

1
불필요하게 포장하거나 녹이는 것은 반 패턴입니다. 명시 적으로 그리고 진정으로 변환이 필요한 경우를 제외하고 응용 프로그램 코드를 통해 정보가 투명하게 전달되어야 합니다 . 유형은 일반적으로 시스템 전체에 대량의 잘못된 구현이 흩어져 있기 때문에 한곳에 위치한 적은 양의 좋은 코드를 대체하기 때문에 좋습니다.
토마스 W

7

아니요, "모든 것"에 대한 유형 (클래스)을 정의해서는 안됩니다.

그러나 다른 답변에서 알 수 있듯이 종종 그렇게 하는 것이 유용합니다. 코드를 작성, 테스트 및 유지 관리 할 때 적합한 유형 또는 클래스 가 없기 때문에 의식적으로 가능한 경우 의식이 너무 심하게 느껴지도록 해야합니다. 저에게 너무 많은 마찰은 여러 개의 프리미티브 값을 단일 값으로 통합하거나 값을 확인해야 할 때입니다 (즉, 프리미티브 유형의 모든 가능한 값 중 유효한 값이 '암시 적 유형').

내 코드에서 너무 많은 디자인을 담당하기 위해 귀하의 질문에 제기 한 것과 같은 고려 사항을 발견했습니다 . 필요 이상으로 더 많은 코드를 작성하는 것을 의도적으로 피하는 습관을 개발했습니다. 코드에 대해 좋은 (자동화 된) 테스트를 작성하기를 희망합니다. 코드를 리팩토링하고 코드를 리팩토링하고 유형 또는 클래스를 추가하면 코드 의 지속적인 개발 및 유지 관리에 순이익을 제공 할 수 있습니다 .

Telastyn의 답변Thomas Junk의 답변은 모두 관련 코드의 컨텍스트와 사용법에 대해 아주 좋은 지적입니다. 단일 코드 블록 (예 : 메소드, 루프, using블록) 내에서 값 을 사용하는 경우 기본 유형을 사용하는 것이 좋습니다. 반복적으로 그리고 다른 많은 장소에서 일련의 값을 사용하는 경우 기본 유형을 사용하는 것이 좋습니다. 그러나 값 세트를 자주 사용하고 광범위하게 사용할수록 해당 값 세트가 기본 유형으로 표시되는 값과 더 밀접하게 일치할수록 값을 클래스 또는 유형으로 캡슐화하는 것을 고려해야합니다.


5

표면적으로는 작업을 식별하기 만하면됩니다.

또한 작업이 수행해야 할 작업을 말합니다.

작업시작 하고 완료모니터하기 위해

이것이 "식별 방법"과 같은 방식으로 말하지만, 이것이 동작을 설명하는 속성이라고 말하고 싶습니다. 그것은 나에게 유형 정의처럼 들리는데, 아주 잘 맞는 "명령 패턴"이라는 패턴조차 있습니다. .

그것은 말한다

명령 패턴 [...]은 나중에 작업수행 하거나 이벤트를 트리거하는 데 필요한 모든 정보를 캡슐화하는 데 사용됩니다 .

나는 이것이 당신이 당신의 작업으로하고 싶은 것과 비교할 때 매우 유사하다고 생각합니다. (두 따옴표로 굵게 표시된 문구를 비교하십시오) 문자열을 반환하는 대신 Operation추상 의미에서에 대한 식별자 , 예를 들어 해당 클래스의 객체에 대한 포인터를 oop로 반환하십시오 .

코멘트에 대해

이 클래스를 명령이라고 부르고 그 안에 논리를 갖지 않는 것이 좋습니다.

아닙니다. 패턴은 매우 추상적 이라는 것을 기억하십시오 , 그들은 다소 메타 있다는 사실 너무 추상적. 즉, 그들은 종종 실제 개념이 아닌 프로그래밍 자체를 추상화합니다. 명령 패턴은 함수 호출의 추상화입니다. (또는 방법) 누군가가 매개 변수 값을 전달한 직후와 실행 직전에 일시 중지 버튼을 누르는 것처럼 나중에 다시 시작할 수 있습니다.

다음은 oop을 고려하고 있지만 그 뒤에있는 동기는 모든 패러다임에 적용되어야합니다. 논리를 명령에 배치하는 것이 나쁜 것으로 간주 될 수있는 몇 가지 이유를 알려 드리고자합니다.

  1. 명령에 모든 논리가 있으면 부풀어지고 지저분해질 것입니다. "이 모든 기능은 별도의 클래스에 있어야합니다."라고 생각할 것입니다. 당신은 것입니다 리팩토링 명령에서 논리를.
  2. 명령에 모든 논리가 있다면 테스트 할 때 테스트하기가 어렵습니다. "거짓 쓰레기, 왜 말도 안되는이 명령을 사용해야합니까? 이 함수 가 1을 뱉어 내는지 여부 만 테스트 하고 싶습니다. 나중에 호출하고 싶지 않습니다. 지금 테스트하고 싶습니다!"
  3. 명령에 모든 논리가 있다면 완료되면 다시보고하는 것이 이치에 맞지 않습니다. 함수가 기본적으로 동기식으로 실행될 것이라고 생각하면 실행이 완료되면 알림을받는 것이 의미가 없습니다. 어쩌면 작업이 실제로 아바타를 영화 형식으로 렌더링하기 때문에 다른 스레드를 시작 중일 수 있습니다 (라즈베리 파이에는 추가 스레드가 필요할 수 있음). 완료되면 다시보고하면 비동기가 발생하기 때문일 수 있습니다 (단어입니까?). 스레드를 실행하거나 서버에 연결하는 것이 명령에 없어야 할 논리라고 생각합니다. 이것은 다소 논란의 여지가 있으며 논란의 여지가 있습니다. 말이 안된다고 생각되면 의견을 말해주십시오.

요약하자면, 명령 패턴을 사용하면 기능을 객체로 래핑하여 나중에 실행할 수 있습니다. 모듈화를 위해 (기능은 명령을 통해 실행되는지 여부에 관계없이 존재 함), 테스트 가능성 (기능은 명령없이 테스트 할 수 있어야 함) 및 본질적으로 좋은 코드 작성 요구를 표현하는 다른 모든 버즈 단어의 경우 실제 논리를 넣지 않습니다. 명령으로.


패턴은 추상적이기 때문에 실제의 은유를 생각해 내기가 어려울 수 있습니다. 시도는 다음과 같습니다.

"할머니, 12시에 TV의 녹화 버튼을 눌러 채널 1의 심슨을 놓치지 마세요?"

할머니는 그 기록 버튼을 눌렀을 때 기술적으로 어떤 일이 일어나는지 모릅니다. 논리는 다른 곳에 있습니다 (TV). 그리고 그것은 좋은 것입니다. 기능이 캡슐화되고 명령에서 정보가 숨겨집니다. API의 사용자이며 반드시 논리의 일부일 필요는 없습니다.


당신은 맞습니다, 나는 그것에 대해 그렇게 생각하지 않았습니다. 그러나 작업의 논리가 다른 클래스에 캡슐화되어 명령 인 객체 안에 넣을 수 없으므로 명령 패턴이 100 % 적합하지 않습니다. 이 클래스를 명령이라고 부르고 그 안에 논리를 갖지 않는 것이 좋습니다.
Ziv

이것은 많은 의미가 있습니다. OperationIdentifier가 아닌 Operation 반환을 고려하십시오. 이것은 Task 또는 Task <T>를 반환하는 C # TPL과 유사합니다. @Ziv : "작업의 논리는 다른 클래스에 캡슐화되어 있습니다." 그러나 그것은 당신이 여기서도 그것을 제공 할 수 없다는 것을 의미합니까?
Moby Disk

@Ziv 나는달라고 간청합니다. 내가 답변에 추가 한 이유를 살펴보고 그들이 당신에게 어떤 의미가 있는지 확인하십시오. =)
null

5

프리미티브 유형을 감싸는 아이디어,

  • 도메인 특정 언어를 설정하려면
  • 잘못된 값을 전달하여 사용자가 저지른 실수를 제한

분명히 모든 곳에서 수행하는 것은 매우 어렵고 실용적이지는 않지만 필요한 경우 유형을 감싸는 것이 중요합니다.

예를 들어 주문 클래스가 있다면

Order
{
   long OrderId { get; set; }
   string InvoiceNumber { get; set; }
   string Description { get; set; }
   double Amount { get; set; }
   string CurrencyCode { get; set; }
}

주문을 조회하기위한 중요한 특성은 대부분 OrderId 및 InvoiceNumber입니다. Amount와 CurrencyCode는 밀접한 관련이 있으며, 누군가 금액을 변경하지 않고 CurrencyCode를 변경하면 주문이 더 이상 유효하지 않은 것으로 간주 될 수 있습니다.

따라서이 시나리오에서는 OrderId, InvoiceNumber를 랩핑하고 통화 복합을 도입하는 것이 적합하며 설명 랩핑은 의미가 없습니다. 선호하는 결과는 다음과 같습니다.

    Order
    {
       OrderIdType OrderId { get; set; }
       InvoiceNumberType InvoiceNumber { get; set; }
       string Description { get; set; }
       Currency Amount { get; set; }
    }

따라서 모든 것을 포장해야 할 이유는 없지만 실제로 중요한 것입니다.


4

인기없는 의견 :

일반적으로 새 유형을 정의해서는 안됩니다!

프리미티브 또는 기본 클래스를 래핑하기 위해 새 유형을 정의하는 것을 의사 유형 정의라고도합니다. IBM은 여기서 이를 나쁜 관행으로 설명합니다 (이 경우 제네릭을 잘못 사용하는 데 중점을 둡니다).

의사 유형은 공통 라이브러리 기능을 쓸모 없게 만듭니다.

Java의 Math 함수는 모든 숫자 프리미티브에서 작동 할 수 있습니다. 그러나 새로운 클래스 백분율을 정의하면 (0에서 1 사이의 범위에 두 배를 감음) 모든 함수는 쓸모가 없으며 백분율 클래스의 내부 표현에 대해 알아야 할 클래스로 감싸 야합니다. .

의사 유형이 바이러스 성

여러 라이브러리를 만들 때 이러한 의사 유형이 바이러스 성인 경우가 많습니다. 위에서 언급 한 백분율 클래스를 하나의 라이브러리에서 사용하는 경우 라이브러리 경계에서 변환해야하거나 (해당 유형을 작성하는 데 필요한 모든 안전 / 의미 / 논리 / 기타 이유를 잃어) 클래스를 만들어야합니다. 다른 도서관에도 접근 할 수 있습니다. 간단한 이중으로 충분할 수있는 유형으로 새 라이브러리를 감염시킵니다.

메시지를 빼앗아

랩핑하는 유형에 많은 비즈니스 로직이 필요하지 않으면 의사 클래스로 랩핑하지 않는 것이 좋습니다. 비즈니스 제약이 심각한 경우에만 클래스를 래핑해야합니다. 다른 경우에는 변수의 이름을 올바르게 지정하면 의미를 전달하는 데 먼 길을 가야합니다.

예를 들면 :

A uint는 UserId를 완벽하게 나타낼 수 있으며, uint==와 같은 Java 내장 연산자를 계속 사용할 수 있으며 사용자 ID의 '내부 상태'를 보호하기 위해 비즈니스 로직이 필요하지 않습니다.


동의하다. 모든 프로그래밍 철학은 훌륭하지만 실제로는 잘 작동해야합니다.
Ewan

당신이 영국에서 가장 가난한 사람들에게 대출에 대한 광고를 본 적이 있다면, 당신은 0..1 범위에서 두 배를 포장 비율이 작동하지 않는 것을 발견 할 것입니다 ...
gnasher729

1
C / C ++ / Objective C에서는 typedef를 사용할 수 있으며 기존 기본 유형의 새 이름을 제공합니다. 반면에, 기본 유형이 무엇이든 상관없이 코드는 작동해야합니다 (예 : OrderId를 uint32_t에서 uint64_t로 변경하면 (40 억 개 이상의 주문을 처리해야하기 때문에).
gnasher729

나는 당신의 용기에 대해 당신을 공언하지 않을 것이지만, 의사 유형과 가장 큰 답변에 정의 된 유형은 다릅니다. 이들은 비즈니스에 특정한 유형입니다. 유사 유형은 여전히 ​​일반적인 유형입니다.
0fnt

@ gnasher729 : C ++ 11에는 사용자 정의 리터럴이있어 래핑 프로세스가 사용자에게 매우 달콤합니다. 거리 d = 36.0_mi + 42.0_km; . msdn.microsoft.com/ko-kr/library/dn919277.aspx
damix911 2012 년

3

모든 팁과 마찬가지로 규칙 적용시기를 아는 것이 기술입니다. 타입 중심 언어로 자신 만의 타입을 만들면 타입 검사를받습니다. 일반적으로 좋은 계획이 될 것입니다.

명명 규칙 은 가독성의 핵심입니다. 이 두 가지 조합은 의도를 명확하게 전달할 수 있습니다.
그러나 ///는 여전히 유용합니다.

따라서 수명이 클래스 경계를 ​​벗어날 때 많은 유형을 작성한다고 말할 수 있습니다. 또한 항상 클래스 가 아니라 Struct및 의 사용을 고려하십시오 Class.


2
무슨 ///뜻입니까?
Gabe

1
///는 C #의 문서 주석으로 intellisense가 호출 방법 지점에서 서명 문서를 표시 할 수 있습니다. 코드를 문서화하는 가장 좋은 방법을 고려하십시오. 도구로 농사를 짓고 지능적인 행동을 할 수 있습니다. msdn.microsoft.com/ko-kr/library/tkxs89c5%28v=vs.71%29.aspx
필 호프
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.