본문 바로가기
Java/Spring

전략패턴) Strategy Pattern - Context, Strategy, Concrete Strategy

by 박채니 2022. 8. 4.

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


전략패턴 (Strategy Pattern)

- GoF의 디자인 패턴에 소개된 행위 패턴 중 하나

 

만일 철수가 강아지를 키우고, 영희가 고양이를 키운다고 하였을 때, Person클래스가 존재하고 주인 이름 / 펫 종류를 필드로 갖게 될 것 입니다.

각 반려동물에 맞는 Dog 클래스와 Cat 클래스가 존재할 것이고, Person 클래스의 필드로 자리잡게 되겠죠.

그렇다면 Person has a Dog / Person has a Cat → 즉 Person은 Cat, Dog에 의존하게 됩니다.

만일 강아지만 키운다고 하였을 때, Cat 클래스는 null이 될 것이고 Snake를 키우는 맹구가 등장한다면 Person클래스에 Snake필드를 추가해줘야할 것이고, 이는 비효율적입니다.

Pet클래스(추상클래스 혹은 인터페이스)를 하나 생성하여 각각의 Dog, Cat, ...이 Pet클래스를 상속받는다면 효율적으로 관리할 수 있을겁니다.

 


Person

public class Person {
	private String name; // 주인이름
	private Pet pet;
	
	public Person() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Person(String name, Pet pet) {
		super();
		this.name = name;
		this.pet = pet;
	}

// getter, setter 생략

	@Override
	public String toString() {
		return "Person [name=" + name + ", pet=" + pet + "]";
	}
}

 

Pet

public abstract class Pet {
	protected String name; // 반려동물 이름

	public Pet() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Pet(String name) {
		super();
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

 

Dog

public class Dog extends Pet {

	public Dog() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Dog(String name) {
		super(name);
		// TODO Auto-generated constructor stub
	}

	@Override
	public String toString() {
		return "Dog [name=" + name + "]";
	}
}

 

Cat

public class Cat extends Pet {

	public Cat() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Cat(String name) {
		super(name);
		// TODO Auto-generated constructor stub
	}

	@Override
	public String toString() {
		return "Cat [name=" + name + "]";
	}
}

- Dog, Cat 클래스는 Pet클래스를 상속하므로, String name 필드가 포함되어있으며 생성자는 상속이 불가하여 선언해줌!

 

Main

public class Main {
	public static void main(String[] args) {
		Person p1 = new Person("철수", new Dog("구리구리"));
		Person p2 = new Person("영희", new Cat("두리두리"));
		
		System.out.println(p1);
		System.out.println(p2);
	}
}

@콘솔출력값
Person [name=철수, pet=Dog [name=구리구리]]
Person [name=영희, pet=Cat [name=두리두리]]

이처럼, 이 외 클래스들이 추가되어도 Person 클래스를 수정할 필요 없이 Pet클래스를 상속받아 사용하면 됩니다.

 

 

Context

- Person

- Strategy를 이용하는 클래스

- 필요에 따라 구체적인 전략 (Dog, Cat)을 바꿔서 사용할 수 있어야 함 (직접 의존하고 있다면, Person클래스가 같이 수정되어야함)

 

Strategy

- Pet

- 인터페이스/추상클래스

- Context가 의존하는 타입으로 자식 클래스에 대해 동일한 규격 제공

 

Concrete Strategy 

- Dog/Cat

- Strategy 클래스의 구현체

- Strategy를 수행하는 주체

구조

 

Snake 반려동물 추가

Snake

public class Snake extends Pet {

	public Snake() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Snake(String name) {
		super(name);
		// TODO Auto-generated constructor stub
	}

	@Override
	public String toString() {
		return "Snake [name=" + name + "]";
	}
}

 

Main

public class Main {
	public static void main(String[] args) {
		Person p1 = new Person("철수", new Dog("구리구리"));
		Person p2 = new Person("영희", new Cat("두리두리"));
		
		System.out.println(p1);
		System.out.println(p2);
		
		// Person이 가질 수 있는 반려동물 Class 이후에 추가될 수 있음
		Person p3 = new Person("맹구", new Snake("쉬리릭"));
		System.out.println(p3);
	}
}

@콘솔출력값
Person [name=맹구, pet=Snake [name=쉬리릭]]

이렇듯, Context가 구체적인 전략에 의존하지 않고 전략 클래스에만 의존하고 있기 때문에 Pet의 자식 타입을 200개 만들어도 Person은 변경되지 않게 됩니다.

 

자식클래스에서 toString() 중복 제거

Person클래스 toString()

@Override
public String toString() {
    String className = this.getClass().getName();
    return className.substring(className.lastIndexOf(".")+1) + " [name=" + name + "]";
}

@콘솔출력값
Person [name=철수, pet=Dog asf[name=구리구리]]
Person [name=영희, pet=Cat asf[name=두리두리]]
Person [name=맹구, pet=Snake asf[name=쉬리릭]]