3장 입츨굼 내역 분석기 확장판 ( 전반부 )
2024. 10. 9. 21:57ㆍ스터디/실전 자바 소프트웨어개발
목표
- 코드베이스에 유연성을 추가하고 유지보수성을 개선하는 데 도움을 주는 개방/폐쇄 원칙(OCP) 을 배워보자
- 언제 인터페이스를 사용해야 좋을지를 설명하는 일반적인 가이드라인과 높은 결합도를 피할 수 있는 상황도 배워보자
예제
// 특정금액 이하 입출금내역 조회하기
public List<BankTransaction> findTransactionsGreaterThanEqual(final int amount) {
final List<BankTransaction> result = new ArrayList<>();
for(final BankTransaction bt: bankTansactions) {
if(bt.getAmount() >= amount) {
result.add(bt);
}
}
return result;
}
또는
// 특정금액 또는 특정 월로 입출금 내역을 조회
public List<BankTransaction> findTransactionsInMonthAndGreater(final Month month, final int amount) {
final List<BankTransaction> result = new ArrayList<>();
for(final BankTransaction bt: bankTansactions) {
if(bt.getDate().getMonth() == month && bt.getAmount() >= amount) {
result.add(bt);
}
}
return result;
}
- 위 코드 구현 방식에는 문제가 있다.
- 거래 내역의 여러 속성을 조합할수록 코드가 점점 복잡해진다.
- 반복 로직과 비즈니스 로직이 결합되어 분리하기가 어려워진다.
- 코드를 반복한다.
해결방법
- 인터페이스를 이용해 위와 같은 결합을 제거
- BankTransactionFilter 인터페이스를 만들어 문제를 해결.
//함수형 인터페이스 @FunctionalInterface public interface BanktransactionFilter { boolean test(BankTransaction bt); }
- BankTransactionFilter 인터페이스를 만들어 문제를 해결.
// 개방/폐쇄 원칙을 적용 한 findTransaction 메서드
public List<BankTransaction> findTransactions(final BankTransactionFilter btf) {
final List<BankTransaction> result = new ArrayList<>();
for(final BankTransaction bt: bankTansactions) {
if(btf.test(bt)) {
result.add(bt);
}
}
return result;
}
함수형 인터페이스 인스턴스 만들기
class BankTransactionIsInFebruaryAndExpensive implements BankTransactionFilter {
@Override
public boolean test(final BankTransaction bt) {
return bt.getDate().getMonth() == Month.FEBRUARY
&& bt.getAmount() >= 1_000);
}
}
특정 BankTransactionFilter 구현으로 findTransactions() 호출하기
final List<BankTransaction> bts = bankStatemetnProcessor.findTransactions(new BankTransactionIsInFebruaryAndExpensive());
- 확실히 더 객체지향적이고, 코드가 깔끔해졌다.
갓 인터페이스
- 한 인터페이스에 모든 기능을 추가하는 형식
interface BankTransactionProcessor { double calculateTotalAmount(); double calculateTotalInMonth(Month month); double calculateTotalInJanuary(); List<BankTransaction> findTransactions(BankTransactionFilter btf); ... }
문제점
- 자바의 인터페이스는 모든 구현이 지켜야할 규칙을 정의한다.
즉 구현 클래스는 인터페이스에서 정의한 모든 연산의 구현코드를 제공해야함. 따라서 인터페이스를 바꾸면 이를 구현한 코드도 바뀐 내용을 지원하도록 갱신해야한다. 더 많은 연산을 추가할 수록 더 자주 코드가 바뀌며, 문제가 발생할 수 있는 범위도 넓어진다. - 월, 카테고리 같은 BankTransaction 속성이 calculateAverageForCategory(), calculateTotalInJanuary() 처럼 메서드 이름의 일부로 사용되었다.
인터페이스가 도메인 객체 특정 접근자에 종속되는 문제가 생김.
→ 도메인 객체의 세부 내용이 바뀌면 인터페이스도 바뀌어야하기 때문에 결과적으로 구현코드도 바뀌어야한다.
지나친 세밀함
그렇다면 위에서 말했듯이 갓 인터페이스가 되면 문제이니, 작을수록 좋은걸까?
interface CalculateTotalAmount {
double calculateTotalAmount();
}
interface CalculateAverage {
double calculateAverage();
}
...
- 지나치게 인터페이스가 세밀해도 코드 유지보수에 방해가 된다. 실제로 위 예제는 안티 으집도 문제가 발생한다. 더욱이 인터페이스가 너무 세밀하면 복잡도가 높아지며, 새로운 인터페이스가 계속해서 프로젝트에 추가된다.
명시적API vs 암묵적API
- 위에서 말한 BankTransactionProcessor는 다양한 구현을 기대하지 않으므로 인터페이스의 필요성이 사라진다. 결론적으로 코드베이스에 불필요한 추상화를 추가해 일을 복잡하게 만들 필요가 없다.
BankTransactionProcessor는 단순히 입출금 내역에서 통계정 연산을 수행하는 클래스일 뿐이다. - 보통 findTransactions() 메서드를 쉽게 정의할 수 있는 상황에서 findTransactionsGreaterThanEqul() 처럼 구체적으로 메서드를 정의해야 하는지 의문이 생긴다. 이러한 딜레마가 명시적 API vs 암묵적 API 이다.
- 상황에 따라 처리하는게 베스트..?
'스터디 > 실전 자바 소프트웨어개발' 카테고리의 다른 글
2장 입출금 내역 분석기 후반부 내용 (1) | 2024.10.08 |
---|---|
2장 입출금 내역 분석기 (2) | 2024.10.06 |