다음의 예시처럼 특정 숫자가 정해진 숫자 범위 안의 숫자에 해당하는지 확인하는 조건문이 있다.
if (age >= 10 && age <= 20) {
...
}
위의 로직을 공통으로 분리하여 범위를 구하는 util class
을 만들어 보겠다.
로직 분리하기
age >= 10 && age <= 20
해당 조건을 어떻게 코드를 분리해낼 수 있을까?
먼저 ‘범위'라는 객체를 생성해볼 수 있을 것 같다.
조건문 속 ‘범위'에 해당하는 부분을 분리해낼 수 있다.
10과 20은 범위에 해당하므로 ‘범위'라는 객체와 시작과 끝을 의미하는 start, end 로 구성해볼 수 있다.
class Range {
private int start; // 범위의 시작
private int end; // 범위의 끝
public Range(int start, int end) {
this.start = start;
this.end = end;
}
}
범위를 비교하는 로직은 어떻게 구현하면 좋을까?
‘범위' 라는 객체에게 ‘범위 안의 숫자인지’ 물어보면 좋을 것 같다.
그럼 다음과 같이 ‘Range’ 객체에게 물어볼 수 있다.
class Range {
...중략...
public boolean contain(int target) { // 범위 확인 메소드 생성
return start <= target && target <= end;
}
}
정적 팩토리 메소드를 적용하여 객체를 생성하는 로직까지 분리해보았다.
// 생성자를 private 으로 변경하여 객체 내 에서만 호출할 수 있도록 변경
private Range(int start, int end) {
...중략...
}
// 외부에서 호출될 객체생성을 위한 정적 팩토리 메소드
public static Range of(int start, int end) {
return new Range(start, end);
}
그럼 처음에 숫자를 비교했던 ‘age >= 10 && age <= 20’ 이 코드는 다음과 같이 변경된다.
if (Range.of(10, 20).contain(age)) {
...
}
여러 숫자 타입을 허용하도록 변경해보기
int 타입의 범위 확인이 가능한 ‘Range’를 활용하여 double타입의 범위도 확인하고 싶다.
어떻게하면 ‘Range’ 객체가 여러 숫자 타입을 허용할 수 있을까?
Number 추상 클래스와 제네릭 활용하기
모든 숫자 자료형은 Number 추상 클래스를 상속하고 있다.
따라서 다음과 같이 ‘Number’를 상속하는 클래스만 Range으로 확인할 수 있도록 적용할 수 있다.
public class Range<N extends Number> {
...중략...
}
그리고 객체 내 범위를 의미하는 start 와 end 그리고 확인할 숫자인 target 또한 타입을 특정하지 않고 Number의 구현체로 허용하기 위해 제네릭으로 바꾸어줄 수 있다.
그럼 코드는 다음과 같아진다.
public class Range<N extends Number> {
private final N start;
private final N end;
private Range(N start, N end) {
this.start = start;
this.end = end;
}
public static <N extends Number> Range<?> of(N start, N end) {
return new Range<>(start, end);
}
...중략...
}
그럼 Number를 상속받는 제네릭 값으로 범위 비교를 어떻게 할 수 있을까?
Number 추상 클래스로 접근할 수 있기 때문에 Number의 메소드를 확인해본다.
doubleValue() 추상 메소드는 모든 구현체들이 오버라이딩하여 메소드 구현을 해야하기 때문에, 해당 메소드를 활용하여 모든 타입을 double로 변경하여 범위 비교를 할 수 있다.
public <N extends Number> boolean contain(N target) {
return start.doubleValue() <= target.doubleValue()
&& target.doubleValue() <= end.doubleValue();
}
모든 숫자 타입을 받아 범위를 비교할 수 있는 Range util 클래스를 만들어 보았다.
전체 코드
public class Range<N extends Number> {
private final N start;
private final N end;
private Range(N start, N end) {
this.start = start;
this.end = end;
}
public static <N extends Number> Range<?> of(N start, N end) {
return new Range<>(start, end);
}
public <N extends Number> boolean contain(N target) {
return start.doubleValue() <= target.doubleValue()
&& target.doubleValue() <= end.doubleValue();
}
}
'공부 > Java' 카테고리의 다른 글
가변객체를 단계적으로 불변객체로 만들어 보자 (0) | 2022.11.03 |
---|---|
[cache] LRU 알고리즘 with LinkedHashMap (0) | 2022.08.22 |
왜 람다에서 지역변수를 변경할 수 없을까? (0) | 2022.01.16 |