
소프트웨어 개발을 하면서 "변경"은 피할 수 없는 요소입니다. 유지보수가 쉽고, 확장성이 좋은 코드를 작성하려면 "유연한 설계"가 필수적이죠. Head First Design Patterns의 첫 번째 장에서는 바로 이러한 "변경"에 대비하는 방법을 다루고 있으며, 이를 통해 객체지향 설계 원칙과 전략 패턴(Strategy Pattern)을 소개합니다.
소프트웨어 설계에서 공통적으로 발생하는 문제들을 해결하는 방법을 정리한 것이 디자인 패턴입니다. 디자인 패턴을 사용하면 코드의 재사용성을 높이고, 유지보수성을 향상시키며, 변경에 유연하게 대응할 수 있습니다.

책에서는 Duck 시뮬레이션 예제를 사용하여 디자인 패턴이 필요한 이유를 설명합니다. 초기 구현에서는 Duck 클래스와 fly() 및 quack() 같은 행동 메서드를 포함하고 있었는데, 점점 다양한 종류의 오리가 추가되면서 코드의 문제점이 드러납니다.
예를 들어 다 같은 오리 형상이지만 고무 오리는 날 수 없고 "꽥-" 하고 우는 대신 "삑-" 소리를 내야합니다. 장식용 오리는 날 수 없고 아무 소리도 내지 않습니다.
처음에는 Duck 클래스를 만들고 MallardDuck, RedheadDuck 같은 구체적인 클래스를 extends Duck로 상속하는 방식으로 구현했습니다. 그러나 이 설계에는 몇 가지 문제가 있습니다.

fly()와 quack()을 가져야 하지만, 어떤 오리는 날지 못하거나 다른 방식으로 울어야 합니다. (고무 오리는 날 수 없고 "꽥-" 하고 우는 대신 "삑-" 하고 울어야합니다.)fly() 메서드에 변경 사항이 생기면 MallardDuck, RedheadDuck 등 여러 클래스를 동시에 수정해야 합니다.이러한 문제를 해결하기 위해 행동(Behavior)을 별도의 클래스로 분리하는 전략 패턴을 적용합니다.
전략 패턴(Strategy Pattern)은 알고리즘(행동)을 인터페이스로 추상화하고, 런타임에 필요에 따라 변경할 수 있도록 만드는 패턴입니다.
이 패턴을 적용하면, fly()와 quack()을 오리 클래스에서 제거하고, 대신 인터페이스를 통해 행동을 캡슐화하여 관리합니다.
FlyBehavior 인터페이스와 QuackBehavior 인터페이스를 생성FlyWithWings, FlyNoWay 같은 구현체를 만들고 FlyBehavior를 구현Quack, MuteQuack, Squeak 같은 구현체를 만들고 QuackBehavior를 구현Duck 클래스에서 FlyBehavior와 QuackBehavior를 구성(composition)으로 포함
// 날 수 있는 행동 인터페이스
public interface FlyBehavior {
void fly();
}
// 날 수 있는 구현체
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("날개를 사용해 납니다!");
}
}
// 날지 못하는 구현체
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("나는 날지 못해요...");
}
}
// 오리 클래스에서 행동을 위임
public abstract class Duck {
FlyBehavior flyBehavior;
public void performFly() {
flyBehavior.fly();
}
public void setFlyBehavior(FlyBehavior fb) {
this.flyBehavior = fb;
}
}
MallardDuck이 날 수 없다가 다시 날 수 있도록 동적으로 변경할 수 있습니다.Duck mallard = new MallardDuck();
mallard.setFlyBehavior(new FlyNoWay()); // 날지 못하도록 설정
mallard.performFly();
Chapter 1에서는 "변경"에 유연하게 대응하는 객체지향 설계 원칙을 배우고, 이를 해결하기 위해 전략 패턴(Strategy Pattern)을 활용하는 방법을 살펴보았습니다. 전략 패턴을 적용하면 행동을 인터페이스로 분리하여 코드의 변경을 최소화하고 유지보수를 쉽게 할 수 있다는 점이 핵심입니다.
전략 패턴을 사용하면 유지보수성과 확장성이 뛰어난 코드 작성이 가능하며, OCP 원칙을 지키는 설계를 할 수 있습니다. 이후 챕터에서는 다른 디자인 패턴들도 다루며, 좀 더 유연하고 확장성 있는 설계를 만들어나가는 방법을익힐 수 있습니다.