CS/디자인패턴

[디자인패턴] Adapter패턴(어댑터, Wrapper패턴)

mabb 2023. 5. 25. 21:53
반응형

▶디자인 패턴 ( Design Pattern)
:프로그램 개발 시 문제 해결을 위하여 빈번히 사용되는 개발자들의 경험, 내적인 축적에 대하여,
GoF(Gang of Four)라 불리는 4인의 개발자들이 각각을 패턴으로 정의하고 이름을 붙였다.
이를 디자인 패턴 (Design Pattern) 이라고 한다.
 23개의 디자인 패턴을
『Elements of Reusable Object-Oriented Software』
라는 책으로 발간하였다.

Gang of Four 분들은 모두 안경을 썼다.

 
▷디자인 패턴의 용어를 빌리면 서로의 아이디어를 보다 용이하게 비교, 논의할 수 있게 된다.
▷재사용과 기능확장이 쉬운 소프트웨어를 만들기 위한 유익한 기법이 바로 디자인 패턴이다.
 
---------------------------------------------------------
1. Iterator
2. Adapter
3. Template Method
4. Factory Method
5. Singleton
6. Prototype
7. Builder
8. Abstract Factory
9. Bridge
10. Strategy
11. Composite
12. Decorator
13. Visitor
14. Chain of Responsibility
15. Facade
16. Mediator
17. Observer
18. Memento
19. State
20. Flyweight
21. Proxy
22. Command
23. Interpreter
---------------------------------------------------------

▶Adapter(Wrapper) 패턴

"쓸 수 있게 호환시키시"

"포장해서 재사용하기"


-왜 사용하는가
-클래스다이어그램
-각 클래스(인터페이스)의 역할
-Java소스


▶Adapter(Wrapper) 패턴에 대한 이해


-클라이언트가 무언가 할 때 A라는 방식을 사용하고 있다.

-클라이언트가 '무언가 유용한 기능'을 사용하려는데 이것은 B라는 방식으로만 사용할 수 있다.

-클라이언트가 A라는 방식을 고수하면서 B방식으로 동작하는 '무언가 유용한 기능'을 사용하려면 어떻게 해야 할까?

-B방식을 A방식으로 수정하여야 할까? 이미 완성되어 있는 '무언가 유용한 기능'을 변경시키는 것은 좋지 않다.
-방법은?!

-B방식을 A방식으로 포장한다면? 겉으로는 A방식으로 동작하게 된다.

-A방식 내부에서 B방식이 실행된다면?  A방식이지만 내부에서는 B방식을 실행시킨다.

-이미 구현되어 있는 기능을 클라이언트가 사용하는 방식에 맞춰 사용할 수 있도록 중간에서 변환시켜 주는 것을 Adapter라고 한다.

-구방식을 고집하는 클라이언트인데 구방식 내부에서 신방식을 동작시킨다면, 겉으로는 구방식이지만 실제로는 신방식을 사용할 수 있다.

-Adapter패턴을 활용하면 클라이언트가 동작하는 방식은 변경하지 않고, 다양한 방식으로 동작하는 버전들을 클라이언트의 방식으로 포장시켜 동작시킬 수 있다.

-Adapter패턴은 사용할 기능을 사용하고 있는 방식으로 감싸거나, 사용하고 있는 방식 내부에서 기능을 위임시켜 구현한다.

-Adapter패턴은 이미 만들어진 클래스를 새로운 인터페이스에 맞게 개조시키는 것이다.

-Adapter패턴은 이미 만들어진 클래스를 전혀 수정하지 않고 사용 중인 인터페이스에 맞추는 것이다.

-Adapter패턴은 2가지 방식이 있다. 

1) 클라이언트가 인터페이스의 기능을 이용하고 있을 때, -다중 상속 방식이라 하자
확장(extends)과 구현(implements)을 통해 Adapter를 구현한다.(인터페이스이므로 다중 상속)

2) 클라이언트가 클래스의 기능을 이용하고 있을 때, -위임 방식이라 하자
확장(extends)과 위임(has하여 이용)을 통해 Adapter를 구현한다.(Adapter가 두 클래스를 다중 상속받을 수 없으므로) 

 

▶왜 사용하는가

다른 방식으로 동작하는 어떠한 유용한 기능을 나(기능을 사용하는 주체)의 방식으로 쓸 수 있게 할 때 사용한다. 다른 방식으로 동작하는 어떠한 기능을 내가 사용하고 있는 방식에 맞춰 개조, 변환, 포장하여 재사용하는 것이다.
220V 전기를 제공하는 콘센트가 아무리 유용하더라도 9V로 충전하는 휴대폰을 직접 충전할 수 없다. 콘센트에서 공급하는 전기 그 자체가 참 유용하고 220V를 그대로 이용하는 가전제품도 있지만,
휴대폰을 충전하기 위해서는 중간에 어댑터가 필요한 것이다.

▶클래스다이어그램


1) 다중 상속으로 Adapter 구현: 클라이언트의 방식을 구현하고, 유용한 기능을 확장하는 Adapter를 만드는 방식

인터페이스에 의존하는 클라이언트인 경우 Java에서 Adapter는 다중 상속 방식으로 구현할 수 있다.

 
2) 확장과 위임으로 Adapter 구현: 클라이언트의 방식을 확장하고, 유용한 기능을 제공하는 객체를 포함하여 기능을 위임하는 Adapter를 만드는 방식

클래스에 의존하는 클라이언트의 경우 Java에서 두 클래스를 다중 상속받는 Adapter를 만들 수 없으므로 확장과 위임을 이용하는 방식으로 구현할 수 있다.

 

▶각 클래스(인터페이스)의 역할

▷ Client 역할 - Client.java
 :본인이 사용하는 방식이 정해져 있다.
▷ Client의 방식 역할 - A.java
 
:Client가 사용하는 방식이다. 인터페이스 또는 클래스의 메서드가 될 수 있다.
▷어댑터 역할 -  AdapterOfUsefulFunction.java
 
:클라이언트가 사용하는 방식에 맞도록  '유용한 기능'을 변형, 개조, 포장하여 Client의 방식으로 '유용한 기능'을 사용할 수 있게 해 준다.
▷유용한 기능 역할 - UsefulFunction.java
 
:Client가 사용할 유용한 기능이다.
 

▶Java 소스

*java1.8 이전 버전을 사용하는 교재를 참고하였습니다.
Java API 문서 등을 참조하여 제네릭을 적용하는 등 소스를 개선할 필요가 있습니다.
(https://docs.oracle.com/en/java/javase/20/docs/api/)
 

1) 다중 상속으로 Adapter 구현: 클라이언트의 방식을 구현하고, 유용한 기능을 확장하는 Adapter를 만드는 방식
Client.java

package main.java.designpattern.adapter;

public class Client{
    public static void main(String[] args){

        A a = new AdapterOfUsefulFunction();

        // 인터페이스A의 방식으로만 동작하는 클라이언트
        // 난 인터페이스 A의 a()라는 방식으로만 동작한다.
        //아래의 코드를 변경시키지 않고 UsefulFunction의 유용한 기능 b()를 이용하는 것이 Adapter패턴이다.

        a.a();

    }
}

 
A.java

package main.java.designpattern.adapter;

public interface A {

    public abstract void a();

}

AdapterOfUsefulFunction.java

package main.java.designpattern.adapter;

public class AdapterOfUsefulFunction extends UsefulFunction implements A{

    @Override
    public void a() {
        b();
    }

    @Override
    public void b() {
        super.b();
    }
}

 
UsefulFunction.java

package main.java.designpattern.adapter;

public class UsefulFunction {

    public void b(){
        System.out.println("나는 유용한 기능b");
    }

}

 
2) 확장과 위임으로 Adapter 구현: 클라이언트의 방식을 확장하고, 유용한 기능을 제공하는 객체를 포함하여 기능을 위임하는 Adapter를 만드는 방식
Client.java

package main.java.designpattern.adapter2;

public class Client {

    public static void main(String[] args){

        A a = new AdapterOfUsefulFunction();

        // 클래스A의 방식으로만 동작하는 클라이언트
        // 난 클래스 A의 a() 메소드만 이용한다.
        //아래의 코드를 변경시키지 않고 UsefulFunction의 유용한 기능 b()를 이용하는 것이 Adapter패턴이다.

        a.a();

    }
}

 
A.java

package main.java.designpattern.adapter2;

public class A {

    public void a(){}

}

 
AdapterOfUsefulFunction.java

package main.java.designpattern.adapter2;

public class AdapterOfUsefulFunction extends A {

    private UsefulFunction usefulFunction;

    public AdapterOfUsefulFunction(){
        this.usefulFunction = new UsefulFunction();
    }

    @Override
    public void a(){
        //a() 기능이지만 내부에서는 b()기능이 동작하도록 기능을 '위임'한다.
        usefulFunction.b();
    }
}

 
UsefulFunction.java

package main.java.designpattern.adapter2;

public class UsefulFunction {

    public void b(){
        System.out.println("나는 유용한 기능b");
    }

}

 
---------------------------------------------------------
※참고자료
-Java언어로 배우는 디자인 패턴 입문
-Headfirst Design Pattern
-나무위키, 위키백과, 네이버백과
-리팩토링구루(https://refactoring.guru/ko)

반응형