개발/자바
전략 패턴(Strategy Pattern)과 방문자 패턴(Visitor Pattern)
피터JK
2025. 2. 19. 17:15
728x90
전략 패턴(Strategy Pattern)과 방문자 패턴(Visitor Pattern)
객체의 행위를 유연하게 관리하기 위해 전략 패턴과 방문자 패턴을 사용할 수 있습니다. 두 패턴은 각각 다형성을 활용하여 instanceof 남용을 방지하는 좋은 대안입니다.
1. 전략 패턴 (Strategy Pattern)
개념
- **전략 패턴(Strategy Pattern)**은 행위를 클래스로 캡슐화하여, 알고리즘(전략)을 런타임에 동적으로 변경할 수 있도록 만드는 디자인 패턴입니다.
- instanceof를 사용하여 특정 타입을 검사하고 동작을 수행하는 대신, 행위를 인터페이스로 추출하고, 각각의 행위를 별도 클래스로 구현하여 동적으로 선택할 수 있도록 합니다.
구성 요소
- 전략 인터페이스(Strategy Interface): 공통된 행동을 정의
- 전략 구현 클래스(Concrete Strategy Classes): 각각의 전략을 구체적으로 구현
- 컨텍스트(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)**를 받아서 적절한 동작을 실행하도록 합니다.
구성 요소
- Visitor 인터페이스: 방문할 메서드를 정의
- Concrete Visitor (구체적인 방문자): 방문하는 객체별 동작을 구현
- Element 인터페이스: 방문자를 받아들이는 역할
- 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