Strategy Pattern

강한친구·2022년 4월 1일
0

OOP Desing Pattern

목록 보기
3/15

전략 패턴?

알고리즘군을 정의하고 각각을 캡슐화하여 교환, 사용할 수 있게 만들어준다. 클라이언트와는 독립적으로 알고리즘 변경이 가능하다.

쉽게 말해서 클라이언트는 그냥 가져다 쓰기만하고 개발자들이 알아서 캡슐화시켜놓고 그걸 바꿔끼면 된다는 뜻이다.

말이 어려우니 예제로 보자.

Duck

오리라는 클래스를 만든다고 하자.

조건은
1. 소리를 낸다. Fly
2. 나는 방식이 다르다(못나는 놈도 있다). Quack

가 조건이다.

우선 Fly, Quack 인터페이스를 만들고, 그 인터페이스를 이용하여 각각의 Fly, Quack 새부기능을 구현해야한다.

Interface

package duck;

public interface FlyBehavior {
    public void fly();
}

Interface를 받은 날개로 나는 클래스

package duck;

public class FlyWithWings implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("Flying with Wings!\n");
    }
}

못나는 클래스

package duck;

public class FlyNoWay implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("This thing cannot fly!\n");
    }
}

소리 인터페이스

package duck;

public interface QuackBehavior {
    public void quack();
}

기본 소리

package duck;

public class Quack implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("Quack!\n");
    }
}

삑소리

package duck;

public class Squeak implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("Squeak");
    }
}

이런식으로 두개의 캡슐화된 객체들을 만들어냈다.
그리고 이를 총괄하는 Duck abstract class를 만들어야 한다.

Duck abstract class

Duck에는 Fly를 정해주고, Quack을 정해주는 함수가 정의되어야한다.

package duck;

public abstract class Duck {
    public Duck() {
    }
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;

    public abstract void Display();

    public void performFly() {
        flyBehavior.fly();
    }
    public void performQuack() {
        quackBehavior.quack();
    }
    public void swim() {
        System.out.println("Every Duck floats. Even fake ones do");
    }

}

performFly, performQuack를 통해 interface에 있는 fly(), quack()와 클래스의 fly, quack을 연결시켜 주는것이다.

이를 상속한 코드를 알아보자

package duck;

public class MallardDuck extends Duck{
    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }

    @Override
    public void Display() {
        System.out.println("I'm Mallard Duck\n");

    }
}

이 클래스는 quackBehavior를 Quack로, flyBehavior를 FlyWithWing으로 정의하고 있다.

실행

package duck;

public class DuckSim {
    public static void main(String[] args) {
        Duck mallard = new MallardDuck();
        mallard.Display();
        mallard.performQuack();
        mallard.performFly();
    }
}

이를 실행시키는 코드이다.

mallard라는 새로운 객체를 생성하고
performQuack함수를 호출, quackBehavior.quack()을 사용한다. 이때, mallard 클래스 내부에서 quackBehavior를 Quack() 클래스로 지정하였기에, 이 는 Quack.Quack()가 된다. 따라서 Quack이 출력되는것이다.

setter

다만 이런방식을 사용하면 동작을 바꾸려면, 객체의 원본코드를 들어내서 고쳐야한다. 이는 동적이지 않다.

비슷하게 만든 ModelDuck코드를 보자.

package duck;

public class ModelDuck extends Duck{
    public ModelDuck() {
        flyBehavior = new FlyNoWay();
        quackBehavior = new Quack();
    }
    @Override
    public void Display() {
        System.out.println("I'm a model duck");
    }
}

이 객체는 날지못하고, 기본 울음소리를 가지고 있다.

이를 실행하면 예상한대로 나올것이다.
그러면 이제 Duck 클래스에 setter를 추가하여, 동적변경이 가능하게 해줘야한다.

package duck;

public abstract class Duck {
    public Duck() {
    }
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;

    public void setFlyBehavior(FlyBehavior fb) {
        this.flyBehavior = fb;
    }
    public void setQuackBehavior(QuackBehavior qb) {
        this.quackBehavior = qb;
    }

    public abstract void Display();

    public void performFly() {
        flyBehavior.fly();
    }
    public void performQuack() {
        quackBehavior.quack();
    }
    public void swim() {
        System.out.println("Every Duck floats. Even fake ones do");
    }

}

보이는것처럼, setFlyBehavior를 통해 새로운 객체를 인자로 받는 함수를 만들었다. 이 함수는 객체를 인자로 받아 해당 객체를 자신의 FlyBehavior로 삼는다.

실행

package duck;

public class DuckSim {
    public static void main(String[] args) {
        Duck mallard = new MallardDuck();
        mallard.Display();
        mallard.performQuack();
        mallard.performFly();

        Duck model = new ModelDuck();
        model.performFly();
        model.setFlyBehavior(new FlyRocketPowered());
        model.performFly();
        model.performQuack();

        Caller c = new Caller();
        c.quack();
    }
}
This thing cannot fly!

Powered By Rocket Engine
Quack!

This is for calling ducks

출력결과는 다음과 같다.
우선 performFly를 통해 기본적으로 설정된 cannot fly가 나온다.
그 후, setter를 통해 일시적으로 flyBehavior를 Rocket을 바꾼것이다. 그리고 다시 performFly를 하면, 바뀐 Rocket이 출력되는것이다.

RPG Game Class

이번에는 무기를 바꿀 수 있는 RPG게임 클래스를 구현해보겠다.

무기변경 인터페이스

package rpg;

public interface WeaponBehav {
    public void useWeapon();
}

무기 클래스 (Axe, Bow, Spear, Sword)

package rpg;

public class Bow implements WeaponBehav{
    @Override
    public void useWeapon() {
        System.out.println("Bow Actions");
    }
}

Charachter 클래스 (이번에는 추상으로 안만들었다)

package rpg;

public class Charachter {
    public Charachter() {
    }
    WeaponBehav weapon;

    public void showWeapon() {
        weapon.useWeapon();
    }

    public void setWeapon(WeaponBehav weapon) {
        this.weapon = weapon;
    }

    public void fight() {
    }
}

king 구현 클래스

package rpg;

public class King extends Charachter{
    public King() {
        weapon = new Spear();
    }
    @Override
    public void fight() {
        System.out.println("King is fighting!");
    }

}

Queen 구현 클래스

package rpg;

public class Queen extends Charachter{
    public Queen() {
        weapon = new Axe();
    }

    @Override
    public void fight() {
        System.out.println("Queen is Fighting");
    }
}

캐릭터 구현 클래스

캐릭터 클래스에서 WeaponBehav 클래스는 weapon 이라는 변수명을 가진다.
따라서 이 변수명을 각 캐릭터 구현 클래스에서 사용하면 된다.

실행

package rpg;

public class RPGSim {
    public static void main(String[] args) {
        King king = new King();
        Queen queen = new Queen();

        queen.showWeapon();
        queen.fight();
        queen.setWeapon(new Bow());
        queen.showWeapon();


        king.showWeapon();
        king.fight();
    }
}
Axe
Queen is Fighting
Bow Actions
Spear Actions
King is fighting!

보이는것처럼, queen은 최초로 자신이 설정한 무기인 도끼를 사용했다.
그 후 setter를 이용하여 new Bow() 객체를 weapon으로 설정하였고
그 결과 weapon이 바뀐것을 볼 수 있다.

결론

객체지향의 아주 기본이지만, 핵심이 되는 디자인 패턴을 학습하였다.

일단 패턴도 패턴이지만, 객체를 이용한 프로그래밍에 좀 더 익숙해질 필요가 있다고 생각한다.

0개의 댓글