SoftwareDesign

2. Strategy

날아갔나요 2020. 1. 7. 01:03

 

Github : https://github.com/junghun9102/DesignPatternTemplate

branch : feature/strategy

 

junghun9102/DesignPatternTemplate

Contribute to junghun9102/DesignPatternTemplate development by creating an account on GitHub.

github.com

 

이번에는 Strategy 패턴이다.

Strategy는 직역하면 전략인데 어떤 패턴일까?

 

다양한 오리들을 가지고 있는 시스템을 만들고 있다.

청둥오리, 고무오리, 미끼용 오리 등 다양한 오리들을 만들어보려고 한다.

 


Commit 4ee19cf6

 

abstract class Duck {

    open fun quack() {
        println("quack! quack!")
    }

    fun swim() {
        println("swimming")
    }

    open fun fly() {
        println("I believe i can fly")
    }

    abstract fun display()

}
class MallardDuck : Duck() {
    override fun display() {
        println("It seems like MallardDuck")
    }
}

 

대부분의 오리들은 "꿱"하고 소리를 낼 것이며 수영을 할 것이고 날 것이다.

단 모두 다르게 생겼기 때문에 display만 추상 메소드로 만들었다.

 

모든 오리들이 위의 형태를 만족하면 좋았겠지만 그렇지 않은 오리가 발생했다.

 

고무 오리는 소리를 낼 수 있고 수영할 수 있지만 날 수 없다.

미끼용 나무 오리(DecoyDuck)는 소리를 낼 수 없고 날 수 없다.

 

 

 

class RubberDuck : Duck() {
    override fun display() {
        println("It seems like RubberDuck")
    }

    override fun fly() {

    }
}
class DecoyDuck : Duck() {
    override fun display() {
        println("It seems like DecoyDuck")
    }

    override fun quack() {

    }

    override fun fly() {

    }
}

 

고무오리는 날 수 없기 때문에 fly 메소드를 아무행동을 하지 않도록 오버라이딩했고

미끼용 나무 오리는 quack, fly 메소드를 빈 몸통으로 오버라이딩했다.

일단은 원하는데로는 동작한다.

 

하지만 날지못하는 오리가 더 늘어난다면 날지 못하도록 모두 오버라이딩해야하고 중복코드가 발생한다. 

"날지못한다"처럼 몸통을 비워 처리해서 중복코드가 많이 없어 보이지만

많약 예외 케이스의 코드양이 많다면 중복코드 양이 늘어날 것이다.

 

또한 오버라이딩을 통해 많은 서브클래스에서 처리한다면 신경쓸 범위가 늘어나는 것이다.

 

변경은 어쩔 수 없이 일어날 수 밖에 없다.

어떻게 해야 그 변경을 잘 받아들일 수 없는 구조를 만들 수 있을까?

 

 

 


Commit cf0b9c56

 

 

문제를 해결하기 위해서는 변경이 일어나는 부분과 그렇지 않을 부분을 구분하는 것에서 시작한다.

 

변경이 일어나는 부분 : quack, fly

변경이 일어나지 않는 부분 : swim, display

 

1. 변경이 일어나는 코드를 각각 캡슐화 한다.

2. 인터페이스를 만들고 이를 상속해 클래스 집합을 만든다.

3. 그리고 구성을 통해 가지고 있으며 변경이 일어나는 메소드에서 사용한다.

4. 하위 클래스에서는 생성자에서 초기화하거나 중간에 동적으로 변경하거나 유연하게 사용할 수 있다.

 

Class Diagram

 

abstract class Duck {

    lateinit var quackBehavior: QuackBehavior
    lateinit var flyBehavior: FlyBehavior

    fun quack() {
        quackBehavior.quack()
    }

    fun fly() {
        flyBehavior.fly()
    }

    fun swim() {
        println("swimming")
    }

    abstract fun display()

}

부모 클래스는 이런식으로 수정이 되었다.

 

interface FlyBehavior {
    fun fly()
}
class FlyWithWings : FlyBehavior {
    override fun fly() {
        println("fly with wings")
    }
}
class FlyNoWay : FlyBehavior {
    override fun fly() {

    }
}

하나의 클래스 집합

 

class MallardDuck : Duck() {

    init {
        quackBehavior = Quack()
        flyBehavior = FlyWithWings()
    }

    override fun display() {
        println("It seems like MallardDuck")
    }
}
class DecoyDuck : Duck() {

    init {
        quackBehavior = MuteQuack()
        flyBehavior = FlyNoWay()
    }

    override fun display() {
        println("It seems like DecoyDuck")
    }
}

초기화하여 사용한다.

 

 

기능이 추가되어도 Interface를 상속받아 만들어 사용할 수 있다.

그 기능을 여러 곳에서 사용하여도 중복코드없이 구성할 수 있다.

 

또한 알고리즘이 변경이 되어도 사용하는 부분에 영향없이 수정이 가능한 장점이 있다.

 

 


스트래티지 패턴

알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다.

스트래티지를 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.