정적 오류 검사를 사용하여 비즈니스 오류 방지


13

나는 정적 유형 검사의 팬입니다. 다음과 같은 어리석은 실수를 저 지르지 않습니다.

// java code
Adult a = new Adult();
a.setAge("Roger"); //static type checker would complain
a.setName(42); //and here too

그러나 다음과 같이 바보 같은 실수를 저지르는 것을 막지는 않습니다.

Adult a = new Adult();
// obviously you've mixed up these fields, but type checker won't complain
a.setAge(150); // nobody's ever lived this old
a.setWeight(42); // a 42lb adult would have serious health issues

동일한 유형을 사용하여 분명히 다른 종류의 정보를 나타내면 문제가 발생합니다. 이에 대한 좋은 해결책은 Integer비즈니스 로직 오류를 방지하고 기능을 추가하지 않기 위해 클래스를 확장하는 것이라고 생각했습니다 . 예를 들면 다음과 같습니다.

class Age extends Integer{};
class Pounds extends Integer{};

class Adult{
    ...
    public void setAge(Age age){..}
    public void setWeight(Pounds pounds){...}
}

Adult a = new Adult();
a.setAge(new Age(42));
a.setWeight(new Pounds(150));

이것이 좋은 습관으로 간주됩니까? 또는 그러한 제한적인 설계로 인해 예상치 못한 엔지니어링 문제가 있습니까?


5
a.SetAge( new Age(150) )여전히 컴파일 되지 않습니까?
John Wu

1
Java의 int 유형에는 고정 범위가 있습니다. 분명히 정수 범위 (예 : Integer <18, 110>)를 갖는 정수를 원합니다. 일부 (주류가 아닌) 언어가 제공하는 구체화 유형 또는 종속 유형을 찾고 있습니다.
Theodoros Chatzigiannakis

3
당신이 말하는 것은 디자인을하는 좋은 방법입니다! 안타깝게도 Java에는 원시 유형 시스템이 있으므로 올바르게 수행하기가 어렵습니다. 그리고 시도하더라도 성능 저하 또는 개발자 생산성 저하와 같은 부작용이 발생할 수 있습니다.
Euphoric

@JohnWu 그렇습니다. 예는 여전히 컴파일되지만 그 논리의 단일 실패 지점입니다. 일단 new Age(...)객체 를 선언 Weight하면 다른 곳 에서는 유형 변수에 잘못 할당 할 수 없습니다 . 실수가 발생할 수있는 장소의 수를 줄입니다.
J-bob

답변:


12

본질적으로 단위 시스템 (미터, 볼트 등과 같은 "물리적 단위"에서와 같이 단위 테스트가 아닌 "단위")을 요구합니다.

코드에서 Age시간과 Pounds질량을 나타냅니다. 이것은 단위 변환, 기본 단위, 정밀도 등과 같은 것들로 이어집니다.


그러한 일을 Java로 가져 오려는 시도가있었습니다.

후자의 두 가지 가이 github에 살고있는 것 같습니다 : https://github.com/unitsofmeasurement


C ++에는 Boost 를 통해 단위가 있습니다


LabView에는 여러 개의 장치 가 함께 제공됩니다 .


다른 언어로 된 다른 예가 있습니다. (편집은 환영합니다)


이것이 좋은 습관으로 간주됩니까?

위에서 볼 수 있듯이 단위를 사용하여 값을 처리하기 위해 언어를 사용할 가능성이 높을수록 기본적으로 단위를 지원합니다. LabView는 종종 측정 장치와 상호 작용하는 데 사용됩니다. 따라서 언어에 그러한 기능을 가지고 있고 사용하는 것이 좋은 습관으로 간주됩니다.

그러나 이러한 엄격한 양에 대한 수요가 낮은 범용 언어에서는 예상치 못한 결과 일 것입니다.

또는 그러한 제한적인 설계로 인해 예상치 못한 엔지니어링 문제가 있습니까?

추측 은 성능 / 메모리입니다. 많은 값을 처리하는 경우 값당 개체의 오버 헤드 가 문제 될 수 있습니다. 그러나 항상 그렇듯이 : 조기 최적화는 모든 악의 근원입니다 .

단위가 일반적으로 암시 적으로 다음과 같이 정의되어 있기 때문에 더 큰 "문제"가 사람들에게 익숙해 졌다고 생각합니다.

class Adult
{
    ...
    public void setAge(int ageInYears){..}

사람들은 int단위 시스템에 익숙하지 않을 때 단순한 것으로 묘사 될 수있는 무언가의 가치로 물건을 전달해야 할 때 혼란 스러울 것입니다 .


"사람들은 단순한 것으로 묘사 될 수있는 무언가에 대한 가치로서 대상을 전달해야 할 때 혼란스러워 할 것입니다 int."--- 우리는 여기에 지침서로 최소 서프라이즈 교장이 있습니다. 잘 잡았습니다.
Greg Burghardt

1
나는 사용자 정의 움직임에게 단위 라이브러리의 영역과 종속 유형에이 밖으로 범위 생각
JK합니다.

6

null의 답과 달리, 정수가 측정을 설명하기에 충분하지 않은 경우 "단위"에 대한 유형을 정의하면 유리할 수 있습니다. 예를 들어, 무게는 종종 동일한 측정 시스템 내에서 여러 단위로 측정됩니다. "파운드"및 "온스"또는 "킬로그램"및 "그램"을 생각하십시오.

보다 세부적인 측정 수준이 필요한 경우 장치 유형을 정의하는 것이 좋습니다.

public struct Weight {
    private int pounds;
    private int ounces;

    public Weight(int pounds, int ounces) {
        // Value range checks go here
        // Throw exception if ounces is greater than 16?
    }

    // Getters go here
}

"나이"와 같은 것을 위해 나는 사람의 생년월일을 기준으로 런타임에 계산하는 것이 좋습니다.

public class Adult {
    private Date birthDate;

    public Interval getCurrentAge() {
        return calculateAge(Date.now());
    }

    public Interval calculateAge(Date date) {
        // Return interval between birthDate and date
    }
}

2

찾고있는 것으로 태그 유형이라고 합니다. "이것은 나이를 나타내는 정수"이고 "이것은 정수이지만 무게를 나타냅니다"그리고 "하나를 다른 것으로 지정할 수 없습니다"라고 말하는 방법입니다. 이것은 미터 또는 킬로그램과 같은 물리적 단위보다 더 큽니다 : 프로그램에서 "사람의 키"와 "지도상의 지점 사이의 거리"를 미터 단위로 측정 할 수 있지만 둘을 할당했기 때문에 서로 호환되지 않을 수 있습니다 다른 하나는 비즈니스 로직의 관점에서 이해가되지 않습니다.

스칼라와 같은 일부 언어는 태그 유형을 매우 쉽게 지원합니다 (위의 링크 참조). 다른 경우에는 고유 한 래퍼 클래스를 만들 수 있지만 이는 덜 편리합니다.

예를 들어 사람의 키가 "합리적인지"확인하는 것도 또 다른 문제입니다. 이러한 코드를 Adult클래스 (생성자 또는 설정자) 또는 태그가 지정된 유형 / 래퍼 클래스에 넣을 수 있습니다 . 어떤면에서, 같은 클래스 내장 URL또는 UUID(예를 들어, 유틸리티 메소드를 제공하는, 다른 사람의 사이에서)와 같은 역할을 수행.

태그 유형이나 래퍼 클래스를 사용하면 실제로 코드를 개선하는 데 도움이 될지 여부는 몇 가지 요소에 따라 다릅니다. 객체가 단순하고 필드가 거의없는 경우 잘못 할당 할 위험이 적으며 태그 유형을 사용하는 데 필요한 추가 코드는 노력할 가치가 없습니다. 복잡한 구조와 많은 필드를 가진 복잡한 시스템에서 (특히 많은 유형이 동일한 기본 유형을 공유하는 경우) 실제로 도움이 될 수 있습니다.

내가 작성한 코드에서 맵을 전달하면 종종 래퍼 클래스를 만듭니다. 와 같은 유형 Map<String, String>은 자체적으로 매우 불투명하므로 의미있는 이름을 가진 클래스로 래핑 NameToAddress하면 많은 도움이됩니다. 물론 태그 유형 Map<Name, Address>을 사용하면 전체지도에 래퍼를 작성할 필요가 없습니다.

그러나 Strings 또는 Integers와 같은 간단한 유형의 경우 랩퍼 클래스 (Java의)가 너무 성가신 것으로 나타났습니다. 일반적인 비즈니스 로직은 그리 나쁘지는 않지만 이러한 유형을 JSON으로 직렬화하고 DB 객체에 매핑하는 등 많은 문제가 발생했습니다. 모든 대형 프레임 워크 (예 : Jackson 및 Spring Data)에 대한 매퍼 및 후크를 작성할 수 있습니다. 그러나이 코드와 관련된 추가 작업 및 유지 보수는이 래퍼를 사용하여 얻는 모든 것을 상쇄합니다. 물론 YMMV와 다른 시스템에서는 균형이 다를 수 있습니다.

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