Java 및 Ceylon 객체의 구성 및 검증

5408 단어 JavaCeylon
자바 코드를 Ceylon 코드로 변환할 때, 때때로 나는 자바 클래스 구조기가 검증과 초기화를 혼동하는 상황을 만날 수 있다.간단하지만 인위적인 코드 예를 사용하여 내가 논술하고 싶은 뜻을 설명하자.
일부 나쁜 코드
다음 Java 클래스를 고려하십시오.(점원, 집에 이런 코드 쓰지 마세요)

public class Period {
 private final Date startDate;
 private final Date endDate;
 //returns null if the given String
 //does not represent a valid Date
 private Date parseDate(String date) {
  ...
 }
 public Period(String start, String end) {
  startDate = parseDate(start);
  endDate = parseDate(end);
 }
 public boolean isValid() {
  return startDate!=null && endDate!=null;
 }
 public Date getStartDate() {
  if (startDate==null) 
   throw new IllegalStateException();
  return startDate;
 }
 public Date getEndDate() {
  if (endDate==null)
   throw new IllegalStateException();
  return endDate;
 }
}
야, 내가 전에 경고했잖아. 그것은 인위적인 거야.그러나 실제 자바 코드에서 이런 물건을 찾는 것은 사실상 흔치 않다.
문제는 인자 입력 (숨겨진parseDate () 방법에서) 의 검증이 실패하더라도 Period의 실례를 얻을 수 있다는 것이다.그러나 우리가 얻은 그 Period는 유효한 상태가 아니다.엄밀히 말하면 내 말은 무엇일까?
좋아, 만약 대상이 공용 작업에 의미 있게 응답하지 못한다면, 나는 그것이 비유효 상태에 있다고 말할 것이다.이 예에서 getStartDate () 와 getendDate () 는 Illegal State Exception 이상을 던진다. 이것이 바로 내가'의미 있는'상황이 아니라고 생각하는 것이다.
다른 측면에서 볼 때 이 예는 Period를 설계할 때 우리에게 유형 안전의 실패가 나타났다.검사되지 않은 이상은 유형 시스템의 빈틈을 나타냅니다.따라서 더 좋은Period의 유형이 안전한 디자인은 검사되지 않은 이상을 사용하지 않는 것이다. 이 예에서 Illegal State Exception 이상을 던지지 않는 것을 의미한다.
(실제로 실제 코드에서 getStart Date () 방법은null을 검사하지 않고 이 코드 줄 다음에 Null Pointer Exception 이상을 일으킬 수 있습니다. 이것은 더욱 엉망입니다.)
위의 Period 클래스를 Ceylon 형식의 클래스로 쉽게 변환할 수 있습니다.

shared class Period(String start, String end) {
 //returns null if the given String
 //does not represent a valid Date
 Date? parseDate(String date) => ... ;
 value maybeStartDate = parseDate(start);
 value maybeEndDate = parseDate(end);
 shared Boolean valid
  => maybeStartDate exists 
  && maybeEndDate exists;
 shared Date startDate {
  assert (exists maybeStartDate);
  return maybeStartDate;
 }
 shared Date endDate {
  assert (exists maybeEndDate);
  return maybeEndDate;
 }
}
물론 이 코드도 원시 자바 코드와 같은 문제에 부딪힐 수 있다.두 개의 assert 기호가 우리를 향해 소리를 질렀는데, 코드의 유형 안전에 문제가 있었다.
Java 코드 향상
자바에서 우리는 어떻게 이 코드를 개선합니까?그래, 이것이 바로 하나의 예이다. 자바에 대해 많은 비난을 받고 있는 이상 검사가 매우 합리적인 해결 방법이 될 것이다!우리는 Period를 약간 수정해서 구조기에서 검사된 이상을 던질 수 있다.

public class Period {
 private final Date startDate;
 private final Date endDate;
 //throws if the given String
 //does not represent a valid Date
 private Date parseDate(String date)
   throws DateFormatException {
  ...
 }
 public Period(String start, String end) 
   throws DateFormatException {
  startDate = parseDate(start);
  endDate = parseDate(end);
 }
 public Date getStartDate() {
  return startDate;
 }
 public Date getEndDate() {
  return endDate;
 }
}
현재, 이 해결 방안을 사용하면 우리는 비유효 상태인Period를 얻지 못할 것입니다. 실례화된 Period 코드는 컴파일러가 무효 입력을 처리하는 것을 책임집니다. 이것은 DateFormateException 이상을 포착할 것입니다.

try {
 Period p = new Period(start, end);
 ...
}
catch (DateFormatException dfe) {
 ...
}
이것은 이미 검사한 이상에 대한 괜찮은, 완벽한, 정확한 사용이다. 불행하게도 나는 자바 코드가 위와 같이 이미 검사한 이상을 사용하는 것을 거의 보지 못했다.
Ceylon 코드 향상
그럼 세일론은 어때요?Ceylon은 이상 검사를 하지 않았기 때문에 다른 해결 방법을 찾아야 합니다.전형적으로, 자바에서 함수를 호출하면 이상이 검사된 상황을 던지고, Ceylon은 함수를 호출하여 연합 형식으로 되돌려줍니다.하나의 종류의 초기화는 클래스 자신을 제외한 모든 유형을 되돌려 주지 않기 때문에, 우리는 혼합된 초기화/검증 논리를 추출하여 공장 함수로 만들어야 한다.

//returns DateFormatError if the given 
//String does not represent a valid Date
Date|DateFormatError parseDate(String date) => ... ;
shared Period|DateFormatError parsePeriod
  (String start, String end) {
 value startDate = parseDate(start);
 if (is DateFormatError startDate) {
  return startDate;
 }
 value endDate = parseDate(end);
 if (is DateFormatError endDate) {
  return endDate;
 }
 return Period(startDate, endDate);
}
shared class Period(startDate, endDate) {
 shared Date startDate;
 shared Date endDate;
}
유형 시스템에 따라 호출자는 DateFormatError를 처리할 의무가 있습니다.

value p = parsePeriod(start, end);
if (is DateFormatError p) {
 ...
}
else {
 ...
}
또는, 만약 우리가 정해진 날짜 형식에 대한 실제 문제에 관심이 없다면, (이것은 가능하다, 우리가 일하는 초기화 코드가 그 정보를 잃어버렸다고 가정한다면), 우리는 DateFormatError가 아니라 Null을 사용할 수 있다.

//returns null if the given String 
//does not represent a valid Date
Date? parseDate(String date) => ... ;
shared Period? parsePeriod(String start, String end)
 => if (exists startDate = parseDate(start), 
   exists endDate = parseDate(end))
  then Period(startDate, endDate)
  else null;
shared class Period(startDate, endDate) {
 shared Date startDate;
 shared Date endDate;
}
적어도 공장 함수를 사용하는 방법은 우수하다고 할 수 있다. 왜냐하면 일반적으로 검증 논리와 대상 초기화 사이에 더욱 좋은 격리가 있기 때문이다.이 점은 Ceylon에서 특히 유용하다. Ceylon에서 컴파일러는 대상 초기화 논리에 매우 엄격한 제한을 추가하여 대상의 모든 영역에 한 번만 값을 부여할 수 있도록 한다.
이상은 본문의 전체 내용입니다. 여러분의 학습에 도움이 되고 저희를 많이 응원해 주십시오.

좋은 웹페이지 즐겨찾기