09. 리팩터링, 테스팅, 디버깅

주요 내용
  • 람다 표현식으로 코드 리팩터링
  • 람다 표현식이 객체지향 설계 패턴에 미치는 영향
  • 람다 표현식 테스팅
  • 람다 표현식과 스트림 API 사용 코드 디버깅

람다 표현식으로 코드 리팩터링#

익명 클래스 -> 람다 표현식#

사용 예
// 익명 클래스
Runnable r1 = new Runnable() {
public void run() {
System.out.println("...");
}
};
// 람다
Runnable r2 = () => System.out.println("...");
  • 익명 클래스와 람다 표현식 차이
    • this, super 스코프 차이
    • 익명 클래스는 shadow variable 을 가질 수 있음
    • 익명 -> 람다 시, 콘텍스트 오버로딩에 따른 모호함이 초래될 수 있음
콘텍스트 오버로딩에 따른 모호함
public static void doSomething(Runnable r) { r.run(); }
public static void doSomething(Task t) { t.execute(); }
doSomething(() -> System.out.println("...")) // ?
doSomething((Task) () -> System.out.println("...")) // 캐스팅으로 모호함 해결

람다 표현식 -> 메서드 참조#

// 람다
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel =
menu.stream()
.collect(
groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
})
)
// 메서드 참조로 리팩터링
public class Dish {
...
public CaloricLevel getCaloricLevel() {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
}
}
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel =
menu.stream().collect(groupingBy(Dish::getCaloricLevel));

명령형 데이터 처리 -> 스트림#

모든 반복자를 스트림으로 변환하는 것을 권장

// 반복자
List<String> dishNames = new ArrayList<>();
for (Dish dish : menu) {
if (dish.getCalories() > 300) {
dishNames.add(dish.getName());
}
}
// 스트림
menu.stream()
.filter(dish -> dish.getCalories() > 300)
.map(Dish::getName)
.collect(toList());

코드 유연성 개선#

  • 조건부 연기 실행 (conditional deffered excecution)
    • 로깅 예시로 public void log(Level level, Supplier<String> msgSupplier) 로 감싸서 수행
  • 실행 어라운드 패턴 (execute around)
    • 매번 같은 준비, 종료 과정을 람다로 바꿀 수 있음
try-with-resources 를 활용한 실행 어라운드 패턴
String oneLine = processFile((BufferedReader b) -> b.readLine());
String twoLine = processFile((BufferedReader b) -> b.readLine() + b.readLine());
public static String processFile(BufferedReaderProcessor p) throws IOExceptiion {
try (BufferedReader br = new BufferedReader(
new FileReader("ModernJavaInAction/chap9/data.txt"))) {
return p.process(br);
}
}
public interface BufferedReaderProcessor {
String process(BufferedReader b) throws IOException;
}

람다 표현식이 객체지향 설계 패턴에 미치는 영향#

전략 패턴#

Strategy

  • 한 유형의 알고리즘을 보유한 상태에서 런타임에 적절한 알고리즘을 선택하는 기법
  • 구성
    • 알고리즘 나타내는 Strategy Interface
    • 알고리즘의 구현체
    • 전략 알고리즘 객체 사용하는 클라이언트
  • 여기에 Strategy Interface 구현체를 람다로 넘길 수 있음

템플릿 메서드 패턴#

  • 해당 알고리즘을 사용하고 싶은데 그대로는 안되고 조금 고쳐야하는 상황
// 추상 클래스
abstract class OnlineBanking {
public void processCustomer(int id) {
Customer c = Database.getCustomerWithId(id);
makeCustomerHappy(c);
}
abstract void makeCustomerHappy(Customer c);
}
// 람다로 개선
class OnlineBanking {
public void processCustomer(int id, Cunsumer<Customer> makeCustomerHappy) {
Customer c = Database.getCustomerWithId(id);
makeCustomerHappy.accept(c);
}
}

옵저버 패턴#

  • 어떤 이벤트가 발생 했을 때, Subject 객체가 여러 Observer 객체에게 notify 해야하는 상황
  • 각 옵저버의 구현체를 람다로 구현할 수 있음
  • 하지만 옵저버가 상태들을 가지고 있다면 람다로 표현하는 건 적절치 않음 (그 때 판단해서 가져가자)

의무 체인 패턴#

chain of responseibility

  • 한 객체가 어떤 작업을 처리한 다음, 다음 객체에 결과를 전달하며 처리하는 방식
// 작업 체인 코드
ProcessingObject<String> p1 = new HeaderTextProcessing();
ProcessingObject<String> p2 = new SpellCheckerProcessing();
p1.setSuccessor(p2);
String result = p1.handle("...");
System.out.println(result);
// 람다 활용
UnaryOperator<String> headerProcessing =
(String text) -> "somthing text: " + text;
UnaryOperator<String> spellChekcerProcessing =
(String text) -> text.replaceAll("labda", "lambda");
Function<String, String> pipeline =
headerProcessing.andThen(spellChekcerProcessing);
String result = pipeline.apply("abcdef labda lambda");

팩토리 패턴#

  • 인스턴스화 로직을 클라에 노출하지 않고 객체를 만들 때 사용
final static Map<String, Supplier<Product>> map = new HashMap<>();
static {
map.put("loan", Loan::new);
map.put("stock", Stock::new);
}

람다 표현식 테스팅#

  • 람다 개별 로직을 테스팅하지말고, 람다를 사용하는 메서드를 테스팅
  • 복잡한 람다는 개별 메서드로 분할

람다 표현식과 스트림 API 사용 코드 디버깅#

  • 스택 트레이스와 .peek 를 사용한 로깅으로 확인
Last updated on