욱'S 노트

Adapter 패턴 이야기 본문

Story/Design Pattern

Adapter 패턴 이야기

devsun 2015. 9. 1. 10:47

Adapter 패턴은 우리가 인지하지도 못한 사이 엄청나게 사용하고 있는 패턴이다. 과연 Adapter 패턴이란 무엇인가? 


다음은 위키피티아의 정의이다. 어댑터 패턴(Adapter pattern)은 클래스의 인터페이스를 사용자가 기대하는 다른 인터페이스로 변환하는 패턴으로, 호환성이 없는 인터페이스 때문에 함께 동작할 수 없는 클래스들이 함께 작동하도록 해준다. 


한마디로 내가 사용하기 편한 메소드들로 감싼다고 생각을 하면 이해가 쉬울 것이다. 


이러한 패턴은 외부 라이브러리를 사용할 때 활용 될 수 있다. 실제 비즈니스 로직을 구현하는데는 외부 라이브러리가 많이 혼재되어 있다면 굉장히 이해하기 어려울 것이다. 이런 경우 내가 사용하고자 하는 인터페이스를 잘 정의하고 그의 구현에서 외부 라이브러리의 클래스들을 사용한다면 훨씬 깔끔하게 우리의 비즈니스 로직을 구현할 수 있을 것이다

public class JdbcTemplateAdapter {
private NamedParameterJdbcTemplate adaptee;

public JdbcTemplateAdapter(DataSource dataSource) {
adaptee = new NamedParameterJdbcTemplate(dataSource);
}

public Map<String, Object> queryForMap(String sql) {
return adaptee.queryForMap(sql, Collections.EMPTY_MAP);
}
}

위와 같이 심플한 외부 스프링 프레임워크에 대한 간단한 Adapter를 만들어 보았다. 언듯 보면 왜 굳이 이러한 Adapter클래스를 만들어야 하는지 이해가 안될 수도 있다. 그러나 일단 만들었으니 이런 경우 실제 데이터베이스 테스트를 하는 것이 확실히 유용하다. 위 클래스에 대한 테스트케이스는 다음과 같이 작성되었다.

@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class JdbcTemplateAdapterTest {
@Inject
private DataSource dataSource;

private JdbcTemplateAdapter jdbcTemplateAdapter;

@Before
public void setup() {
jdbcTemplateAdapter = new JdbcTemplateAdapter(dataSource);
}

@Test
public void testQueryForMap() {
Map<String, Object> result = jdbcTemplateAdapter.queryForMap("SELECT KAKAO FROM KAKAO_FRIENDS WHERE KAKAO_ID = 'A'");

assertEquals(1, result.size());
assertEquals("KAKAO", result.get("KAKAO"));
}
}

만약 위의 테스트케이스가 정상적으로 수행했다면 우리는 성공적으로 Adapter 클래스를 작성했다고 볼 수 있겠다. 여기서 자세한 스프링 테스트에 대한 설명은 생략하겠다.


다음은 해당 클래스를 사용하는 다른 클래스를 하나 작성해보자. 이 클래스의 목적은 데이터베이스로부터 특정값을 읽어서 URL을 생성하는 것이다.

public class JdbcUrlFactory {
private JdbcTemplateAdapter jdbcTemplateAdapter;
private String urlPattern;
private String sql;
private UriTemplateHandler uriTemplateHandler = new DefaultUriTemplateHandler();

public JdbcUrlFactory(JdbcTemplateAdapter jdbcTemplateAdapter, String urlPattern, String sql) {
this.jdbcTemplateAdapter = jdbcTemplateAdapter;
this.urlPattern = urlPattern;
this.sql = sql;
}

public String create() {
Map<String, Object> result = jdbcTemplateAdapter.queryForMap(sql);
URI uri = uriTemplateHandler.expand(urlPattern, result);
return uri.toString();
}
}

역시 간단하다. 그러나 진가는 테스트에서 발휘된다. JdbcTemplate에 대한 어댑터를 만들어놓았기 때문에 해당 Factory 클래스의 테스트는 데이터베이스 디펜던시가 사라진다.

public class JdbcUrlFactoryTest {
private static final String QUERY = "SELECT V_ID FROM MOVIE";
private static final String EXPECTED_URL = "http://api.kakao.com?v_id=1234";
private JdbcTemplateAdapter jdbcTemplateAdapter;

@Before
public void setup() {
jdbcTemplateAdapter = mock(JdbcTemplateAdapter.class);

Map<String,Object> result = new HashMap<>();
result.put("V_ID", "1234");

when(jdbcTemplateAdapter.queryForMap(QUERY)).thenReturn(result);
}

@Test
public void testCreate() {
JdbcUrlFactory jdbcUrlFactory = new JdbcUrlFactory(
jdbcTemplateAdapter,
"http://api.kakao.com?v_id={V_ID}",
QUERY);

String url = jdbcUrlFactory.create();

assertEquals(EXPECTED_URL, url);
}
}

결론적으로 어댑터를 잘 활용하면 외부 라이브러리에 대한 검증을 확실히 하는 반면 테스트에 대한 이점도 얻을 수 있다. 그리고 어댑터에 대한 테스트 케이스를 충분히 작성해놓는다면 외부 라이브러리의 변경에 대한 완충 작용을 수행할 수 있게되어 좀 더 안전한 버젼업을 진행할 수 있다.

'Story > Design Pattern' 카테고리의 다른 글

Builder pattern vs Factory pattern  (2) 2016.05.02
Comments