본문 바로가기
Thinking/Concept

어댑터 패턴(Adapter Pattern) 이해하기

by Dev_카페인 2024. 11. 15.
반응형

어댑터 패턴(Adapter Pattern) 이해하기

어댑터 패턴은 서로 다른 인터페이스를 가진 클래스들이 함께 작동할 수 있도록 중간에 어댑터 객체를 두어 호환성을 제공하는 구조적 패턴입니다. 어댑터 패턴은 기존 클래스의 인터페이스를 변환하여 새 인터페이스에 맞추는 방식으로, 클라이언트 코드의 수정 없이 기존 코드의 재사용을 가능하게 합니다.

이번 포스트에서는 어댑터 패턴의 개념과 구조, 구현 방법, 장단점 및 사용 시점을 구체적인 예제와 함께 알아보겠습니다.

 

어댑터 패턴이란?

어댑터 패턴은 호환되지 않는 인터페이스를 가진 클래스들이 함께 작동할 수 있도록 변환해주는 디자인 패턴입니다. 어댑터는 기존 인터페이스를 새로운 인터페이스로 감싸는 래퍼(wrapper) 역할을 하며, 클라이언트가 원하는 형태로 기존 클래스를 사용할 수 있게 해줍니다.

어댑터 패턴은 다음과 같은 경우에 유용합니다:

  • 기존 클래스를 재사용해야 하지만 인터페이스가 맞지 않는 경우
  • 클라이언트가 특정 인터페이스를 기대하지만, 기존 클래스가 해당 인터페이스를 구현하지 않는 경우

어댑터 패턴의 구조

어댑터 패턴은 다음과 같은 구성 요소로 이루어집니다.

  1. Target (목표 인터페이스): 클라이언트가 기대하는 인터페이스입니다. 어댑터 패턴을 통해 기존 클래스의 인터페이스가 이 목표 인터페이스에 맞춰집니다.
  2. Client (클라이언트): Target 인터페이스를 사용하며, 인터페이스 변환 없이 기존 클래스를 사용할 수 없기 때문에 어댑터를 통해 접근합니다.
  3. Adaptee (적응 대상): 클라이언트가 사용하려는 기존 클래스입니다. Target과는 다른 인터페이스를 가지고 있습니다.
  4. Adapter (어댑터): Target 인터페이스를 구현하며, Adaptee의 인터페이스를 Target에 맞게 변환하여 Adaptee를 사용할 수 있도록 합니다.

어댑터(Adapter)
어댑터(Adapter)

어댑터 패턴의 구현 방법

예제 코드: 전원 어댑터 예제

가정용 전기 장비가 미국 표준(110V)으로 설계되었으나, 유럽의 220V 전원에 맞춰 사용할 수 있도록 변환하는 전원 어댑터 예제를 통해 어댑터 패턴을 구현해보겠습니다.

// Target 인터페이스: 클라이언트가 기대하는 전압 규격
public interface ITarget
{
    void Connect();
}

// Adaptee 클래스: 기존 전압 시스템 (220V)
public class EuropeanPlug
{
    public void ConnectEuropeanPlug()
    {
        Console.WriteLine("Connected to 220V power.");
    }
}

// Adapter 클래스: ITarget 인터페이스를 구현하고 Adaptee 클래스를 감쌉니다.
public class Adapter : ITarget
{
    private EuropeanPlug _europeanPlug;

    public Adapter(EuropeanPlug europeanPlug)
    {
        _europeanPlug = europeanPlug;
    }

    public void Connect()
    {
        // Adaptee 클래스의 인터페이스를 Target 인터페이스에 맞게 변환합니다.
        Console.WriteLine("Adapter converting 220V to 110V.");
        _europeanPlug.ConnectEuropeanPlug();
    }
}

// Client 코드
class Program
{
    static void Main()
    {
        EuropeanPlug europeanPlug = new EuropeanPlug();
        ITarget adapter = new Adapter(europeanPlug);

        // 어댑터를 통해 110V 장비에 220V 플러그 연결
        adapter.Connect();
    }
}

위 코드에서 EuropeanPlug 클래스는 220V 전원을 사용하는 기존 시스템입니다. 이 클래스의 ConnectEuropeanPlug() 메서드는 220V 연결을 나타내지만, 클라이언트는 110V 연결을 기대하고 있으므로 어댑터가 필요합니다.

Adapter 클래스는 ITarget 인터페이스를 구현하고, 내부적으로 EuropeanPlug 객체를 포함하여 Connect() 메서드에서 ConnectEuropeanPlug()를 호출합니다. 이를 통해 어댑터는 EuropeanPlug의 인터페이스를 ITarget에 맞게 변환하여 클라이언트가 기대하는 대로 작동할 수 있도록 합니다.

 

어댑터 패턴의 종류

어댑터 패턴은 클래스 어댑터객체 어댑터 두 가지 유형으로 나눌 수 있습니다.

  1. 클래스 어댑터(Class Adapter): 다중 상속을 사용하여 Target 인터페이스와 Adaptee를 상속받아 어댑터를 구현하는 방식입니다. 다중 상속이 가능한 언어에서 사용되며, 어댑터가 Target과 Adaptee의 관계를 갖습니다.
  2. 객체 어댑터(Object Adapter): Adaptee 객체를 Target 인터페이스로 감싸서 변환하는 방식입니다. Adaptee를 상속받지 않고 포함(Composition)하여 구현하므로 더 유연하게 적용할 수 있습니다.

대부분의 경우 C#과 같은 단일 상속 언어에서는 객체 어댑터 방식이 사용됩니다.

 

어댑터 패턴의 장단점

장점

  1. 클라이언트 코드 수정 없이 기존 클래스 재사용: 어댑터를 통해 기존 클래스를 클라이언트가 원하는 형태로 사용할 수 있으므로 기존 코드의 수정 없이 재사용할 수 있습니다.
  2. 확장성: 어댑터를 추가하는 방식으로 여러 개의 호환되지 않는 인터페이스를 동시에 지원할 수 있습니다.
  3. 유연성: 객체 어댑터 방식에서는 상속 대신 포함(Composition)을 사용하므로, 필요에 따라 다른 Adaptee를 감싸는 방식으로 유연하게 적용할 수 있습니다.

단점

  1. 복잡도 증가: 어댑터 객체를 추가해야 하므로 코드 구조가 다소 복잡해질 수 있습니다.
  2. 다중 어댑터 관리 필요: 여러 개의 어댑터가 필요할 경우 어댑터 객체를 관리하는 부담이 있을 수 있습니다.
  3. 성능 저하: 어댑터를 통한 간접 호출로 인해 약간의 성능 저하가 발생할 수 있습니다.

 

어댑터 패턴의 사용 예시

어댑터 패턴은 다음과 같은 경우에 유용하게 사용됩니다.

  1. 레거시 코드와 새로운 코드의 통합: 기존 시스템과 새 시스템을 연결할 때, 서로 다른 인터페이스 간의 호환성을 제공하기 위해 어댑터 패턴을 사용합니다.
  2. 서로 다른 라이브러리의 통합: 외부 라이브러리나 서드파티 API가 기존 코드와 호환되지 않는 경우, 어댑터 패턴을 통해 인터페이스 호환성을 확보합니다.
  3. 데이터 변환이 필요한 경우: 여러 데이터 소스에서 다양한 데이터 형식이 제공되는 경우, 각 형식에 맞는 어댑터를 작성하여 일관된 인터페이스로 데이터를 처리할 수 있습니다.
  4. 플랫폼 간 통합: 플랫폼마다 지원하는 표준이 다른 경우, 어댑터 패턴을 사용하여 호환성을 제공할 수 있습니다.

 

어댑터 패턴과 데코레이터 패턴의 차이점

어댑터 패턴과 데코레이터 패턴은 모두 기존 객체를 감싸는 방식으로 동작하지만, 목적과 사용 방식에서 차이가 있습니다.

  • 어댑터 패턴: 주로 서로 다른 인터페이스를 맞추기 위한 목적으로 사용됩니다. 기존 객체를 클라이언트가 원하는 형태로 변환하여 호환성을 확보합니다.
  • 데코레이터 패턴: 객체에 새로운 기능을 추가하기 위한 목적으로 사용됩니다. 객체의 인터페이스를 변환하지 않으며, 기능을 추가하기 위해 기존 객체를 감싸는 방식으로 확장합니다.

따라서, 호환성을 위한 변환이 필요한 경우에는 어댑터 패턴을, 기능 확장을 위한 경우에는 데코레이터 패턴을 사용하는 것이 적절합니다.

 

마무리하며

어댑터 패턴은 기존 클래스를 수정하지 않고도 클라이언트가 원하는 인터페이스로 변환하여 재사용할 수 있게 만드는 유용한 패턴입니다. 특히, 호환되지 않는 인터페이스를 맞추거나, 기존 코드를 유지하면서 새로운 기능을 도입할 때 큰 장점을 제공합니다.

이 패턴을 적절히 활용하면 레거시 코드와 새로운 코드 간의 통합이 수월해지며, 시스템 간의 호환성을 높일 수 있습니다. 다양한 시스템과 인터페이스가 공존하는 소프트웨어 설계에서, 어댑터 패턴을 통해 유연하게 설계하고 확장 가능성을 확보해 보세요.

반응형