개발/자바

전략 패턴(Strategy Pattern)과 방문자 패턴(Visitor Pattern)

피터JK 2025. 2. 19. 17:15
728x90

전략 패턴(Strategy Pattern)과 방문자 패턴(Visitor Pattern)

객체의 행위를 유연하게 관리하기 위해 전략 패턴방문자 패턴을 사용할 수 있습니다. 두 패턴은 각각 다형성을 활용하여 instanceof 남용을 방지하는 좋은 대안입니다.


1. 전략 패턴 (Strategy Pattern)

개념

  • **전략 패턴(Strategy Pattern)**은 행위를 클래스로 캡슐화하여, 알고리즘(전략)을 런타임에 동적으로 변경할 수 있도록 만드는 디자인 패턴입니다.
  • instanceof를 사용하여 특정 타입을 검사하고 동작을 수행하는 대신, 행위를 인터페이스로 추출하고, 각각의 행위를 별도 클래스로 구현하여 동적으로 선택할 수 있도록 합니다.

구성 요소

  1. 전략 인터페이스(Strategy Interface): 공통된 행동을 정의
  2. 전략 구현 클래스(Concrete Strategy Classes): 각각의 전략을 구체적으로 구현
  3. 컨텍스트(Context Class): 전략 객체를 유지하고 실행

예제 코드

문제 상황: 동물마다 다른 소리를 내는 경우 (instanceof 남용)

class Animal {
    void makeSound() {
        System.out.println("Some generic sound");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Bark");
    }
}

class Cat extends Animal {
    @Override
    void makeSound() {
        System.out.println("Meow");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();

        // instanceof 사용 (좋지 않은 방법)
        if (animal instanceof Dog) {
            System.out.println("Bark");
        } else if (animal instanceof Cat) {
            System.out.println("Meow");
        }
    }
}

위처럼 instanceof를 사용하면 새로운 동물이 추가될 때마다 if-else 문을 수정해야 합니다.


전략 패턴 적용

// 1. 전략 인터페이스 정의
interface SoundStrategy {
    void makeSound();
}

// 2. 전략 구현 클래스 (각각의 전략)
class BarkSound implements SoundStrategy {
    public void makeSound() {
        System.out.println("Bark");
    }
}

class MeowSound implements SoundStrategy {
    public void makeSound() {
        System.out.println("Meow");
    }
}

// 3. 컨텍스트 클래스 (동물 클래스)
class Animal {
    private SoundStrategy soundStrategy;

    // 생성자 주입
    public Animal(SoundStrategy soundStrategy) {
        this.soundStrategy = soundStrategy;
    }

    // 전략 실행
    public void performSound() {
        soundStrategy.makeSound();
    }
}

// 4. 실행 코드
public class Main {
    public static void main(String[] args) {
        Animal dog = new Animal(new BarkSound());
        Animal cat = new Animal(new MeowSound());

        dog.performSound();  // 출력: Bark
        cat.performSound();  // 출력: Meow
    }
}

전략 패턴을 적용하면 instanceof 없이도 유연하게 동작을 추가할 수 있습니다.
새로운 동물(예: Lion)이 추가되더라도 기존 코드 수정 없이 SoundStrategy 구현 클래스만 만들면 됩니다.


2. 방문자 패턴 (Visitor Pattern)

개념

  • **방문자 패턴(Visitor Pattern)**은 객체 구조에서 실행될 알고리즘을 분리하여, 새로운 동작을 쉽게 추가할 수 있도록 하는 패턴입니다.
  • instanceof를 사용하여 객체 타입을 검사하지 않고, 객체가 **방문자(Visitor)**를 받아서 적절한 동작을 실행하도록 합니다.

구성 요소

  1. Visitor 인터페이스: 방문할 메서드를 정의
  2. Concrete Visitor (구체적인 방문자): 방문하는 객체별 동작을 구현
  3. Element 인터페이스: 방문자를 받아들이는 역할
  4. Concrete Element (구체적인 요소): 실제 객체

예제 코드

문제 상황: 다양한 동물의 행동을 처리 (instanceof 남용)

class Animal {}

class Dog extends Animal {
    void bark() {
        System.out.println("Bark");
    }
}

class Cat extends Animal {
    void meow() {
        System.out.println("Meow");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();

        // instanceof 사용 (좋지 않은 방법)
        if (animal instanceof Dog) {
            ((Dog) animal).bark();
        } else if (animal instanceof Cat) {
            ((Cat) animal).meow();
        }
    }
}

✅ instanceof를 사용하여 타입을 구분하면, 동물 종류가 늘어날수록 코드 수정이 필요하게 됩니다.


방문자 패턴 적용

// 1. Visitor 인터페이스 정의
interface AnimalVisitor {
    void visit(Dog dog);
    void visit(Cat cat);
}

// 2. Element 인터페이스 (동물이 방문자를 받아들이도록 함)
interface Animal {
    void accept(AnimalVisitor visitor);
}

// 3. 구체적인 동물 클래스 (각 동물이 방문자를 받아들임)
class Dog implements Animal {
    public void accept(AnimalVisitor visitor) {
        visitor.visit(this);
    }

    public void bark() {
        System.out.println("Bark");
    }
}

class Cat implements Animal {
    public void accept(AnimalVisitor visitor) {
        visitor.visit(this);
    }

    public void meow() {
        System.out.println("Meow");
    }
}

// 4. 방문자 구현 클래스
class SoundVisitor implements AnimalVisitor {
    public void visit(Dog dog) {
        dog.bark();
    }

    public void visit(Cat cat) {
        cat.meow();
    }
}

// 5. 실행 코드
public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();

        AnimalVisitor soundVisitor = new SoundVisitor();

        dog.accept(soundVisitor);  // 출력: Bark
        cat.accept(soundVisitor);  // 출력: Meow
    }
}

방문자 패턴을 사용하면 instanceof 없이 새로운 동작(예: EatVisitor, SleepVisitor)을 쉽게 추가할 수 있습니다.
즉, 기존 클래스 수정 없이 방문자 클래스만 추가하면 됩니다.


비교 및 선택 기준

패턴 개념 언제 사용하면 좋은가?
전략 패턴 (Strategy Pattern) 알고리즘(행위)을 캡슐화하고 실행 시 동적으로 변경 행위를 교체할 필요가 있을 때 (예: 정렬, 인코딩, UI 테마 변경 등)
방문자 패턴 (Visitor Pattern) 객체 구조를 변경하지 않고 새로운 동작을 추가 새로운 기능을 자주 추가해야 하지만 기존 클래스를 수정할 수 없을 때 (예: XML 파서, AST 트래버싱 등)

결론

  • instanceof를 남용하면 유지보수가 어려워지고, 코드가 복잡해질 수 있습니다.
  • 전략 패턴은 행위를 동적으로 변경할 필요가 있을 때 유용합니다.
  • 방문자 패턴은 객체 구조가 변경될 가능성이 낮고, 새로운 동작을 쉽게 추가해야 할 때 적합합니다.

둘 중 어떤 패턴을 선택할지는 상황에 따라 다릅니다!
💡 전략 패턴은 알고리즘을 교체할 때,
💡 방문자 패턴은 새로운 동작을 추가할 때 유용합니다.

728x90