어댑터 패턴?
- 한 클래스의 인터페이스를 클라이언트에서 사용하고자하는 다른 인터페이스로 변환하는 패턴
다시 말하자면, 호환되지 않는 인터페이슬르 사용하는 클라이언트를 그대로 활용할 수 있으며, 이로써 클라이언트와 구현된 인터페이스를 분리시키며, 이후에 인터페이스가 바뀌더라도 그 변경 내역은 어댑터에 캡슐화 되기 때문에 클라이언트를 바꿀 필요가 없어진다.
예를 들어, 로봇(Robot)을 만들어보자. 이 로봇의 기능은 왼팔과 오른팔을 위아래로 움직을 수 있다.
//Robot의 팔을 위, 아래롤 움직이게 할 인터페이스 구현 interface ArmMoving{ //ArmMoving의 추상 메서드 구현(=로봇의 팔을 움직이게 하는 메서드) public abstract void up(); public abstract void down(); } //Robot의 Arm 객체를 담을 클래스 정의(feat.ArmMoving인터페이스) class Arm implements ArmMoving { //Arm 생성자 (=Robot의 Arm을 만드는) public Arm() { System.out.println("Make Robot Arms"); } //ArmMoving 인터페이스를 오버라이딩(=추상 메서드 정의) public void up(){ System.out.println("Robot Arm up"); } public void down(){ System.out.println("Robot Arm down"); } } //Robot를 구현할 클래스 정의 class Robot { //ArmMoving 인터페이스 변수 설정 private ArmMoving leftArm; //왼쪽 팔 private ArmMoving rightArm; //오른쪽 팔 //Robot 생성자 public Robot(ArmMoving leftArm,ArmMoving rightArm) { this.leftArm = leftArm; this.rightArm = rightArm; } //Robot의 Arm을 움직일 메서드 구현 public void armUp(){ rightArm.up(); leftArm.up(); } public void armDown(){ rightArm.down(); leftArm.down(); } } //Client 클래스 구현(main클래스 구현) public class Cleint { public static void main (String[] args){ //Robot객체 생성 Robot robot = new Robot(new Arm(),new Arm()); //Robot의 Arm 움직이기 robot.armDown(); robot.armUp(); } } //결과값 Make Robot Arms Make Robot Arms Robot Arm down Robot Arm down Robot Arm up Robot Arm up
하지만 여기에서 Robot의 오른 팔을 새로운 팔로 교체해야 한다고 가정하자. 하지만 새로운 팔의 소스는 수정해서는 안된다고 하자. 한 두번 팔을 수정하는 것은 가능하다. 하지만 수정사항이 계속해서 들어온다면, 그때마다 코드를 수정해야하고, 그럴때 마다 코드는 길어진다.
- BrandNewArm 클래스 정의
//Robot의 팔을 위, 아래롤 움직이게 할 인터페이스 구현 interface ArmMoving{ //ArmMoving의 추상 메서드 구현(=로봇의 팔을 움직이게 하는 메서드) public abstract void up(); public abstract void down(); } //Robot의 Arm 객체를 담을 클래스 정의(feat.ArmMoving인터페이스) class Arm implements ArmMoving { //Arm 생성자 (=Robot의 Arm을 만드는) public Arm() { System.out.println("Make Robot Arms"); } //ArmMoving 인터페이스를 오버라이딩(=추상 메서드 정의) public void up(){ System.out.println("Robot Arm up"); } public void down(){ System.out.println("Robot Arm down"); } } //Robot를 구현할 클래스 정의 class Robot { //ArmMoving 인터페이스 변수 설정 private ArmMoving leftArm; //왼쪽 팔 private ArmMoving rightArm; //오른쪽 팔 //Robot 생성자 public Robot(ArmMoving leftArm,ArmMoving rightArm) { this.leftArm = leftArm; this.rightArm = rightArm; } //Robot의 Arm을 움직일 메서드 구현 public void armUp(){ rightArm.up(); leftArm.up(); } public void armDown(){ rightArm.down(); leftArm.down(); } } //BrandNewArm 클래스 정의 class BrandNewArm { //BrandNewArm 생성자 public BrandNewArm() { System.out.println("Make Robot BrandNewArm"); } //BrandNewArm을 움직이는 메서드 public void newUp() { System.out.println("Robot BrandNewArm Up"); } public void newDown() { System.out.println("Robot BrandNewArm Down"); } }
- BrandNewArm클래스의 Adapter 클래스 정의
//BrandNewArmAdapter클래스 정의 class BrandNewArmAdapter extends BrandNewArm implements ArmMoving { //BrandNewArmAdapter 생성자 public BrandNewArmAdapter (){} //ArmMoving 인터페이스에 있는 추상 메서드 오버라이딩 public void up(){ super.newUp(); } public void down(){ super.newDown(); } } //Client 클래스 구현(main클래스 구현) public class Main { public static void main (String[] args){ //Robot객체 생성(+ BrandNewArm 붙이기) Robot robot = new Robot(new BrandNewArmAdapter(),new Arm()); //Robot의 Arm 움직이기 robot.armDown(); robot.armUp(); } } //결과값 Make Robot Arms Make Robot BrandNewArm Robot BrandNewArm Down Robot Arm down Robot BrandNewArm Up Robot Arm up
이렇게 한다면, 기존의 코드 변경 없이, 새로운 팔이 등장한다면, 그에 대한 클래스만 추가하면 된다.
장점
- 기존의 코들르 변경할 필요가 없다.
- 클래스의 재활용성이 높아진다.
단점
- 구성 요소를 위해 클래스를 증가시켜야 하기 때문에 코드가 복잡해질 우려가 잇다
- 클래스 어댑테의 경우 상속을 사용하기 때문에 클래스간의 결합도가 높아질 수 있다.
- 객체 어댑터의 경우 대부분의 코들를 다시 작성해야하기 때문에 효율적이지 못하다.