본문 바로가기
Book

[독서 기록] 모던 자바 인 액션 9장 리팩터링, 테스팅, 디버깅

by Renechoi 2023. 1. 10.

모던 자바 인 액션 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

 

 

 

반응형