OCP는 구성 요소가 자신의 확장에는 열려있고, 주변의 변화에는 닫혀 있어야 한다는 원칙이다.
즉, 기능 변경/수정할 때 기존 코드를 고치지 않고 동작할 수 있어야 한다는 의미이다.
행위(동작) == 전략을 클래스로 캡슐화해 동적으로 선택, 교체할 수 있게 하는 디자인 패턴이다.
OCP를 준수하기 위한 디자인 패턴이고, 새로운 기능의 추가가 기존 코드에 영향을 미치지 못하게 되면서 OCP를 만족하게 된다.
Train과 Bus 클래스가 Movable 인터페이스를 구현하고 있다.

interface Movable {
public void move();
}
class Train implements Movable {
public void move() {
System.out.println("선로로 이동");
}
}
class Car implements Movable {
public void move() {
System.out.println("도로로 이동");
}
}
public class Client
{
public static void main(String[] args) {
Movable train = new Train();
train.move();
}
}
현재 기차는 선로로, 버스는 도로로 이동하고 있다.
하지만 추후에 기차가 도로로 이동하도록 변경된다면, Bus의 move() 메서드를 수정해야 한다.
이러한 방식은 시스템 확장, 유지 보수를 어렵게 한다.
이 방법은 OCP에 위배된다. OCP에 의하면 기존 코드를 수정하지 않고 동작을 변경해야 한다.
새로운 교통수단이 추가되거나, 기존 교통수단이 변경될때마다 클래스를 수정하는 것은 좋지 않은 방법이다.
해당 문제의 해결을 위해서 전략 패턴을 사용한다.
도로로 이동하는 기차가 개발되었다.
(1) 전략(행위)을 생성
현재 존재하는 운행 방식은 선로와 도로이다.
각 방식에 대한 Strategy 클래스를 생성한다. (RailRoadStrategy, RoadStrategy)
두 전략 클래스를 캡슈화하기 위해 MovableStrategy 인터페이스를 생성한다.
캡슐화의 이유는 운행 방식에 대한 전략 뿐 아닌 다른 전략이 추가로 확장되는 경우를 고려하는 설계이다. -> 확장에 용이.

(2) 교통수단에 대한 클래스 정의
Train과 Bus는 move 메서드를 통해 움직일 수 있다.
이동 방식을 직접 메서드로 구현하는 것이 아닌, 어떻게 움직일 지에 대한 전략을 설정하여, 그 전략의 움직임 방식을 사용해 움직이도록 하자!
교통수단 클래스(Transport) 내에 전략 설정 메서드(setMovableStrategy)를 생성한다.

(3) Client에서 객체 사용
Train과 Bus 객체를 생성하고 각 교통수단의 움직임 설정을 위해 setMovableStrategy() 메서드를 호출한다.
전략 패턴을 사용하면 로직이 변경되었을 때 유연하게 수정이 가능하다.
interface MovableStrategy {
public void move();
}
class RailRoadtrategy implements MovableStrategy {
public void move() {
System.out.println("선로로 이동");
}
}
class Roadtrategy implements MovableStrategy {
public void move() {
System.out.println("도로로 이동");
}
}
class Transport {
private MovableStrategy movableStrategy; // 컴포지션
public void move() {
movableStrategy.move(); // 실제 구현은 위임
}
public void setMovableStrategy(MovableStrategy movableStrategy) {
this.movableStrategy = movableStrategy;
}
}
class Bus extends Transport {
}
class Train extends Transport {
}
public class Client
{
public static void main(String[] args) {
Transport train = new Train();
Transport bus = new Train();
train.setMovableStrategy(new RailRoadtrategy());
bus.setMovableStrategy(new Roadtrategy());
train.move(); // 선로
bus.move(); // 도로
// 도로로 움직이는 train 개발.
train.setMovableStrategy(new Roadtrategy());
train.move(); // 선로
}
}
composition은 하나의 객체가 다른 객체를 자신의 필드로 포함하고, 그 객체에게 일을 위임(delegation)하여 동작하는 설계 방식이다.
상속이 is-a 관계일 때, composition은 has-a 관계, 즉 'A는 B를 갖고있다' 형태의 구조이다.
전략 패턴을 사용하지 않은 예제 코드에서 교통수단 클래스는 Movable을 상속받고 있다.
전략 패턴을 사용한 코드에서는 Transport 클래스가 MovableStrategy를 상속받는 것이 아닌, 자신의 필드로 포함시켜 기능을 위임하고 있다.
즉 상속이 아닌 composition 구조이다.
(1) 인터페이스
MovableStrategy 인터페이스는 이동 전략이라는 개념을 추상화 했다.
구현체에 상관없이, move()를 실행할 수 있는 계약을 제공한다.
(2) composition
Transport는 특정 이동 전략을 상속받는 것이 아닌, 전략 객체를 내부에 보유(has-a)하고 있다.
이것이 composition 이고 실제 동작을 자신의 전략 객체에 위임(delegation)한다.
(3) 전략 패턴
행동을 외부에서 선택 가능하게 분리하는 패턴.
참고