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

❓어댑터 패턴?

어댑터 패턴은 구조 패턴으로, 다른 말로 래퍼(Wrapper) 패턴이라고도 합니다. 연관성 없는 2개의 객체를 묶어서 인터페이스를 만들어 주는 패턴을 말하는데요, 이를 통해 서로 연관 없는 두 객체의 호환성을 이루어줄 수 있습니다!

❓어댑터 패턴의 종류

어댑터 패턴은 두 가지 방식이 있는데요, 클래스를 이용한 클래스 어댑터 패턴(Class Adapter Pattern), 그리고 객체를 이용한 객체 어댑터 패턴(Object Adapter Pattern)이 있습니다!

❗클래스 어댑터 패턴

클래스의 상속을 이용한 어댑터 패턴입니다.

// 기존에 사용하던 클래스
class OldDate {
  constructor(dateString) {
    this.dateString = dateString;
  }

  getFormattedDate() {
    return this.dateString;
  }
}

// 새로운 타겟 인터페이스
class NewDate {
  constructor(date) {
    this.date = date;
  }

  getFormattedDate() {
    const year = this.date.getFullYear();
    const month = this.date.getMonth() + 1;
    const day = this.date.getDate();
    return `${year}-${month}-${day}`;
  }
}

// 어댑터 클래스
class DateAdapter extends NewDate {
  constructor(oldDate) {
    // 기존 클래스의 데이터를 새로운 클래스에 맞게 변환
    const [year, month, day] = oldDate.getFormattedDate().split('-');
    const newDate = new Date(year, month - 1, day);
    
    // 새로운 클래스의 생성자 호출
    super(newDate);
  }
}

// 기존의 OldDate를 사용하는 경우
const oldDate = new OldDate('2023-11-14');
console.log(date.getFormattedDate()); // 2023-11-14

// 어댑터를 사용하여 NewDate 인터페이스로 호출
const adaptedDate = new DateAdapter(oldDate);
console.log(date.getFormattedDate()) // 2023-11-14

기존에 사용하던 OldDate 클래스를 NewData로 변경하려고 합니다. 이 때 기존의 데이터를 그대로 둔 채, 어댑터 클래스를 이용하여 원하는 형태를 만들 수 있고, 이를 통해 호환성을 가진 NewDate 클래스를 사용할 수 있게 됩니다.

하지만 상속을 이용하기 때문에 계층 간의 강한 결합이 생긴다는 단점이 있기 때문입니다.

❗객체 어댑터 패턴

객체의 의존성을 이용한 어댑터 패턴입니다.

// 기존에 존재하는 클래스
class OldDate {
  constructor(dateString) {
    this.dateString = dateString;
  }

  getFormattedDate() {
    return this.dateString;
  }
}

// 새로운 타겟 인터페이스
class NewDate {
  constructor(date) {
    this.date = date;
  }

  getFormattedDate() {
    const year = this.date.getFullYear();
    const month = this.date.getMonth() + 1;
    const day = this.date.getDate();
    return `${year}-${month}-${day}`;
  }
}

// 어댑터 클래스
class DateAdapter {
  constructor(oldDate) {
    this.oldDate = oldDate;
  }

  getFormattedDate() {
    // 기존 클래스의 데이터를 새로운 클래스에 맞게 변환
    const [year, month, day] = this.oldDate.getFormattedDate().split('-');
    const newDate = new Date(year, month - 1, day);
    
    const adaptedDate = new NewDate(newDate);
    
    return adaptedDate.getFormattedDate();
  }
}

// 기존의 OldDate를 사용하는 경우
const oldDate = new OldDate('2023-11-14');
console.log(date.getFormattedDate()); // 2023-11-14

// 어댑터를 사용하여 NewDate 인터페이스로 호출
const adaptedDate = new DateAdapter(oldDate);
console.log(date.getFormattedDate()) // 2023-11-14

객체의 구성(Composition), 의존성을 이용하여 상속 없이 NewDate객체를 만들어낼 수 있습니다. 상속의 강한 결합이라는 단점을 해결한 방식이라는 것을 알게 되었네요!

❗결론!

어댑터 패턴은 코드의 성능이 향상되는 것이 아닙니다. 기존 코드와 호환성을 만들어낸다는 장점 때문에 사용하는 것이죠. 외부 라이브러리나 서드파티 컴포넌트에 사용하기 적합한 패턴이라고 할 수 있습니다. 성능은 오히려 어댑터가 생김으로써 속도가 저하됩니다. 그렇기 때문에 이를 유의하고 적절하게 사용하면 될 것 같습니다!