Skip to Content
Suffering builds character
아카이브10.Java실습9. Phase 7 - 결합도 낮추기

9. Phase 7 - 결합도 낮추기

1. 새로운 파일 확장자 추가?

현재 BankStatementAnalyzer 클래스는 BankDataTSVParser 나 BankDataCSVParser 클래스에 의존하고 있음

Q. TSV, CSV 파일뿐만 아니라 JSON이나 XML 등 기존과는 다른 형식의 파일을 읽고,
파싱할 수 있도록 처리해야 할 경우?

해결 방법

→ BankStatementAnalyzer 클래스의 특정 부분의 코드를 전부 수정해야 함

2. 해결 방법에 기반한 적용 시나리오

위 해결 방법에 기반하여 적용한 코드 예시는 다음과 같음

BankStatementAnalyzer.java
private final BankDataTSVParser tsvParser = new BankDataTSVParser(); // 다른 확장자를 가진 파일을 읽어야할 때 private final BankDataCSVParser cSVParser = new BankDataCSVParser(); private final BankDataXMLParser xMLParser = new BankDataXMLParser(); private final BankDataJSONParser jsonParser = new BankDataJSONParser(); // ...

요구사항은 구현되었지만, 새로운 확장자를 가진 파일을 처리해야하는 요구사항이 추가될 때마다 BankStatementAnalyzer의 결합도는 점점 높아짐

3. 인터페이스

이런 상황에서는 interface를 활용하여 Analyzer 클래스와 특정 확장자에 대한 Parser 클래스(ex. BankDataTSVParser) 간의 직접적인 관계를 분리하여 결합도를 낮출 수 있음

클래스 A
BankStatementAnalyzer

클래스 B
BankDataTSVParser

라고 가정할 때,

클래스 A는 클래스 B의 인스턴스를 생성하고, 메서드를 호출함
두 클래스는 서로 직접적인(directly) 관계가 있다고 볼 수 있음

BankStatementAnalyzer.java
public static void main(String[] args) { BankDataTSVParser tsvParser = new BankDataTSVParser(); // 인스턴스 생성 tsvParser.parseLinesFromTSV(); // 메서드 호출 }

interface1

만약 클래스 B(BankDataTSVParser)의 메서드의 선언부가 변경되면, 클래스 A에서 해당 메서드를 호출하는 코드도 변경해야 하는 단점이 있음

BankDataTSVParser.java
// parseLinesFromTSV(); parseLinesFromTabSeperatedValue(); // 메서드 이름이 보다 구체화된 이름으로 변경됨
BankStatementAnalyzer.java
public static void main(String[] args) { BankDataTSVParser tsvParser = new BankDataTSVParser(); // 인스턴스 생성 // tsvParser.parseLinesFromTSV(); // 컴파일 에러, 메서드명 변경 필요 tsvParser.parseLinesFromTabSeperatedValue(); }

따라서 interface를 활용하여 BankStatementParser라는 BankDataTSVParser보다 이름이 상대적으로 추상화된 인터페이스를 구현할 경우,

클래스 A는 클래스 B를 직접적으로 사용하지 않고, 인터페이스를 통해 클래스 B의 메서드를 사용할 수 있음

BankDataParser.java
interface BankDataParser { // TSV와 같은 구체적인 이름을 제거, 추상화된 형태의 네이밍 parseLineFrom(); // 메서드명도 추상화 }
BankStatementAnalyzer.java
public static void main(String[] args) { // BankDataTSVParser tsvParser = new BankDataTSVParser(); // 기존 방식 BankDataParser parser = new BankDataTSVParser(); // 인터페이스 활용 방식 // → 변수의 타입이 구체화된 클래스(BankDataTSVParser)가 아닌 인터페이스의 타입(BankDataParser)으로 선언 parser.parseLinesFrom(); // BankDataTSVParser에 직접 접근하지 않음 }

interface2

→ 결과적으로 클래스 A와 B의 관계는 직접적인 관계에서 간접적인 관계로 변화되었음

클래스 A는 interface I(BankDataParser)만 이용하기 때문에 실제로 사용되는 클래스B의 이름을 알 필요가 없게됨

4. interface 적용 후 다이어그램

클래스 다이어그램에서는 BankDataTSVParser, BankDataCSVParser를 BankDataParser 인터페이스를 구현했다고 해서 구현체(Implementation)라고 함
→ 인스턴스를 생성할 수 없는 인터페이스를 인스턴스를 생성할 수 있도록 실체화(Realization) 시킴

BankDataTSVParser 뿐만 아니라 다른 구현체(BankDataCSVParser)도 BankDataParser 인터페이스를 구현

sample.java
// before parseFromTSV() parseLinesFromTSV() // ... parseFromCSV() parserFromJSON() // after parseFrom() parseLinesFrom()

TSVParser와 동일하게 입출금 내역 파일을 읽어들여 결과를 출력했고, 코드를 수정할 때는 인스턴스만 수정하면 됨
→ 조건식으로 분기할 경우, 코드 변경 없이도 동작하도록 구현 가능

BankStatementAnalyzer.java
// before // final BankDataTSVParser tsvParser = new BankDataTSVParser(); // after final BankDataParser parser = new BankDataCSVParser();
Last updated on