티스토리 뷰

📌 목차

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();

 

📎 Stream API

 


 

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 가독성을 우선하여 개발 후 성능 개선이 필요하다면 튜닝!

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
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
글 보관함