5.1 리스트 추상화1 - 인터페이스 도입

다형성과 OCP 원칙을 가장 잘 활용할 수 있는 곳 중에 하나가 바로 자료 구조이다.

List 자료 구조

순서가 있고, 중복을 허용하는 자료 구조를 리스트(List)라 한다.

직접 구현한 MyArrayList, MyLinkedList 는 내부 구현만 다를 뿐 같은 기능을 제공하는 리스트이다. 물론 내부 구현이 다르기 때문에 상황에 따라 성능은 달라질 수 있다. 핵심은 사용자 입장에서 보면 같은 기능을 제공한다는 것이다.

이 둘의 공통 기능을 인터페이스로 뽑아서 추상화하면 다형성을 활용한 다양한 이득을 얻을 수 있다.

5.2 리스트 추상화2 - 의존관계 주입

컬렉션 프레임워크 - ArrayList컬렉션 프레임워크 - LinkedList의 글에서는 데이터를 앞에서 추가하거나 삭제할 때 MyArrayList 보다 MyLinkedList 가 O(1)로 더 좋은 성능을 낸다고 하였다.

만약 리스트의 앞에 반복적으로 데이터를 추가하는 코드가 MyArrayList 를 사용하고 있다고 가정해보자.

public class BatchProcessor {
	private final MyArrayList<Integer> list = new MyArrayList<>(); //코드 변경
 	public void logic(int size) {
 		for (int i = 0; i < size; i++) {
 			list.add(0, i); //앞에 추가
 		}
 	}
}

위의 코드는 리스트의 앞에 반복적으로 데이터를 추가하므로 MyLinkedList 를 사용하는 것이 효율적이므로 코드를 변경해야 한다. 이 경우 리스트를 사용하는 코드의 주체(클라이언트)인 BatchProcessor 의 내부 코드도 변경해야 한다.

이러한 경우를 BatchProcessor는 구체적인 클래스에 의존한다고 표현한다. 이렇게 직접적인 클래스에 직접 의존하면 MyArrayList를 MyLinkedList로 변경할 때 마다 BatchProcessor(클라이언트)의 코드를 변경해야 한다.

OCP 원칙을 지키기 위해 BatchProcessor 가 구체적인 클래스에 의존하는 대신 추상적인 MyList 인터페이스를 의존하게 한다.

public class BatchProcessor {
	private final MyList<Integer> list;

    public BatchProcessor(MyList<Integer> list) {
 		this.list = list;
	}

 	public void logic(int size) {
 		for (int i = 0; i < size; i++) {
 			list.add(0, i); //앞에 추가
		}
 	}
}
main() {
	new BatchProcessor(new MyArrayList()); //MyArrayList를 사용하고 싶을 때
 	new BatchProcessor(new MyLinkedList()); //MyLinkedList를 사용하고 싶을 때
}

위의 코드 변경을 통해 MyList 를 사용하는 클라이언트 코드인 BatchProcessor 를 전혀 변경하지 않고, 원하는 리스트 전략을 런타임에 지정할 수 있다.

의존관계 주입

public BatchProcessor(MyList<Integer> list) {
	this.list = list;
}