구조에 따른 패턴 분류
적용 범위 Scope | 클래스 class - 클래스 간 관계 - 주로 inheritance 사용 - 정적) 컴파일 시 결정됨 |
Adapter (class) (상속) |
객체 object - 객체 간 관계 - 주로 composition 사용 - 동적) 실행 시 변경 가능 |
Adapter (object) (구성) Facade (퍼사드) Composite Decorator Proxy Bridge Flyweight |
인터페이스 관련 패턴
• Adapter pattern
• Facade pattern
• Composite pattern
• Bridge pattern
※ 모두 구조 패턴에 속함
Adapter 구조 패턴
언제 ?
-> Interface mismatching
client 가 요구하는 인터페이스와 제공된 클래스의 인터페이스가 일치하지 않을 때
예시)
Client calls methodA(), Library class has methodB()
: Client는 methodA를 원하는데 라이브러리 클래스의 메소드 명은 methodB
Key Idea : 인터페이스 변환 - Adaptee 의 interface를 Client가 원하는 인터페이스로 변환
Client | Adapter | Adaptee |
cals methodA() | methodA(){ adaptee.methodB(); } |
methodB(){ // ...\ } |
Adapter 에 Client가 원하는 메소드를 가지고 있고, 호출 시 Adaptee의 메소드를 호출한다.
적용 방법
1. Object Adapter pattern (구성)
2. Class Adapter pattern (다중 상속)
Object Adapter pattern
장점) Composition 을 사용하여 adaptee에게 위임하기 때문에 flexible 하다.
단점) Adaptee 객체 만들어야 사용가능
adapter에 행동 추가 時 모든 subclass에게도 적용됨.
subclass에 adapt 가능
Class Adapter pattern
장점) Adaptee 객체를 만들지 않아도 된다. I don’t have to reimplement my entire adaptee.
단점) 상속 때문에 한 Adapter 클래스가 특정 Adaptee 클래스에만 적용 가능
Adaptee의 subclass는 adapt 하지 못함.
Object Adapter pattern using Composition(구성)
specificRequest() 는 Request 의 구현
위임
인스턴스 불러와서 대신 시킴
Target <interface> : defines the interface that Client uses.
Adaptee : defines an existing interface that needs adpating.
Adapter : converts the adaptee's interface into the target interface.
So, Adapter hides Adaptee's interface from Client
예제 코드
Client | Target Interface | Adaptee |
class DuckTestDrive // 제공된 클래스가 오리가 아니고 칠면조 Turkey 칠면조 = new WildTrukey(); // 오리(adapter)를 만들어야지. //Adapter wraps Adaptee. Duck 오리 = new RedDuck(칠면조); 어댑터 ( 어댑티 ); //calls 오리. 오리.quack(); 오리.fly(); |
public interface Duck{ public void quack(); public void fly(); } |
public interface Turkey{ public void gobble(); public void fly(); } public class WildTurkey implements Turkey { public void gobble(){ ... } public void fly() {.... } } |
Adapter | public class RedDuck implements Duck { //TurkeyAdapter Turkey 칠면조; //COMPOSITION: 파라미터로 받아서 인스턴스 객체 내부에 저장 public RedDuck(Turkey 칠면) { 칠면조 = 칠면; } // of adaptee public void quack() { 칠면조.gobble(); } //DELEGATION public void fly() { 칠면조.fly(); 칠면조.fly(); } // to adaptee } // ★ 컴포지션을 이용해서 위임을 한다. |
// * TARGET Interface
public interface Duck
{
public void quack();
public void fly();
}
public class MallardDuck implements Duck {
public void quack() {
System.out.println("MallardDuck Quack");
}
public void fly() {
System.out.println("I'm flying");
}
}
// * Adapter
public class TurkeyAdapter implements Duck {
Turkey turkey; // Composition : 파라미터로 받아서 인스턴스 객체를 클래스 내부에 저장
public TurkeyAdapter(Turkey turkey) { //of adaptee
this.turkey = turkey;
}
public void quack() {
turkey.gobble(); // Deletegation : Turkey가 대신 수행
}
public void fly() { // to adaptee
for(int i=0; i < 5; i++) {
turkey.fly();
}
}
}
//*** Composition을 통해서 위임을 한다.
// * Adaptee
public interface Turkey
{
public void gobble();
public void fly();
}
public class WildTurkey implements Turkey
{
public void gobble() {
System.out.println("WildTurkey Gobble gobble");
}
public void fly() {
System.out.println("I'm flying a short distance");
}
}
public class DuckAdapter implements Turkey
{
Duck duck;
Random rand;
public DuckAdapter(Duck duck) {
this.duck = duck;
rand = new Random();
}
public void gobble() {
duck.quack();
}
public void fly() {
if (rand.nextInt(5) == 0) {
duck.fly();
}
}
}
public class DuckTestDrive {
public static void main(String[] args) {
//제공된 클래스가 오리가 아니고 칠면조
Turkey turkey = new WildTurkey();
Duck duck = new MallardDuck();
// 오리 어댑터를 생성
Duck turkeyAdapter = new TurkeyAdapter(turkey);
System.out.println("The Turkey says...");
turkey.gobble();
turkey.fly();
System.out.println("\nThe Duck says...");
testDuck(duck);
// calls 오리 (Duck)
System.out.println("\nThe TurkeyAdapter says...");
testDuck(turkeyAdapter);
}
static void testDuck(Duck duck) {
duck.quack();
duck.fly();
}
}
<실행 결과>
The Turkey says...
WildTurkey Gobble gobble
I'm flying a short distance
The Duck says...
MallardDuck Quack
I'm flying
The TurkeyAdapter says...
WildTurkey Gobble gobble
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
Process finished with exit code 0
Class Adapter Pattern using Multiple Inheritance.
(C++) class Adapter : public TargetClass, private Adaptee (타겟의 Request 만 사용하게 하기 위해 / client가 Specificrequest 사용 불가 하게)
- Client에게 공개할 Target 클래스는 public 상속,
- Adapter 내부에서만 사용할 Adaptee는 private 상속.
class Adapter extends Adaptee implements Target
interface 를 통해 다중 상속의 효과를 얻을 수 있다.
public class Adaptee
{
public void specificRequest(List list){
System.out.println("specificRequest");
for(Object o : list) System.out.println(o);
}
}
public class Adapter extends Adaptee implements Target
{
@Override
public void request(List list) {
specificRequest(list);
}
}
public class Client {
public static void main (String args[])
{
Adapter adapter = new Adapter();
adapter.request(Arrays.asList(args));
}
}
Old world Enumerators
New world Iterators
Writing the EnumerationIterator adapter
new world iterators
When Sun released their more recent
Collections classes they began using an
Iterator interface that, like Enumeration,
allows you to iterate through a set of items
in a collection, but also adds the ability to
remove items.
EnumerationIterator : Adapter
Iterator : Target interface
Enumeration : Adaptee interface
/*
Since we’re adapting Enumeration
to Iterator, our Adapter
implements the Iterator interface...
it has to look like an Iterator.
*/
public class EnumerationIterator implements Iterator
{
Enumeration enumeration;
//The Enumeration we’re adapting.
//We’re using composition so we stash(넣어 두다 숨기다)
//it in an instance variable
public EnumerationIterator(Enumeration enumeration) { // 구성
this.enumeration = enumeration;
}
//The Iterator’s hasNext() method
//is delegated to the Enumeration’s
//hasMoreElements() method...
public boolean hasNext() {
return enumeration.hasMoreElements(); //위임
}
//... and the Iterator’s next() method
//is delegated to the Enumerations’s
//nextElement() method.
public Object next() {
return enumeration.nextElement();
}
//Unfortunately, we can’t support
//Iterator’s remove() method, so
//we have to punt (in other words,
//we give up!). Here we just throw
//an exception.
public void remove() {
throw new UnsupportedOperationException();
}
}
적용
public class Client {
public static void main (String args[]) {
Vector v = new Vector(Arrays.asList(args));
Iterator iterator = new EnumerationIterator(v.elements());
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
- 국내 가전제품을 외국에서 사용한다고 할 때, 동일한 전압을 사용하지 않는다면 중간에 변환 코드가 필요하다. 이러한 변환 코드를 어댑터 구조 패턴이라고 한다.
- 인터페이스로 인해 함께 사용하지 못하는 클래스를 함께 사용하도록 하는 패턴
- 호환성이 없는 인터페이스 때문에 함께 동작할 수 없는 클래스들을 함께 동작하도록 해준다.
댓글