문제

비슷하다 하드 코딩 리터럴이 허용 되는가?, 그러나 나는 여기서 "마법 줄"을 구체적으로 생각하고 있습니다.

큰 프로젝트에는 다음과 같은 구성 옵션 테이블이 있습니다.

Name         Value
----         -----
FOO_ENABLED  Y
BAR_ENABLED  N
...

(수백 명).

일반적인 관행은 일반 기능을 호출하여 다음과 같은 옵션을 테스트하는 것입니다.

if (config_options.value('FOO_ENABLED') == 'Y') ...

(물론,이 동일한 옵션은 시스템 코드의 많은 장소에서 점검해야 할 수도 있습니다.)

새 옵션을 추가 할 때 다음과 같은 "Magic String"을 숨기는 기능을 추가하는 것을 고려하고있었습니다.

if (config_options.foo_enabled()) ...

그러나 동료들은 내가 선외로 가서이를 수행하는 것에 반대한다고 생각했다. 하드 코딩을 선호했다.

  • 그것이 우리가 일반적으로하는 일입니다
  • 코드를 디버깅 할 때 무슨 일이 일어나고 있는지 쉽게 볼 수 있습니다.

문제는, 나는 그들의 요점을 볼 수 있다는 것입니다! 현실적으로, 우리는 어떤 이유로 든 옵션의 이름을 바꾸지 않을 것이므로, 내 기능에 대해 생각할 수있는 유일한 장점은 컴파일러가 fo_enabled ()와 같은 오타를 잡을 것이지만 'fo_enabled'가 아니라는 것입니다.

어떻게 생각해? 다른 장점/단점을 놓쳤습니까?

도움이 되었습니까?

해결책

if (config_options.isTrue('FOO_ENABLED')) {...
}

맵의 래퍼 클래스를 작성하는 경우에도 하드 코드 y 수표를 한 곳으로 제한하십시오.

if (config_options.isFooEnabled()) {...
}

100 개의 구성 옵션과 100 가지 방법이있을 때까지 괜찮을 수 있습니다 (따라서 구현을 결정하기 전에 미래의 응용 프로그램 성장과 요구에 대한 판단을 할 수 있습니다). 그렇지 않으면 매개 변수 이름에 대한 정적 문자열 클래스를 갖는 것이 좋습니다.

if (config_options.isTrue(ConfigKeys.FOO_ENABLED)) {...
}

다른 팁

코드에서 한 번 문자열을 사용하면 일반적으로 어딘가에 일정하게 만드는 것에 대해 걱정하지 않습니다.

코드에서 문자열을 두 번 사용하면 고려하다 일정하게 만듭니다.

코드에서 문자열을 세 번 사용하면 거의 확실하게 상수로 만들 것입니다.

나는 그 질문이 오래되었다는 것을 알고 있지만, 그것은 내 마진에 나타났습니다.

AFAIC, 여기의 문제는 질문이나 답변에서 정확하게 식별되지 않았습니다. 잠시 동안 '하코드 끈 "을 잊어 버리십시오.

  1. 데이터베이스에는 참조 테이블이 포함되어 있습니다 config_options. PK는 문자열입니다.

  2. PK에는 두 가지 유형이 있습니다.

    • 사용자 (및 개발자)가보고 사용하는 의미있는 식별자. 이 PK는 안정적이어야하며 의존 할 수 있습니다.

    • 무의미한 Id 사용자가 절대 볼 수없는 열, 개발자가 알고 있어야하며 코드를 코딩해야합니다. 이것들은 의존 할 수 없습니다.

  3. 의미있는 PK의 절대 값을 사용하여 코드를 작성하는 것은 일반적이고 정상입니다. IF CustomerCode = "IBM" ... 또는 IF CountryCode = "AUS" 등.

    • 무의미한 PK의 절대 값을 참조하는 것은 허용되지 않습니다 (자동 증가로 인해, 간격이 바뀌었기 때문에, 도매 대체 값).
      .
  4. 참조 테이블은 의미있는 pks를 사용합니다. 코드에서 문자 문자열을 참조하는 것은 피할 수 없습니다. 가치를 숨기면 유지 보수가 더 어려워집니다. 코드는 더 이상 문자가 아닙니다. 동료가 옳습니다. 또한 사이클을 씹는 추가 중복 기능이 있습니다. 문자 그대로의 오타가 있다면, 당신은 곧 UAT 전에 DEV 테스트 중에 그것을 알게 될 것입니다.

    • 수백 리터의 수백 가지 기능은 터무니 없습니다. 함수를 구현하는 경우 코드를 정규화하고 수백 리터에 사용할 수있는 단일 함수를 제공하십시오. 이 경우, 우리는 벌거 벗은 문자로 돌아가고 기능을 분배 할 수 있습니다.

    • 요점은, 문자를 숨기려는 시도는 가치가 없다는 것입니다.
      .

  5. 그것은 "하드 코딩"으로 해석 될 수 없으며, 그것은 상당히 다른 것입니다. 나는 그것이 당신의 문제가있는 곳이라고 생각합니다. 이러한 구성을 "하드 코드"로 식별합니다. 말 그대로 의미가 많은 PK를 참조하는 것입니다.

  6. 이제 모든 코드 세그먼트의 관점에서만 동일한 값을 몇 번 사용하는 경우 변수의 문자 그대로 문자열을 캡처 한 다음 나머지 코드 블록의 변수를 사용할 수 있습니다. 확실히 기능이 아닙니다. 그러나 그것은 효율성과 좋은 관행 문제입니다. 그조차도 효과를 바꾸지 않습니다 IF CountryCode = @cc_aus

내 경험에 따르면, 이런 종류의 문제는 실제 OOP를 수행하지 못하고 건조 원칙을 따르지 않는 더 깊은 문제를 숨기고 있습니다.

간단히 말해서 각 조치에 대한 적절한 정의로 시작 시간에 결정을 캡처하십시오. 내부에 그만큼 if 진술을 한 다음 두 가지를 버리십시오 config_options 그리고 런타임 테스트.

아래 세부 사항.

샘플 사용량은 다음과 같습니다.

if (config_options.value('FOO_ENABLED') == 'Y') ...

특히 다음과 같은 진술에 관해서는 "Ellipsis에서 무슨 일이 일어나고 있습니까?"라는 명백한 질문을 제기합니다.

(물론,이 동일한 옵션은 시스템 코드의 많은 장소에서 점검해야 할 수도 있습니다.)

이들 각각을 가정 해 봅시다 config_option 값은 실제로 단일 문제 도메인 (또는 구현 전략) 개념에 해당합니다.

이 작업을 수행하는 대신 (코드 전체의 다양한 장소에서 반복적으로) :

  1. 문자열 (태그)을 가져 가서
  2. 해당 다른 문자열 (값)을 찾으십시오.
  3. 그 가치를 부울 평등 한 것으로 테스트하고,
  4. 이 테스트를 바탕으로 조치를 수행할지 여부를 결정하십시오.

"구성 가능한 동작"의 개념을 캡슐화하는 것이 좋습니다.

예를 들어 봅시다 (분명히 과대 FOO_ENABLED ... ;-) 코드가 영어 단위 또는 메트릭 단위로 작동해야합니다. 만약에 METRIC_ENABLED "true", 내부 계산을 위해 사용자 입력 데이터를 메트릭에서 영어로 변환하고 결과를 표시하기 전에 다시 변환합니다.

인터페이스 정의 :

public interface MetricConverter {
    double toInches(double length);
    double toCentimeters(double length);
    double toPounds(double weight);
    double toKilograms(double weight);
}

한 곳에서 개념과 관련된 모든 행동을 식별합니다. METRIC_ENABLED.

그런 다음 해당 행동을 수행하는 모든 방법을 구체적으로 구현합니다.

public class NullConv implements MetricConverter {
    double toInches(double length) {return length;}
    double toCentimeters(double length) {return length;}
    double toPounds(double weight)  {return weight;}
    double toKilograms(double weight)  {return weight;}
}

그리고

// lame implementation, just for illustration!!!!
public class MetricConv implements MetricConverter {
    public static final double LBS_PER_KG = 2.2D;
    public static final double CM_PER_IN = 2.54D
    double toInches(double length) {return length * CM_PER_IN;}
    double toCentimeters(double length) {return length / CM_PER_IN;}
    double toPounds(double weight)  {return weight * LBS_PER_KG;}
    double toKilograms(double weight)  {return weight / LBS_PER_KG;}
}

시작 시간에 config_options 값, 다음과 같이 구성 가능한 작업 세트를 초기화합니다.

MetricConverter converter = (metricOption()) ? new MetricConv() : new NullConv();

(표현 metricOption() 위는 metric_enabled의 가치를 보는 것을 포함하여 일회성 점검에 대한 스탠드 인입니다 .-)

그런 다음 코드가 말한 곳마다 다음과 같습니다.

double length = getLengthFromGui();
if (config_options.value('METRIC_ENABLED') == 'Y') {
    length = length / 2.54D;
}
// do some computation to produce result
// ...
if (config_options.value('METRIC_ENABLED') == 'Y') {
    result = result * 2.54D;
}
displayResultingLengthOnGui(result);

다시 작성하십시오.

double length = converter.toInches(getLengthFromGui());
// do some computation to produce result
// ...
displayResultingLengthOnGui(converter.toCentimeters(result));

해당 한 개념과 관련된 모든 구현 세부 사항은 이제 깨끗하게 포장되어 있기 때문에 다음과 관련된 모든 향후 유지 보수 METRIC_ENABLED 한 곳에서 수행 할 수 있습니다. 또한 런타임 트레이드 오프는 승리입니다. 메소드를 호출하는 "오버 헤드"는 맵에서 문자열 값을 가져오고 문자열#equals를 수행하는 오버 헤드와 비교할 때 사소한 것입니다.

나는 당신이 언급 한 두 가지 이유 인 String에서 틀린 틀린이 가능하며, 실행 시간과 이름 변경의 가능성 (비록 슬림)이 될 때까지 감지 할 수 없다고 생각합니다.

그 외에도 타이핑 함수를 얻을 수 있습니다. 이제 부울 만 저장하는 것 같습니다. int, 문자열 등을 저장 해야하는 경우 get_string ( "foo") 또는 유형과 함께 get_foo ()를 사용합니다. get_int ( "foo").

나는 상수를 사용해야하며 하드 코딩 된 리터럴을 사용해서는 안됩니다.

당신은 그들이 바뀌지 않을 것이라고 말할 수 있지만, 당신은 결코 알지 못할 수도 있습니다. 그리고 그것을 습관으로 만드는 것이 가장 좋습니다. 상징적 상수를 사용합니다.

여기에는 두 가지 문제가 있다고 생각합니다.

  • 현재 프로젝트에서 하드 코딩 된 문자열을 사용하는 협약은 이미 잘 확립되어 있으므로 프로젝트 작업을 수행하는 모든 개발자는 이에 익숙합니다. 그것은 모든 이유에 대해 차선책이 될 수 있지만, 코드에 익숙한 모든 사람들이 코드를보고 코드가 무엇을 해야하는지 본능적으로 알고 있습니다. 특정 부분에서 "새로운"기능을 사용하도록 코드를 변경하면 코드를 읽기가 약간 어렵게 만들 수 있습니다 (사람들은 새로운 컨벤션이 무엇을 생각하고 기억해야하기 때문에) 유지하기가 어렵 기 때문입니다. 그러나 전체 프로젝트를 새로운 컨벤션으로 바꾸는 것은 전환을 신속하게 스크립트 할 수 없다면 잠재적으로 엄청나게 비싸다고 생각합니다.
  • a 새로운 프로젝트, 상징적 상수는 모든 이유 때문에 IMO 방식입니다. 특히 컴파일 타임에 컴파일러 캐치 오류를 만드는 것은 런타임에 인간이 잡는 데 오류를 만드는 것은 매우 유용한 규칙이기 때문입니다.

코드를 통해 사용되는 경우 강하게 유형 된 구성 클래스를 선호합니다. 적절하게 명명 된 메소드를 사용하면 가독성이 손실되지 않습니다. 문자열에서 다른 데이터 유형 (Decimal/Float/Int)으로 전환 해야하는 경우 여러 위치에서 변환을 수행하는 코드를 반복 할 필요가 없으며 결과를 캐시하여 변환이 한 번만 발생합니다. 당신은 이미 이미 이것의 기초를 가지고 있었기 때문에 나는 그것이 새로운 일을하는 방법.

고려해야 할 또 다른 것은 의도입니다. 현지화 하드 코딩 문자열이 필요한 프로젝트에있는 경우 모호 할 수 있습니다. 다음을 고려하세요:

const string HELLO_WORLD = "Hello world!";
print(HELLO_WORLD);

프로그래머의 의도는 분명합니다. 상수를 사용한다는 것은이 문자열을 국소화 할 필요가 없음을 의미합니다. 이제이 예를 살펴보십시오.

print("Hello world!");

여기서 우리는 확실하지 않습니다. 프로그래머는이 문자열이 현지화되기를 원하지 않았거나 프로그래머 가이 코드를 작성하는 동안 현지화를 잊었습니까?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top