• 한 클래스의 인터페이스를 사용하고자 하는 다른 인터페이스로 변환
  • 클라이언트가 사용하는 인터페이스를 따르지 않는 기존의 코드를 재사용할 수 있게 된다
  • 써드파티 라이브러리의 인터페이스를 일반적으로 애플리케이션 코드로 변환하는데 사용
  • ex) 220V-110V어댑터(a.k.a 돼지코)

용어

image

  • Client : 클라이언트 코드
  • Target Interface : 어댑터가 구현하는 인터페이스 (From)
  • Adapter : Client-Adaptee를 연결해주는 역할
  • Adaptee : 클라이언트 코드와 호환성 없는 외부 코드 (To)

구현

  • Target Interface : 클라이언트가 사용하는 인터페이스이자 Adapter가 연결할 인터페이스 (어댑터 작동 목표)
public interface Electronic110V { 
    void powerOn();
}

public class HairDryer implements Electronic110V { 
    @Override 
    public void powerOn() { 
        System.out.println("HairDryer On"); 
    } 
}
  • Adaptee : 클라이언트 코드와 연관없는 외부 코드, 어댑터에 의해 변환 당할 자료형
public interface Electronic220V { 
    void connect();
}

public class Cleaner implements Electronic220V { 
    @Override 
    public void connect() {
        System.out.println("Cleaner On"); 
    } 
}
  • Adapter : 변환 결과(어댑터 작동 목표)의 인터페이스를 구현
    • Electronic220V 인터페이스를 쓰는 객체를 Electronic110V로 변환 (220V -> 110V)
    • SocketAdapter : 220V 전자기기를 110V 콘센트에 꽂을 수 있도록 하는 어댑터
public class SocketAdapter implements Electronic110V { 

    Electronic220V electronic220V;      // Adaptee

    public SocketAdapter(Electronic220V electronic220V) { 
        this.electronic220V = electronic220V; 
    } 

    @Override 
    public void powerOn() { 
        System.out.print("Using Adapter : "); 
        electronic220V.connect(); 
    }
}
public class Main {

    public static void main(String[] args) {
        // HairDryer (110V)
        Electronic110V hairDryer = new HairDryer();
        hairDryer.powerOn();        // HairDryer On

        // Cleaner (220V)
        Electronic220V cleaner = new Cleaner();
        cleaner.connect();          // Cleaner On

        // Adapter (220V -> 110V)
        SocketAdapter adapter = new SocketAdapter(cleaner);
        adapter.powerOn();      // Using Adapter : Cleaner On 
    }
}

ex) Spring Security

  • Spring Security는 보안 기능을 갖고 있는 스프링 서브 프레임워크이다. 여기에는 로그인 과정에서 사용하기 위한 UserDetails, UserDetailsService가 인터페이스가 만들어져 있다
  • 어플리케이션에서 사용하는 객체를 Spring Security에 적용하기 위해서는 이에 맞게 변환해서 사용해야 한다 (i.g. username 대신 email 등)

  • Target Inferface
public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    String getPassword();
    String getUsername();
    boolean isAccountNonExpired();
    boolean isAccountNonLocked();
    boolean isCredentialsNonExpired();
    boolean isEnabled();
}

public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
  • Adaptee
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String email;
    private String password;
}
  • Adapter
public class CustomUserDetails implements UserDetails {
    private User user;

    public CustomUserDetails(User user) {
        this.user = user;
    }

    @Override
    public String getPassword() { 
        return user.getPassword();
    }

    @Override
    public String getUsername() {  
        return user.getEmail();
    }
    ...
}

public class UserService implements UserDetailsService {
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException(username));

        return new CustomUserDetails(user);
    }
}

장점

  1. 클라이언트가 사용하는 인터페이스를 따르지 않는 기존의 코드를 재사용할 수 있게 된다

단점

  1. 새 클래스가 생겨 구조가 복잡해진다