조건 분기가 중첩되어 낮아지는 가독성 개선하는 방법
- 조기 리턴으로 중첩 제거하기
- 가독성을 낮추는 else 구문도 조기 리턴으로 해결하기
switch 조건문
switch 조건문 중복이 위험한 이유
- 같은 형태의 switch 조건문이 여러 개 사용될 수 잇다.
- 요구사항 변경 시 수정(case문 추가)을 누락할 수 있다.
- switch조건문이 폭발적으로 늘어날 수 있다.
switch 조건문 중복을 해소하는 방법
단일 책임 선택의 원칙에 대해 생각해보아야 한다.
소프트웨어 시스템이 선택지를 제공해야 한다면, 그 시스템 내부의 어떤 한 모듈 만으로 모든 선택지를 파악할 수 있어야 한다.
-> 조건 식이 같은 조건 분기를 여러 번 작성하지 말고 한 번에 작성하자는 뜻이다.
먼저 switch 조건문을 하나로 묶은 후 인터페이스를 사용하면 된다.
인터페이스 적용 예시
도형의 나타내는 클래스 Rectangle과 Circle이 있다고 한다면, 면적을 구하는 area메소드가 이름이 같아도 서로 다르다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Rectangle {
private final double width;
private final double height;
double area() {
return width * height;
}
}
class Circle {
private final double radius;
double area() {
return radius * radius * Math.PI;
}
}
이러한 경우 도형(Object shape)의 area를 구할 때마다 Circle 인지, Rectangle인지 instanceof를 체크해야되는 분기조건이 필요할 수 있다.
이럴 때 도형이라는 interface를 만들어주면 Circle과 Rectangle같은 다른 자료형을 같은 자료형처럼 사용할 수 있게 된다.
1
2
3
interface Shape {
double area();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Rectangle implements Shape {
private final double width;
private final double height;
double area() {
return width * height;
}
}
class Circle implements Shape {
private final double radius;
double area() {
return radius * radius * Math.PI;
}
}
전략패턴으로 인터페이스를 switch 조건문 중복에 응용하기
- 종류별로 다르게 처리해야 하는 기능을 인터페이스의 메서드로 정의하기
- 인터페이스의 이름을 결정하는 방법 : ‘어떤 부류에 속하는지’ 생각해 볼 것
- 종류별로 클래스를 만들고 각각의 클래스에 인터페이스를 구현할 것
- Switch 조건문에 의존하지 않고 다른 형태로 전환하기 어려울 때, Map으로 변경해보기. type을 Enum으로 지정하고 값을 인터페이스 구현 클래스의 인스턴스로 지정해보자.
- 메서드를 구현하지 않으면 오류로 인식하게 만들기
Map이 switch 조건문처럼 경우에 따라 처리를 구분하는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
final Map<MagicType, Magic> magics = new HashMap<>();
final Fire fire = new Fire(member);
final Lightning lightning = new Lightning(member);
final HellFire hellfire = new HellFire(member);
magics.put(MagicType.fire, fire);
magics.put(MagicType.lightning, lightning);
magics.put(MagicType.hellFire, hellFire);
void magicAttack(final MagicType magicType) {
final Magic usingMagic = magics.get(magicType);
showMagicName(usingMagic);
consumeMagicPoint(usingMagic);
consumeTechnicalPoing(usingMagic);
magicDamage(usingMagic);
}
void showMagicName(final Magic magic) {
final String name = magic.name();
//생략
}
void comsumeMagicPoint(final Magic magic) {
final int costMagicPoint = magic.costMagicPoint();
//생략
}
void consumeTechnicalPoint(final Magic magic) {
final int costTechnicalPoint = magic.costTechnicalPoint();
//생략
}
void magicDamage(final Magic magic) {
final int attackPower = magic.attackPower();
//생략
}
magics.get(magicType)
으로 모두 한꺼번에 전환. 처리별로 switch 문을 사용하는 분기 자체를 하지 않음- 전략 패턴: 인터페이스를 사용해서 처리를 한꺼번에 전환하는 설계