[Java] 어댑터 패턴(Adapter Pattern)을 이용한 OCP 개방 폐쇄 원칙 이해하기

    OCP - 개방 폐쇄 원칙

    소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에 대해서는 열려 있어야 하지만 변경에 대해서는 닫혀 있어야 한다. by 로버트 C. 마틴 

     

    위 말을 의역하면 "자신의 확장에는 열려 있고, 주변의 변화에 대해서는 닫혀 있어야 한다" 이렇게 된다. 말만 들으면 이해하기 어렵지만 이 원칙을 지킨 디자인 패턴중 대표적으로 어댑터 패턴이 있다. 즉 어댑터 패턴을 이해하면 OCP를 이해할 수 있다.

     

    어댑터 패턴(Adapter Pattern)

    어댑터를 번역하면 변환기(converter)라고 할 수 있다. 대표적으로 110V 인터페이스를 -> 220V 인터페이스로 변환해준 어댑터를 생각하면 쉽다.

     

    위 처럼 서로 다른 두개의 인터페이스가 연결이 가능하게끔 해주는 것이고, 이것을 프로그래밍화 해서 설계한 것이 어댑터 패턴이다.

     

    예제 코드를 통해 어댑터 패턴을 이해하기

    public interface Animal {
        void walk();
    }
    
    public class Dog implements Animal {
        @Override
        public void walk() {
            System.out.println("dog walking");
        }
    }
    
    public class Cat implements Animal {
        @Override
        public void walk() {
            System.out.println("cat walking");
        }
    }
    
    public class Fish {
        void swim() {
            System.out.println("fish swimming");
        }
    }

    Animal 인터페이스가 있고 walk() 메소드가 있다. Animal을 상속받은 Dog와 Cat 클래스가 있고 walk()를 구현하였다.

    그리고 Fish 클래스가 따로 있고 Fish클래스에는 swim() 함수만 있다. 

     

    당연히 dog와 cat 객체는 walk()가 되지만 fish 객체는 swim()은 되지만 walk()는 되지 않는다.

    public class AdapterPatternMain {
        public static void main(String[] args) {
            Animal dog = new Dog();
            Animal cat = new Cat();
    
            dog.walk(); //dog walking
            cat.walk(); //cat walking
    
            Fish fish = new Fish();
            fish.swim(); //fish swimming
            //fish.walk(); //error
        }
    }

     

    그런데 fish객체가 인어공주여서 다리가 생겨서 걸을 수 있으니 walk()가 가능하게 해달라는 요청이 왔다. 그러면 대게 Fish에도 Animal을 상속받아 walk()를 구현 했을것이다.

    public class Fish implements Animal {
        void swim() {
            System.out.println("fish swimming");
        }
    
        @Override
        public void walk() {
            System.out.println("fish walking");
        }
    }
    

     

    하지만 위 처럼 walk() 추가 변경으로 Fish 클래스를 수정하면 OCP 원칙을 위배한다. OCP 원칙을 지키려면 Fish 클래스를 수정하면 안되고 FishAdapter 클래스를 하나 만드는 것 이다.

     

    public class FishAdapter implements Animal {
        Fish fish = new Fish();
    
        public FishAdapter(Fish fish) {
            this.fish = new Fish();
        }
    
        @Override
        public void walk() {
            System.out.println("fish walking");
        }
    }
    

     

    FishAdapter 클래스를 만들고 Animal 인터페이스를 상속 받아 walk()를 구현하였고 Main함수에서 FishAdatper클래스로 fish 객체를 만들고 구현한 walk() 함수를 실행시켜 fish도 걸을수 있게 하였다.

    public class AdapterPatternMain {
        public static void main(String[] args) {
            Animal dog = new Dog();
            Animal cat = new Cat();
    
            dog.walk(); //dog walking
            cat.walk(); //cat walking
    
            FishAdapter fishAdapter = new FishAdapter(new Fish());
            fishAdapter.walk(); //fish walking
            fishAdapter.fish.swim(); //fish swimming
        }
    }

     

    위 처럼 Fish 객체는 변경하지 않고 기능을 확장하도록 한여 OCP 원칙을 지킨것이다.

     

    어댑터 패턴은  어댑터 클래스를 만들고 구현할 인터페이스를 상속 받아서 기존 클래스 객체를 필드에 넣어서 사용한다는 것이 중요 관점이다.

     

    참고

    댓글

    Designed by JB FACTORY