모던 자바 인 액션 9장 리팩터링, 테스팅, 디버깅
일반적으로 코드 가독성이 좋다는 것은 '어떤 코드를 다른 사람도 쉽게 이해할 수 있음'을 의미한다. 즉 코드 가독성을 개선한다는 것은 우리가 구현한 코드를 다른 사람이 쉽게 이해하고 유지보수할 수 있게 만드는 것을 의미한다.
- 294p
익명 클래스를 람다 표현식으로 리팩터링
Runnable r1 = new Runnable() {
public void run(){
System.out.println("hello");
}
}
Runnable r2 = () -> System.out.println("hello");
- 295p
람다 표현식을 메서드 참조로 리팩터링
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream()
.collect(
groupingBy(dish -> {
if (dish.getCalories() < 400) return CaloricLevel.DIET;
...
람다 표현식을 별도의 메서드로 추출한 다음에 groupingBy에 인수로 전달할 수 있다.
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(groupingBy(Dish::getCaloricLevel));
이제 Dish 클래스에 getCaloricLevel 메서드를 추가해야 한다.
public Dish {
...
public CaloricLevel getCaloricLevel() {
...
inventory.sort(comparing(Apple::getWeight)); <= 코드가 문제 자체를 설명한다.
- 295 ~ 297p
이론적으로 반복자를 이용한 기존의 모든 컬렉션 처리 코드를 스트림 API로 바꿔야 한다. 이유가 뭘까? 스트림 API는 데이터 처리 파이프라인의 의도를 더 명확하게 보여준다. 스트림은 5장에서 설명한 것처럼 쇼트서킷과 게으름이라는 강력한 최적화뿐 아니라 멀티코어 아키텍처를 활용할 수 있는 지름길을 제공한다.
- 298p
예를 들어 다음 명령형 코드는 두 가지 패턴(필터링과 추출)으로 엉킨 코드다. 이 코드를 접한 프로그래머는 전체 구현을 자세히 살펴본 이후에야 전체 코드의 의도를 이해할 수 있다. 게다가 이 코드를 병렬로 실행시키는 것은 매우 어렵다.
List<String> dishNames = new ArrayList<>();
for (Dish dish : menu) {
if (dishj.getCalories() > 300) {
dishNames.add(dish.getName());
}
}
스트림 API를 이용하면 문제를 더 직접적으로 기술할 수 있을 뿐 아니라 쉽게 병렬화할 수 있다.
menu.pallelStream()
.filter(d -> d.getCalories() > 300)
.map(Dish::getName)
.collect(toList());
- 298p
조건부 연기 실행
다음은 내장 자바 Logger 클래스를 사용하는 예제다.
if (logger.isLoggable(Log.FINER)) {
logger.finer("problem: " + generateDiagnostic());
}
위 코드는 다음과 같은 사항에 문제가 있다.
- logger의 상태가 isLoggable이라는 메서드에 의해 클라이언트 코드로 노출된다.
- 메시지를 로깅할 때마다 logger 객체의 상태를 매번 확인해야 할까? 이들은 코드를 어지럽힐 뿐이다.
public void log(Level level, Supplier<String> msgSupplier) {
if (logger.isLoggable(level)){
log(level, msgSupplier.get());
}
}
logger.log(Level.FINER, () -> "problem: " + generateDiagnostic()));
실행 어라운드
String oneLine = processFile ((BufferedReader b) -> b.readLine());
String twoLines = processFile((BufferedReader b) -> b.readLine() + b.readLine());
...
public interface BufferedReaderProcessor {
String process(BufferedReader b) throws IOException;
}
- 300 ~ 301p
오직 소문자 또는 숫자로 이루어져야 하는 등 텍스트 입력이 다양한 조건에 맞게 포맷 되어 있는지 검증한다고 가정하다. 먼저 String 문자열을 검증하는 인터페이스부터 구현한다.
public interface ValidationStrategy {
boolean execute(String s);
}
이번에는 위에서 정의한 인터페이스를 구현하는 클래스를 하나 이상 정의한다.
public class IsAlloLowerCase implements ValidationStrategy {
public boolean execute(String s) {
return s.matches("[a-z]+");
}
}
public class IsNumeric implements ValidationStrategy {
public boolean execute(String s) {
return s.matches("\\d+");
}
}
검증 전략으로 활용하기
public class Validator {
private final ValidationStrategy stragegy;
public Validator(ValidationStrategy v) {
this.strategy = v;
}
public boolean validate(String s) {
return stategy.execute(s);
}
Validator numericValidator = new Validator(new IsNumeric());
람도 표현식으로 사용
Validator numericValidator = new Validator( (String s) -> s.matches("[a-z]+"));
Validator lowerCaseValidator = new Validator( (String s) -> s.matches("\\d+"));
boolean b1 = numericaValidator.validate("aaaa");
boolean b2 = lowerCaseValidator.validate("bbbb");
람다를 직접 전달
- 302 ~ 304p
함수 체인으로으로 객체 연결하기
UnaryOperator<String> headerProcessing = (String text) -> "From Raoul, Mario and Alan: " + text;
UnaryOperator<String> spellCheckerProcessing = (String text) -> text.replaceAll("labda", "lambda");
Function<String, String> pipeline = headerProcessing.andThen(spellCheckerProcessing);
- 310p
다양한 상품을 만드는 Factory 클래스
public class ProductFactory {
public static Product createProduct(String name) {
switch(name) {
case "loan" : return new Loan();
case "stock" : return new Stock();
case "bond" : return new Bond();
default : throw new RuntimeException("no such");
}
}
}
product p = ProductFactory.createProduct("loan);
람다 표현식 사용
Supplier<Product> loanSupplier = Loan::new;
Loan loan = loanSupplier.get();
final static Map<String, Supplier<Product>> map = new HashMap<>();
static {
map.put("loan", Loan::new);
map.put("stock", Stock::new);
map.put("bond", bond::new);
}
public static Product createProduct(String nmae){
Supplier<Prouct> p = map.get(name);
if (p !=null) return p.get();
throw new IllegalArgumentException("no such);
}
- 311p
스트림 파이프라인에서 각 단계별 상태 확인 하기
=> peek을 이용
List<Integer> result = numbers.stream()
.peek(x-> System.out.println("from stream:" + x))
.map(x->x+17)
...
- 319p
'Book' 카테고리의 다른 글
[독서 기록] 모던 자바 인 액션 11장 null 대신 Optional 클래스 (0) | 2023.01.11 |
---|---|
[독서 기록] 모던 자바 인 액션 10장 람다를 이용한 도메인 전용 언어 (0) | 2023.01.10 |
[독서 기록] 모던 자바 인 액션 8장 컬렉션 API 개선 (0) | 2023.01.09 |
[독서 기록] 모던 자바 인 액션 7장 병렬 데이터 처리와 성능 (0) | 2023.01.09 |
[독서 기록] 모던 자바 인 액션 6장 스트림으로 데이터 수집 (0) | 2023.01.08 |