본문 바로가기
디자인패턴GoF/구조패턴

자바스크립트로 이해해보는 어댑터패턴 (Adapter Pattern) In 구조패턴

by 봉이로그 2024. 1. 21.

정의

어댑터패턴은 하나의 인터페이스를 다른 인터페이스로 변환한다. 두개의 인터페이스 호환성을 제공하는 패턴이다.

어댑터를 사용하여 인터페이스 불일치로 인해 동작하지 않을 프로그래밍 구성요소를 함께 동작시킬 수 있다.

어댑터 패턴은 래퍼(Wrapper Pattern)이라고도 한다.

어댑터 패턴을 이용하면 유지보수와 확장성을 향상시킬 수 있다.

 

Usecase

1) 새 구성요소를 통합하고, 애플리케이션의 기존 구성요소와 함께 작동해야 하는 경우

2) 개선된 인터페이스로 프로그램의 일부를 다시 작성하지만 여전히 이전 코드의 동작을 기대해야하는 경우

 

Example1

interface IMonitor {
  show: () => void;
}

class OldMonitor1 implements IMonitor {
  show() {
    return "show old monitor1";
  }
}

class OldMonitor2 implements IMonitor {
  show() {
    return "show old monitor2";
  }
}

interface INewMonitor {
  visible: () => void;
}

class NewMonitor implements INewMonitor {
  visible() {
    return "show new monitor";
  }
}

class NewMonitorAdapter implements IMonitor {
  show() {
    return new NewMonitor().visible();
  }
}

function client() {
  let oldMonitor1 = new OldMonitor1();
  let oldMonitor2 = new OldMonitor2();

  let newMonitor = new NewMonitorAdapter();

  let monitors = [oldMonitor1, oldMonitor2, newMonitor];

  for (let item of monitors) {
    console.log(item.show()); // 기존의 show 메소드를 이용해 출력가능
  }
}

client();

// 출력
// show old monitor1
// show old monitor2
// show new monitor

 

client 코드에서는 모니터들을 for...of를 이용해 열거하고 출력한다.

 

구형모니터들은 출력을 show 메소드를 이용해 구현을 하고 있다.

그러다 신형모니터부터는 출력을 visible 메소드를 이용하기로 변경되었다.

 

기존의 구형모니터들의 코드를 직접 visible함수로 수정할수도 있겠지만 사이드이펙트라던지 여러 부가작용들이 많을수 있다고 가정

그러기에 우리는 무조건 show 메소드를 무조건 사용해야 한다고 생각해본다. (= 기존코드를 변경할수 없는상황)

 

이럴경우도 어댑터패턴을 사용할수 있을것이다.

 

NewMonitorAdapter 클래스를 생성한후 기존의 show 메소드를 선언하고 함수 블럭안에 신형모니터(NewMonitor)의 출력(visible) 기능을 구현한다.

 

이렇게 함으로써 기존 출력 열거코드에 모니터들이 추가가 되더라도 item.show 코드를 유지할수 있다.

 

Example2

function Shipping() {
  this.request = function (zipStart, zipEnd, weight) {
    // ...
    return "$49.75";
  };
}

// new interface

function AdvancedShipping() {
  this.login = function (credentials) {
    /* ... */
  };
  this.setStart = function (start) {
    /* ... */
  };
  this.setDestination = function (destination) {
    /* ... */
  };
  this.calculate = function (weight) {
    return "$39.50";
  };
}

// adapter interface

function ShippingAdapter(credentials) {
  var shipping = new AdvancedShipping();

  shipping.login(credentials);

  return {
    request: function (zipStart, zipEnd, weight) {
      shipping.setStart(zipStart);
      shipping.setDestination(zipEnd);
      return shipping.calculate(weight);
    },
  };
}

function runShipping() {
  var shipping = new Shipping();
  var credentials = { token: "30a8-6ee1" };
  var adapter = ShippingAdapter(credentials);

  // original shipping object and interface

  var cost = shipping.request("78701", "10010", "2 lbs");
  console.log("Old cost: " + cost);

  // new shipping object with adapted interface

  cost = adapter.request("78701", "10010", "2 lbs");

  console.log("New cost: " + cost);
}

runShipping();

 

기존의 배송(Shipping) 함수의 로직은 상품의 거리와 무게를 구하여 비용을 계산해 request 요청을 수행하고 있었다.

(=로직을 이용해 API를 호출(Request) 하고 있다는 거임)

개선된 배송(AdvancedShipping) 함수에서는 로그인 등 다른 로직들이 수정되었다.

(=기존 배송(Shipping)보다 로직들이 추가 및 개선됨)

 

우리는 기존의 API 호출(request) 코드를 직접 수정하지 않고 사용하기 위해 AdvancedShippingAdapter를 만들어 AdvancedShipping의 추가 및 개선된 로직들을 반영하여 API(request) 코드를 return한다.

 

그리하여 사용자코드(runShipping)에서는 API호출(request)코드를 수정하지 않고 adapter를 이용해 개선된 배송(AdvancedShipping) 프로그램을 수행한다.

 

 

https://www.dofactory.com/javascript/design-patterns/adapter

 

Helping .NET developers grow and make more money

Ready to land your dream job? Dofactory is the place for Microsoft Technology jobs, like C#, .NET, Azure, SQL, Data, Copilot, ML, AI and others

www.dofactory.com