앞서 알아본 싱글턴, 팩토리, 추상팩토리, 빌더, 프로토타입 패턴은 객체의 생성과 관련된 생성 패턴(Create Pattern)이었습니다. 이번 패턴부터는 객체의 구조와 관련된 구조 패턴(Structural Pattern)을 알아보겠습니다.
구조 패턴은 상속(Inheritance)과 합성(Composition)을 통해 작은 클래스를 큰 클래스로 생성하는 방법을 제공하는 패턴입니다.
1. 어댑터 패턴(Adapter Pattern)
어댑터 패턴은 호환성 없는 서로 다른 인터페이스를 함께 사용할 수 있도록 하는 패턴입니다. 우리나라에서는 220V의 전압을 사용하지만 다른 나라에서는 110V를 사용하는 곳이 있으며 콘센트 규격도 다릅니다. 이때 우리는 어댑터를 통해 규격이 다른 콘센트일지라도 전기를 사용할 수 있습니다. 어댑터 패턴도 이와 같습니다. 서로 다른 인터페이스를 어댑터를 통해 빠르게 전환할 수 있습니다. 또한 호환성이 없어서 함께 동작할 수 없는 클래스들이 함께 작동하도록 해줍니다.
예제를 통해 220V 전압을 110V 전압으로 전환해 보겠습니다.
Socket은 220V 전압을 가지지만 SocketAdapter에 의해서 110V로 전환할 수 있습니다.
Volt.java: 볼트(V)를 나타내는 클래스입니다.
Socket.java: 소켓 클래스 입니다. 해당 소켓은 220V의 전압을 사용합니다.
SocketAdapter.java: 어댑터의 인터페이스 입니다.
어댑터 패턴을 구현하기 위해 사용하는 방법에는 클래스 어댑터(Class Adapter)와 객체 어댑터(Object Adapter) 2가지가 있습니다.
클래스 어댑터는 다음과 같습니다.
SocketClassAdapter.java: 소켓 클래스와 어댑터 인터페이스를 상속받은 소켓 어댑터 입니다. 이 어댑터는 220V의 전압을 110V의 전압으로 전환할 수 있습니다.get110Vot 메소드와 get220Volt 메소드는 SocketAdapter 인터페이스를 오버라이딩하여 구현하고, getVot 메소드는 Socket 클래스를 상속받아 사용하는 메소드입니다.
AdapterPattern.java:
Socket은 220V의 전압을 가지지만 SocketAdapter에 의하여 110V를 출력할 수 있게 되었습니다! 클래스 어댑터의 경우 상속을 활용하기때문에 유연하지는 못하지만 어댑터 코드를 다시 구현할 필요가 없습니다.
아래는 객체 어댑터 방식으로 어댑터를 구현한 예입니다.
SocketObjectAdapter.java: 객체 어댑터의 경우 Socket 클래스를 상속받지 않고, 어댑터 내부에서 Socket 객체를 만들어서 볼트를 전환시켜 줍니다. 합성(Composition)을 사용하여 유연하지만 어탭터를 코드의 대부분을 구현해야합니다.
AdapterPattern.java:
다른 예제를 작성해 보겠습니다.
Person.java: Person 클래스로 이름과 전화번호를 속성으로 가집니다.
SendMessage.java: 메시지 전송 클래스 입니다. 객체 생성시 Person 객체가 주어지면 해당 객체의 전화번호와 이름을 통해 메시지를 전송합니다.
PushAdapter.java: 메시지 전송 어댑터 입니다. PushMessage를 통해 한사람에게 메시지를 전송할 뿐만 아니라 리스트로 작성된 사람들에게 단체로 메시지를 전송할 수 있는 어댑터입니다.
PushAdapterImpl.java: PushAdapter를 상속받아 어댑터를 구현한 클래스입니다. 클래스 어댑터 방식으로 SendMessage를 상속받아 어댑터를 만들었습니다.
Main.java: SendMasage 클래스는 객체 생성시 Person 자료형만 매개변수로 받을 수 있지만 어댑터를 통해 ArrayList 자료형도 매개변수로 받을 수 있게 되었습니다. 그러므로 어댑터를 통해 단체문자를 보낼 수 있습니다.
(2) 컴포지트 패턴(Composite Pattern)
컴포지트 패턴은 단일 객체와 집합 객체를 동일한 타입으로 취급하는 패턴입니다. 단일 객체는 집합 객체를 부모로 하는 트리구조로 구성됩니다.
Leaf 객체는 단일 객체를, Composite 객체는 집합 객체를 나타내는데 Leaf 객체와 Composite 객체는 동일한 타입의 인터페이스로 되어있습니다. 예를 들면 윈도우에서 하나의 폴더는 폴더와 응용 파일로 구성될 수 있으며, 폴더와 응용 파일은 동일하게 파일로 취급되어 생성, 삭제, 이름 변경 등의 처리가 가능합니다.
클래스 다이어그램을 통해 다음과 같이 나타낼 수 있습니다.
Component는 Leaf와 Composite가 구현해야 할 인터페이스입니다. Leaf와 Composite는 동일하게 Component 타입이 됩니다.
Leaf는 단일 객체입니다. Composite의 자식으로 집합(Aggregation) 관계가 됩니다.
Composite는 집합 객체입니다. Leaf의 부모로 클라이언트는 Composite를 통해 Leaf와 Composite를 제어할 수 있습니다.
예제코드를 작성해보겠습니다.
File.java: Component에 해당하는 파일 인터페이스 입니다.
ApplicationFile.java: Leaf에 해당하는 응용 파일입니다.
Folder.java: Composite에 해당하는 폴더입니다. 빌더 패턴을 살짝 넣어봤습니다.
CompositePattern.java: 클라이언트 입니다. 파일을 생성, 삭제해보고 파일 목록을 출력하도록 합니다.
|