티스토리 뷰
📌 목차
1️⃣ if문 제거하기
2️⃣ getter와 setter
3️⃣ stream API
4️⃣ stream API와 Optional
1️⃣ if문 제거하기
❗ 너무 많은 if문은 코드 가독성을 해침 → 수정, 디버깅이 어려워짐
👩🏻🏫 중구난방 IF문 개선해보자!
CalculateType calculateType = calculateCommand.getCalculateType();
int num1 = calculateCommand.getNum1();
int num2 = calculateCommand.getNum2();
int result = 0;
if(calculateType != null && calculateType.equals(CalculateType.ADD)) {
result = num1 + num2;
} else if(calculateType != null && calculateType.equals(CalculateType.MINUS)) {
result = num1 - num2;
} else if(calculateType != null && calculateType.equals(CalculateType.MULTIPLY)) {
result = num1 * num2;
} else if(calculateType != null && calculateType.equals(CalculateType.DIVIDE)) {
if(num2 == 0) {
throw new RuntimeException("0으로 나눌 수 없습니다.");
} else {
result = num1 / num2;
}
}
✔️ 유효성 검사를 if문 앞으로 꺼내오기 (Early return)
✔️ IF문의 로직을 객체(Enum)안으로 숨기기
✔️ 유효성 검사(ex. null체크, 0체크)는 객체 생성 시점에 하기
🖐🏻 리팩토링?
: 결과의 변경 없이 코드의 구조를 재조정하는 것 ← 테스트 코드를 통해 결과가 변경되지 않았음을 확인할 수 있음!
: 가독성을 높이거나 유지보수를 편하게 함
2️⃣ getter와 setter
❗ getter와 setter는 클라이언트에게 너무 많은 정보를 제공해줌 (간접적으로) → 캡슐화❌
→ 생성자를 통한 간접 노출은 제한된 영역에서만 사용되므로 크게 문제 X (또는 팩터리 관련 디자인 패턴으로 해결 가능)
👩🏻🏫 getter와 setter를 없애보자!
CalculateType calculateType = calculateCommand.getCalculateType();
int num1 = calculateCommand.getNum1();
int num2 = calculateCommand.getNum2();
int result = calculateType.calculate(num1, num2);
✔️ getter을 없애자 → 클라이언트 코드에서 getter를 사용하는 부분을 메서드 안으로 숨기자
✔️ 불안전한 인스턴스가 생성되지 않도록 생성자를 활용하자 (기본 생성자를 지양)
✔️ 기술적인 문제로 getter가 필요한 경우에는
내부에서 사용되는 데이터 클래스와 외부와 접점이 있는 데이터 클래스(DTO)를 나눠서 사용하자!
✔️ getter와 setter를 제거했다면 해당 기능을 제공하는 메서드 이름을 잘 짓자!
🔎 캡슐화
: 클라이언트 코드에게 서버 코드 정보를 숨기는 것
: 클라이언트에게 field는 숨기고, public method로 "이 객체가 ~~ 기능을 제공하고 있다"를 공개
: 캡슐화 된 코드는 결합도↓ 응집도↑ (변경에 강함)
- 결합도 : 클라이언트가 객체에 지나치게 의존하고 있는 것
➖ 결합도가 높으면 코드 변경이 어렵고, 한쪽을 변경하면 다른쪽으로도 전파 - 응집도 : 관련있는 것들끼리 얼마나 모여 있는가
3️⃣ stream API 사용하기 (가독성↑)
- 기본적으로 for, if문을 대체할 수 있음 (주로 forEach, filter, map...)
- stream API는 Collection 인터페이스 내에 존재하는 메서드들 → Map은 ❌
✔️ 반복문 대체 - forEach()
List<Integer> list = new ArrayList<>();
list.stream().forEach(System.out::println); // for문을 썼다면?
➖ for문의 i처럼 index에 접근할 수 없음
➖ 도중에 break를 할 수 없음 (early return으로 continue는 가능)
→ RuntimeException을 던져서 빠져나올수도
→ filter()로 해결 가능
✔️ 조건에 맞는 요소만 뽑기 - filter()
Integer find = list.stream()
.filter(i -> (i.equals(40))? true : false)
.findAny() // 하나라도 filter()에서 걸리는 것이 있으면 반복을 멈춤!
.get();
✔️ 요소를 매핑시키기 - map()
List<Integer> x10list = list.stream()
.map(i -> i * 10)
.toList();
💁🏻♀️ Map에 stream API 사용할 수 있는 방법이 없을까?
💡 Set으로 변경 후 사용 가능
// 방법 1
Set<Map.Entry<String, Integer>> entries = map.entrySet(); // Map<String,Integer> map;
entries.stream()
.forEach(entery -> { ... }); // entriy.getKey(); entriy.getValue()로 (key, value)를 얻을 수 있음
// 방법 2
Set<String> keySet = map.keySet();
//방법 3
Collection<Integer> values = map.values();
4️⃣ stream API와 Optional
💡 Stream API를 Optional과 함께 사용하면 가독성을 더 높일 수 있음!
→ for + if문을 Stream + Optional로 변경 가능!
// for + if
Integer findNum = null;
for(int i = 0; i < list.size(); i++) {
if(list.get(i).equals(1)) {
findNum = ist.get(i);
break;
}
}
if(findNumber == null) throw new RuntimeException();
// Stream + Optional
findNum = list.stream()
.filter(i -> (i.equals(1))? true : false)
.findAny()
.orElseThrow(); // empty 옵셔널이 반환되면 예외(NoSuchElementException)를 던짐
💡 (tip) 실무에서 DB에서 데이터를 받아와서 보통 Stream + Optional을 많이 사용!
💁🏻♀️ Stream API는 for문보다 느리지 않나요?
💡 경우에 따라 다르지만, 실제로 느릴 수 있음 but 가독성을 우선하여 개발 후 성능 개선이 필요하다면 튜닝!
'데브코스 > 1주차 - 프레임워크를 위한 JAVA' 카테고리의 다른 글
Chapter06 구현된 걸 사용하게 되는 패턴 (0) | 2023.09.25 |
---|---|
Chapter05 직접 구현하게 되는 패턴 (0) | 2023.09.25 |
Chapter04 SOLID (0) | 2023.09.24 |
Chapter03 의존 (0) | 2023.09.22 |
Chapter01 객체지향 관련 문법 정리 (0) | 2023.09.20 |