“요요 문제를 피하십시오”가“원시 강박 관념”을 허용하는 타당한 이유입니까? 이동해야한다고 생각합니다. 요요 문제 (반 패턴)로

에 따르면 때 원시 집착하지 않는 코드 냄새입니까? String 객체 대신 우편 번호를 나타내는 ZipCode 객체를 만들어야합니다.

그러나 내 경험상, 나는보고 싶어한다

public class Address{
    public String zipCode;
}

대신에

public class Address{
    public ZipCode zipCode;
}

후자는 프로그램을 이해하기 위해 ZipCode 클래스로 이동해야한다고 생각하기 때문입니다.

그리고 모든 기본 데이터 필드가 클래스로 대체 된 경우 정의를 보려면 많은 클래스간에 이동해야한다고 생각합니다. 요요 문제 (반 패턴)로 고통받는 것처럼 느껴집니다 .

따라서 ZipCode 메소드를 새 클래스로 이동하고 싶습니다. 예를 들면 다음과 같습니다.

낡은:

public class ZipCode{
    public boolean validate(String zipCode){
    }
}

새로운:

public class ZipCodeHelper{
    public static boolean validate(String zipCode){
    }
}

우편 번호를 확인해야하는 사람 만이 ZipCodeHelper 클래스에 의존합니다. 그리고 원시적 인 강박 관념을 유지하는 또 다른 “이점”을 발견했습니다. 클래스가 직렬화 된 양식처럼 보이게합니다 (예 : 문자열 열 zipCode가있는 주소 테이블).

내 질문은 “요요 문제를 피하는 것”(클래스 정의 간 이동)이 “원시적 인 집착”을 허용하는 타당한 이유인가?



답변

Address 클래스를 이해하기 위해 ZipCode 클래스를 요요 할 필요 가 없다고 가정 합니다. ZipCode가 잘 디자인되어 있다면 Address 클래스를 읽는 것만으로도 분명합니다.

프로그램은 엔드-투-엔드로 읽히지 않습니다. 일반적으로 프로그램을 구현하기에는 너무 복잡합니다. 프로그램의 모든 코드를 동시에 생각할 수는 없습니다. 따라서 추상화와 캡슐화를 사용하여 프로그램을 의미있는 단위로 “청크”하므로 프로그램에 의존하는 모든 코드를 읽을 필요없이 프로그램의 한 부분 (주소 클래스 등)을 볼 수 있습니다.

예를 들어 코드에서 String을 만날 때마다 String의 소스 코드를 읽지 않아도됩니다.

ZipCode에서 ZipCodeHelper로 클래스 이름을 바꾸면 우편 번호와 우편 번호 도우미라는 두 가지 개념이 있습니다. 두 배나 복잡합니다. 이제 유형 시스템은 유형이 동일하므로 임의의 문자열과 유효한 우편 번호를 구별하는 데 도움이되지 않습니다. 이것은 “강박 관념”이 적절한 곳입니다. 당신은 프리미티브 (primitive) 주위에 간단한 래퍼 타입을 피하기 위해 더 복잡하고 덜 안전한 대안을 제안하고 있습니다.

이 특정 유형에 따라 유효성 검사 나 다른 논리가없는 경우 프리미티브 사용이 정당화됩니다. 그러나 로직을 추가하자마자이 로직을 유형으로 캡슐화하면 훨씬 간단합니다.

직렬화에 관해서는 사용중인 프레임 워크의 제한 사항처럼 들립니다. 반드시 ZipCode를 문자열로 직렬화하거나 데이터베이스의 열에 맵핑 할 수 있어야합니다.


답변

할 수 있다면 :

new ZipCode("totally invalid zip code");

그리고 ZipCode의 생성자는 다음을 수행합니다.

ZipCodeHelper.validate("totally invalid zip code");

그런 다음 캡슐화를 중단하고 ZipCode 클래스에 꽤 바보 같은 의존성을 추가했습니다. 생성자 호출 하지 않으면ZipCodeHelper.validate(...) 실제로 시행하지 않고 자체 섬에서 로직을 분리 한 것입니다. 잘못된 우편 번호를 만들 수 있습니다.

validate메소드는 ZipCode 클래스의 정적 메소드 여야합니다. 이제 “유효한”우편 번호에 대한 지식이 ZipCode 클래스와 함께 제공됩니다. 코드 예제가 Java처럼 보이면 ZipCode 생성자는 잘못된 형식이 제공되면 예외를 발생시켜야합니다.

public class ZipCode {
    private String zipCode;

    public ZipCode(string zipCode) {
        if (!validate(zipCode))
            throw new IllegalFormatException("Invalid zip code");

        this.zipCode = zipCode;
    }

    public static bool validate(String zipCode) {
        // logic to check format
    }

    @Override
    public String toString() {
        return zipCode;
    }
}

생성자는 형식을 확인하고 예외를 발생시켜 유효하지 않은 우편 번호가 생성되지 않도록하고 정적 validate코드를 다른 코드에서 사용할 수 있으므로 형식을 확인하는 논리가 ZipCode 클래스에 캡슐화됩니다.

이 ZipCode 클래스 변형에는 “yo-yo”가 없습니다. 적절한 객체 지향 프로그래밍이라고합니다.


또한 ZipCodeFormat 또는 PostalService라는 다른 클래스 (예 : PostalService.isValidPostalCode (…), PostalService.parsePostalCode (…) 등)가 필요할 수있는 국제화도 무시합니다.


답변

이 질문으로 많은 어려움을 겪고 있다면 사용하는 언어가 그 일에 적합한 도구가 아닐 수 있습니다. 이러한 종류의 “도메인 유형 프리미티브”는 예를 들어 F #으로 표현하기가 쉽습니다.

예를 들어 다음과 같이 작성할 수 있습니다.

type ZipCode = ZipCode of string
type Town = Town of string

type Adress = {
  zipCode: ZipCode
  town: Town
  //etc
}

let adress1 = {
  zipCode = ZipCode "90210"
  town = Town "Beverly Hills"
}

let faultyAdress = {
  zipCode = "12345"  // <-Compiler error
  town = adress1.zipCode // <- Compiler error
}

이것은 다른 엔티티의 ID를 비교하는 것과 같은 일반적인 실수를 피하는 데 실제로 유용합니다. 이러한 유형의 프리미티브는 C # 또는 Java 클래스보다 훨씬 가볍기 때문에 실제로 사용하게됩니다.


답변

답은 우편 번호로 실제로하고 싶은 것에 전적으로 달려 있습니다. 두 가지 극단적 인 가능성이 있습니다.

(1) 모든 주소는 단일 국가에 있어야합니다. 전혀 예외는 없습니다. (예 : 외국 고객이 없거나 외국 고객을 위해 일하는 동안 개인 주소가 해외에있는 직원이 없습니다.)이 국가에는 우편 번호가 있으며 심각한 문제가 없을 것으로 예상 할 수 있습니다 (예 : 자유 형식 입력이 필요하지 않음) “현재 D4B 6N2이지만 2 주마다 변경됩니다”). 우편 번호는 주소 지정뿐만 아니라 지불 정보의 검증 또는 유사한 목적으로 사용됩니다. -이러한 상황에서 우편 번호 클래스는 의미가 있습니다.

(2) 주소는 거의 모든 국가에있을 수 있으므로 우편 번호가 있거나없는 수십 또는 수백 개의 주소 지정 체계 (수천 가지의 예외 및 특수 사례 포함)가 적합합니다. “ZIP”코드는 우편 번호를 제공하는 것을 잊지 않는 국가에서 온 사람들에게 상기시켜 줄 것을 요청합니다. 주소는 누군가가 자신의 계정에 액세스 할 수없고 이름과 주소를 증명할 수있는 경우에만 액세스가 복원되도록 사용됩니다. -이러한 상황에서 모든 관련 국가의 우편 번호 클래스는 엄청난 노력이 될 것입니다. 다행히도 그들은 전혀 필요하지 않습니다.


답변

다른 답변은 OO 도메인 모델링 및 더 풍부한 유형을 사용하여 가치를 나타내는 것에 대해 이야기했습니다.

나는 당신이 게시 한 예제 코드를 감안할 때 동의하지 않습니다.

그러나 그것이 실제로 귀하의 질문에 대한 답변인지 궁금합니다.

다음 시나리오를 고려하십시오 (실제로 진행중인 프로젝트에서 가져 왔습니다).

중앙 서버와 통신하는 필드 장치에 원격 응용 프로그램이 있습니다. 장치 항목의 DB 필드 중 하나는 필드 장치가있는 주소의 우편 번호입니다. 우편 번호 (또는 그 문제에 대한 나머지 주소)는 신경 쓰지 않습니다. 그것에 관심이있는 모든 사람들은 HTTP 경계의 반대편에 있습니다. 당신은 데이터의 단일 진실 소스가됩니다. 도메인 모델링에는 자리가 없습니다. 당신은 그것을 기록하고, 유효성을 검사하고, 저장하고, 요청시 JSON 블롭에서 다른 곳으로 셔플합니다.

이 시나리오에서 SQL 정규식 제약 (또는 ORM에 해당)에 삽입을 확인하는 이상 아무것도 많이하고있는 것은 아마도 과잉 YAGNI의 다양한.


답변

ZipCode귀하의 경우 추상화는 의미를 만들 수있는 Address클래스도하지 않은 TownName속성을. 그렇지 않으면 절반의 추상화가 있습니다. 우편 번호는 도시를 지정하지만이 두 가지 관련 정보는 다른 클래스에 있습니다. 이치에 맞지 않습니다.

그러나 그럼에도 불구하고 여전히 원시적 인 강박 관념에 대한 올바른 응용 (또는 오히려 해결책)이 아닙니다. 내가 이해하는 것처럼 주로 두 가지에 중점을 둡니다.

  1. 특히 프리미티브 콜렉션이 필요한 경우 메소드의 입력 (또는 출력) 값으로 프리미티브를 사용합니다.
  2. 시간이 지남에 따라 추가 속성을 키우는 클래스는 이들 중 일부를 자체 하위 클래스로 그룹화해야하는지 여부를 다시 고려하지 않습니다.

귀하의 경우도 마찬가지입니다. 주소는 명확하게 필요한 속성 (거리, 번호, 우편 번호, 도시, 주, 국가 등)이 포함 된 잘 정의 된 개념입니다. 지구상의 위치 지정이라는 단일 책임 이 있기 때문에이 데이터를 분해 할 이유가 거의 없습니다 . 주소를 이해하려면 이러한 모든 필드가 필요 합니다 . 주소의 절반은 의미가 없습니다.

이렇게하면 더 이상 세분화 할 필요가 없다는 것을 알 수 있습니다. 더 이상 세분화하면 Address클래스 의 기능적 의도가 손상됩니다 . 마찬가지로, 사람이 연결 되지 않은 상태가 도메인에서 의미있는 개념이 아니라면 클래스 Name에서 하위 클래스를 사용할 필요가 없습니다 . (보통) 그렇지 않습니다. 이름은 사람을 식별하는 데 사용되며 일반적으로 가치가 없습니다.PersonName


답변

기사에서 :

보다 일반적으로, 요요 문제는 또한 사람이 개념을 이해하기 위해 다른 정보원 사이를 계속 넘겨야하는 모든 상황을 지칭 할 수있다.

소스 코드는 작성된 것보다 훨씬 자주 읽습니다 . 따라서 많은 파일간에 전환해야하는 요요 문제가 우려됩니다.

그러나, 어떤 (서로 사이에 앞뒤로 전화) 깊이 상호 의존적 인 모듈 또는 클래스를 처리 할 때, 요요 문제는 훨씬 더 관련 느낀다. 이것들은 읽을만한 특별한 종류의 악몽이며 요요 문제의 창시자가 생각한 것입니다.

그러나 – , 추상화의 너무 많은 레이어를 피하는 것이 중요합니다!

사소한 추상화는 어느 정도 누설됩니다. -새는 추상화의 법칙.

예를 들어, 나는 mmmaaa의 대답 에서 ” 주소 클래스를 이해하기 위해 ZipCode 클래스를 [(방문)] 할 필요 가 없다 ” 는 가정에 동의 하지 않습니다 . 내 경험에 따르면 당신 적어도 처음 몇 번은 코드를 읽었습니다. 다른 사람이 언급 한 그러나, 거기에 있습니다 경우가 ZipCode클래스가 적합하다.

YAGNI (나중에 않을거야 필요 그것은) (너무 많은 레이어 코드) 라자냐 코드를 방지하기 위해 따라야하는 더 좋은 패턴이다 – 추상화, 같은 종류의 같은과 클래스는 프로그래머가 도움을, 그리고 그들이하지 않는 한 사용되어서는 안 된다 원조.

나는 개인적으로 “코드 줄 저장”(그리고 관련 “파일 / 모듈 / 클래스 저장”등)을 목표로합니다. 나는 “원초적인 집착”이라는 표현을 나에게 적용 할 사람이 있다고 확신한다. 레이블, 패턴, 반 패턴에 대해 걱정하는 것보다 추론하기 쉬운 코드를 갖는 것이 더 중요하다 . 함수, 모듈 / 파일 / 클래스 생성 또는 공통 위치에 함수를 배치 할시기를 올바르게 선택하는 것은 매우 상황이 좋지 않습니다. 나는 대략 3-100 개의 라인 함수, 80-500 개의 라인 파일, 그리고 재사용 가능한 라이브러리 코드에 대한 “1, 2, n”을 목표로한다 ( SLOC- 주석 또는 상용구를 포함하지 않음; 나는 일반적으로 필수 라인 당 최소 1 개의 추가 SLOC를 원한다) 상용구).

가장 긍정적 인 패턴은 개발자가 필요할 때 정확히 그렇게함으로써 발생 합니다 . 동일한 문제를 해결하지 않고 패턴을 적용하는 것보다 읽기 쉬운 코드를 작성하는 방법을 배우는 것이 훨씬 중요합니다. 모든 훌륭한 개발자는 문제에 적합한 드문 경우에 팩토리 패턴을 미리 보지 않고도 구현할 수 있습니다. 팩토리 패턴, 옵저버 패턴 및 아마도 수백 개를 그 이름을 알지 못하고 사용했습니다 (즉, “가변 할당 패턴”이 있습니까?). 재미있는 실험을 위해-JS 언어에 몇 개의 GoF 패턴이 내장되어 있는지 확인하십시오 -2009 년 12-15 년 후에 다시 계산을 중단했습니다. 팩토리 패턴은 JS 생성자에서 객체를 반환하는 것만 큼 간단합니다. WidgetFactory.

그래서 – , 가끔 ZipCode 좋은 클래스입니다. 그러나, , 요요 문제는 엄격하게 관련되지 않습니다.