Java 다형성, 역할과 구현하기 예제 - OCP

"Java 다형성, 역할과 구현하기 예제 - 자동차" 게시 글에서 코드 예제를 만들어 보는 시간을 가졌다. 코드 예제를 통해 OCP 에 대해서 자세히 알아보도록 한다.


OCP (Open - Closed Principle) 원칙

객체 지향 설계 원칙 중 하나로 OCP 원칙이 있다. OCP 원칙의 장점은 객체지향의 장점을 극대화한다.

  • Open for extension(확장은 열려야 한다.) 새로운 기능의 추가나 변경 사항이 생긴 경우, 기존 코드는 확장할 수 있어야 한다.
  • Closed for modification(변경은 닫아야 한다.) 기존의 코드는 수정하지 않는다.

확장에는 열고, 변경에는 닫아야 한다는 뜻으로, 기존의 코드 수정 없이 신규 기능을 추가하는 의미이다.
어려운 내용을 쉽게 표현하면 추가, 확장을 가까이 하고, 수정은 멀리 하라는 원칙이다.


신규 자동차 추가하기

코드 예제에서 ModelYCar, K5Car 코드를 개선하였는데, 이어서 차량을 추가하는 코드를 추가해보도록 한다.

신규 차량을 코드를 추가하는데 Driver 클래스의 코드는 변경되지 않아야 한다.
운전 가능한 차량의 종류들이 늘어나도 Car 클래스를 바라보는 Driver 코드는 변경되지 않아야 한다.
즉, 기능이 확장되더라도 main() 제외한 코드는 변경되지 않아야 한다.

HydrogenCar 신규 차량을 추가하여 OCP 에 익숙해지도록 한다

HydrogenCar.java

package k5;

public class HydrogenCar implements Car {

    @Override
    public void startEngine() {
        System.out.println("수소차 엔진 켜기");
    }

    @Override
    public void pressAccelerator() {
        System.out.println("수소차 가속하기");
    }

    @Override
    public void offEngine() {
        System.out.println("수소차 엔진 끄기");
    }
}

CarMain.java

package k5;

public class CarMain {
    public static void main(String[] args) {
        Driver driver = new Driver();

        Car k5car = new K5Car();
        driver.setCar(k5car);
        driver.drive();

        Car modelYCar = new ModelYCar();
        driver.setCar(modelYCar);
        driver.drive();

        Car hydrogenCar = new HydrogenCar();
        driver.setCar(hydrogenCar);
        driver.drive();
    }
}

출력 결과

선택한 자동차 불려옵니다. k5.K5Car@4e50df2e
선택한 자동차 운전하기
K5 엔진 켜기
K5 엑셀 가속하기
K5 엔진 끄기
선택한 자동차 불려옵니다. k5.ModelYCar@7cc355be
선택한 자동차 운전하기
Model Y 엔진 켜기
Model Y 엑셀 가속하기
Model Y 엔진 끄기
선택한 자동차 불려옵니다. k5.HydrogenCar@12edcd21
선택한 자동차 운전하기
수소차 엔진 켜기
수소차 가속하기
수소차 엔진 끄기

HydrogenCar 클래스와 추가하였을 뿐인데, main() 에서 추가한 클래스가 즉시 적용이 되었다.
OCP 원칙의 의미들을 이제 다시 확인해보도록 한다.

확장에 열려야 한다는 의미

Car 인터페이스를 사용해 신규 차량 HydrogenCar 자유롭게 추가하였다. Car 인터페이스로 구현(HydrogenCar)하여 기능을 추가한 것이다. Driver 클래스에서도 Car 인터페이스를 통해 새롭게 추가한 차량을 변경이나 수정 없이 호출할 수 있었다.

수정은 닫혀야 한다는 의미.

새로운 차를 추가한 경우 기존 코드의 수정이 필요하다. 코드의 수정이 필요하다.
첫째로, 코드가 변하지 않는 부분이 없는 Car 클래스를 가리켜 기능을 수행하는 Driver 클래스가 있다. Car 인터페이스를 사용하여 클라이언트인 Driver 코드를 수정이 필요 없어졌다.
둘째로, 코드가 변하는 부분은 main 함수로 새로운 차를 생성하고, Driver 에게 나에게 필요한 차를 전달한다. 이러한 코드 수정은 피할 수 없다. main() 함수는 전체 프로그램을 설정하고 조율하는 역할로, OCP 원칙을 지켜도 변경이 필요하다.

정리

  • Car 사용하는 클라이언트 코드 Driver 덕에 코드 변경 없이 새 차량을 확장하였다.
  • 다형성을 활용하고 역할과 구현을 잘 분리한 덕에 새 차량을 추가하여도 핵심 코드는 유지할 수 있었다.

코드의 변경 없이 쉽게 교체하는 디자인 패턴 중 하나로 전략 패턴이 있다.
앞에서 신규 차량을 추가한 것이 OCP 원칙을 지켰을 뿐 아니라, 전략 패턴을 사용한 것과 같다.

💡
전략 패턴(Strategy Pattern)
클라이언트 코드의 변경 없이 쉽게 교체할 수 있는 것이다. Car 인터페이스가 전략을 정의하는 인터페이스가 되고, 각각의 차량이 전략의 구체적인 구현체가 된다.