개발/Java, Kotlin

[디자인패턴] 변화하는 것을 분리하자! (Strategy 전략패턴)

Kangjieun11 2023. 8. 13. 17:34
728x90

 

✅ 개요

 

애플리케이션의 요구사항은 계속해서 변한다.

 

 

 

오리가 수영하고, 소리를 낼 수 있다는 슈퍼클래스를 만들고

오리인형, 오리A, 오리B가 슈퍼클래스<오리>를 상속받도록 했다고 가정해보자.

 

 

 

다음 요구사항이 들어왔다.

 

오리가 날 수 있게 만들어줘

 

 

✍️ 상속을 사용하는것은 좋지 못할 수 있다.

슈퍼클래스<오라>에 Fly()를 추가하는 방법이 있다. 

그러나 오리 인형이 <오리>를 상속받고 있기 때문에 사용할 수 없다.

(오리인형은 수영할 수 있지만, 날 수 없다.)

 

 

✍️ 인터페이스를 사용할 경우는? 

Flyable이라는 인터페이스를 만들면  날아야만 하는 오리들에 대해서만 구현해주면 된다.

그러나 코드 중복을 무시할 수 없고, 날 수 있는 오리의 종류가 100가지가 넘어가게 되면 코드를 전부 고쳐주어야한다.

재사용에 유연한 코드를 작성할 수 없게된다.

 

상속과 인터페이스 모두 적절한 방법이아니다.

 

 

 

어떻게 하면 변경에 유연한 코드를 작성할 수 있을까?

이런 고민들은 선배개발자분들이 이미 다 겪었고, 

나같은 초보 개발자는 디자인 패턴을 배워서 잘 적용하려고 노력하면 된다! ㅎㅎ

 


 

 

✅ 변화하는 것을 분리하자.

 

가장 중요한 기본 디자인 원칙은 다음과 같다.

 

⭐️⭐️⭐️⭐️⭐️
✔️ 바뀌는 부분따로 뽑아 캡슐화한다.
✔️ 나중에 바뀌지 않는 부분에는 영향이 없는 상태로 해당 부분을 고치거나 확장할 수 있다.

 

 

 

1️⃣ 바뀌는 부분은 무엇인가?

 

오리가 날다, 울다 : 문제가 발생할 수 있는 부분이다.

나머지(수영하다)는 문제가 되거나 자주 바뀌지 않는다. 

 

 

이를 구분하는게 가장 먼저 해야할 일이다.

 

 

 

 

2️⃣ 바뀌는 부분에 대해 유연하게 만들어준다 

오리 클래스는 수영하다를 갖고 있다.

오리 인스턴스를 새로 만들 때 오리의 행동을 동적으로 바꿀 수 있게 만든다.

 


1) 나는 행동과 우는 행동을 인터페이스로 만든다.

2) 나는 행동에 대해 필요한 구현체를 생성한다 >>  날개로 날다, 날지 않는다

3) 오리는 나는 행동을 갖게 되고, 어떤 구현체를 갖게 되는지 관심을 두지 않는다.  (SOLID 중 의존관계 역전의 원칙!)

 

👩‍💻 생성자(Constructor)에서 구현체 설정

4) 오리A, 오리B와 같이 실제 오리 인스턴스는 생성될 때 구현체를 결정한다. 

 

 

 

 

 

👩‍💻 세터(Setter)에서 구현체 설정

인형 오리는 날 수 없다. 따라서 FlyNoWay로 생성했다고 가정하자.

 

public class ModelDuck extends Duck {
	public ModelDuck() {
           flyBehavior = new FlyNoWay();
	}
}

 

근데 인형 오리도 날 수 있는 방법이 새로 생겼다!

바로 인형 오리에 전기모터를 달면 인형 오리도 날 수 있다고 한다!!! 

 

 

>>> 요구사항의 변화 : 인형 오리도 날 수 있게 변경할 것.

전기모터는 따로 구매할 수 있는 옵션이 되었고, 기존에 있던 제품들에도 붙힐 수 있다.
 

 

이런 경우  Setter를 이용해주어야한다.

 

 

 

4) 오리는 Setter 메서드를 추가함으로써 오리의 행동을 즉시 바꿀 수 있게 바꾼다.

public class ModelDuck extends Duck {

    //기존 인형오리 생성자
    public ModelDuck() {
        flyBehavior = new FlyNoWay(); //기본적으로는 날 수 없게 생성
    }
    
    //Setter 추가
    public void setFlyBehavior(FlyBehavior fb){
    	flyBehavior = fb;
    }
}

//모터로 날기
public class FlyWithMotor implements FlyBehavior {
    public void fly() {
        System.out.println("모터를 달아서 날 수 있습니다.");
    }
}

 

ModelDuck modelA = new ModelDuck();
modelA.performFly();
//날 수 없음

modelA.setFlyBehavior(new FlyWithMotor());
modelA.performFly();
//모터를 달아서 날 수 있습니다.

 

 

 

이렇게 생성자과 Setter를 통해 적절히 구현체를 설정해주면

변경에 유연한 객체지향적 설계가 가능해진다!

 

 

 


 

References

  • Head First Design Patterns