8장 경계

Seunghee Ryu·2023년 12월 11일
0

클린 코드

목록 보기
8/18

  • 소프트웨어 경계를 깔끔하게 처리하는 기법을 알 수 있다

1. 외부 코드 사용하기

  • 인터페이스 제공자는 적용성을 최대한 넓히려 한다
  • 인터페이스 사용자는 자신의 요구에 집중하는 인터페이스를 바란다
  • 이러한 양방향의 차이로 인해 시스템 경계에서 문제가 생길 수 있다

Map

  • 외부 라이브러리 중 컬렉션이 있고 이중 자주 쓰이는 것으로 java.util.Map이 있다
  • 이 맵은 api로 clear, containsKey, entrySet 등의 다양하고 많은 함수를 제공한다
  • Map 인터페이스가 변할 경우 해당 인터페이스를 사용해서 만든 인스턴스가 많다면 수정할 코드가 많아진다
  • 일급 콜렉션을 사용한다면 Map을 콜렉션으로 숨기면서 불변성도 보장할 수 있고 해당 일급 콜렉션만 수정하면 되기에 변경의 범위가 축소된다
  • 즉 Map 인스턴스를 공개 API의 인수로 넘기거나 반환값으로 사용하지 않는다

경계 살피고 익히기

  • 간단한 테스트 케이스(학습 테스트)를 작성해서 외부 코드를 익힌다
  • 프로그램에서 사용하려는 방식대로 외부 API를 호출한다
  • 통제된 환경에서 API를 제대로 이해하는지 확인하며 API를 사용하려는 목적을 명확하게 인지한다

// first test case
// Appender가 필요하다는 에러 발생
@Test
public void testLogCreate(){
	Logger logger = Logger.getLogger("MyLogger");
	logger.info("hello");
}

// second test case
// ConsoleAppender라는 클래스가 있다는 사실 확인
// Appender에 출력 스트림이 없다는 에러 발생
@Test
public void testLogCreate(){
	Logger logger = Logger.getLogger("MyLogger");
	ConsoleAppender appender = new ConsoleAppender();
	logger.addAppender(appender);
	logger.info("hello");
}

// third test case
// 정상 작동
@Test
public void testLogCreate(){
	Logger logger = Logger.getLogger("MyLogger");
	logger.removeAllAppenders();
	logger.addAppender(new ConsoleAppender(
				new PatternLayout("%p %t %m%n"),
				ConsoleAppender.SYSTEM_OUT));
	logger.info("hello");
}

//최종
public class LogTest {
	private Logger logger;
	
	@Before
	public void setup() {
		logger = Logger.getLogger("logger");
		logger.removeAllAppenders();
		Logger.getRootLogger().removeAllAppenders();
	}

	@Test
	public void basicLogger(){
		BasicConfigurator.configure();
		logger.info("basicLogger");
	}

	@Test
	public void addAppenderWithStream() {
		logger.addAppender(new ConsoleAppender(
				new PatternLayout("%p %t %m%n"),
				ConsoleAppender.SYSTEM_OUT));
		logger.info("addAppenderWithStream");
	}
	
	@Test
	public void addAppenderWithoutStream() {
		logger.addAppender(new ConsoleAppender(
				new PatternLayout("%p %t %m%n")));
		logger.info("addAppenderWithoutStream");
	}
}
  • 학습 테스트를 함으로써 API에 대한 이해도를 높이고 새로운 버전으로 업데이트 되더라도 기존에 작성한 학습 테스트를 통해 호환성 체크도 할 수 있고 그에 따른 적용도 가능하다

3. 아직 존재하지 않는 코드를 사용하기

  • 경계의 또 다른 유형은 아는 코드와 모르는 코드를 분리하는 것이다
  • 협업을 하다보면 아직 다른 팀에서 완성하지 못한 API를 사용해야 하는 경우가 있다

  • 다른 팀에서 아직 API를 제작하지 못했다면 해당 API에서 인터페이스를 추출해 Controller에서 분리한 뒤 Fake 구현체를 생성해서 사용하며, API가 제작이 완료되면 Adapter를 이용해 간극을 메워준다
  • 이렇게 Fake 구현체 클래스를 사용하면 controller도 테스트 할 수 있다

깨끗한 경계

  • 외부 패키지를 호출하는 코드를 줄여 경계를 관리하며 새로운 클래스나 Adapter patter을 이용한다
  • 외부 패키지의 수정이나 변경에 대응 범위가 작아지고 경계를 분명히 해 코드 가독성이 높아진다

개인적인 감상

  • 가장 처음 맡았던 프로젝트에서 외부 api를 사용했었는데 그때 adater 패턴에 대해 알았다면 조금 더 깔끔하게 만들 수 있었지 않았을까 싶다
  • 테스트 케이스를 만들어 본 적이 없기 때문에 외부 api를 호출할 때 학습 테스트를 만든다는 것을 처음 알았다
  • 일급 컬렉션이라는 개념이 제대로 정립되지 않아서 추가적인 검색이 필요하다

일급 컬렉션

  • Collection을 Wrapping하면서, Wrapping한 Collection 외 다른 멤버 변수가 없는 상태를 일급 컬렉션이라 한다

// warpping 전
public class Person {
    private String name;
    private List<Car> cars;
    // ...
}

public class Car {
    private String name;
    private String oil;
    // ...
}


// wrapping 후
public class Person {
    private String name;
    private Cars cars;
    // ...
}

// List<Car> cars를 Wrapping
// 일급 컬렉션
public class Cars {
    // 멤버변수가 하나 밖에 없다!!
    private List<Car> cars;
    // ...
}

public class Car {
    private String name;
    private String oil;
    // ...
}
  • 일급 컬렉션은 List cars 외 다른 멤버 변수가 없다
  • 일급 컬렉션을 사용하면 상태과 로직을 따로 관리할 수 있기 때문에 로직이 사용되는 클래스의 부담을 줄일 수 있고, 중복코드를 줄일 수 있다

0개의 댓글