플라이 웨이트 패턴은 동일하거나 유사한 객체들 사이에 가능한 많은 데이터를 서로 공유하여 사용하도록 하여 메모리 사용량을 최소화하는 패턴이다. 플라이웨이트 패턴에서는 일부 오브젝트의 상태 정보를 외부 자료 구조에 저장하여 플라이웨이트 오브젝트가 잠깐 동안 사용할 수 있도록 전달한다.
@Getter
@Setter
public class Pizza {
private String name;
private int price;
private Dough dough;
private Sauce sauce;
}
피자가 있다.
이제부터 피자를 아주 많이 만들어서 배송할건데, 만들 때 마다 새 인스턴스로 생성해서 배송하기엔 메모리가 너무 아깝다. 그래서 자주 변하지 않고, 범위가 작은 속성인 도우와 소스를 키값으로 Map에 저장해서 저장되어있는 도우와 소스를 사용하는 피자를 만들때는 똑같은 메모리에 저장해서 재사용해보려고 한다.
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class Ingredient{
private Dough dough;
private Sauce sauce;
}
public enum Dough {
NORMAL,
THIN
}
public enum Sauce {
TOMATO,
CHEESE
}
도우와 소스를 enum으로 만들고 두 클래스를 엮어서 Key값으로 저장하기 위해 Ingredient라는 새 클래스를 만들었다.
@NoArgsConstructor
public class PizzaIngredientFactory {
private static final HashMap<Ingredient, Pizza> flyweightData = new HashMap<>();
public static Pizza getIngredient(Dough dough, Sauce sauce) {
Ingredient ingredient = new Ingredient(dough, sauce);
Pizza pizza = flyweightData.get(ingredient);
if (pizza == null) {
pizza = new Pizza(ingredient);
flyweightData.put(ingredient, pizza);
}
return pizza;
}
}
public class Pizza {
public Pizza(Ingredient ingredient){
this.dough = ingredient.getDough();
this.sauce = ingredient.getSauce();
}
...
}
Dough와 Sauce 값을 받아서 그 두개의 재료만 준비되어있는 피자를 반환하는 PizzaIngredientFactory를 만들어줬다. 그리고 Pizza에 Ingredient로 새 인스턴스를 생성하는 생성자를 만들었다. Ingredient를 키로 Map에서 Pizza를 찾아서 반환하기 때문에 똑같은 Ingredient를 요청하면 똑같은 메모리 주소를 반환한다.
public class App {
public static void main(String[] args) {
Pizza pizza1 = PizzaIngredientFactory.getIngredient(Dough.NORMAL, Sauce.TOMATO);
pizza1.setName("일반 피자");
pizza1.setPrice(10000);
System.out.println("pizza1 = " + pizza1);
Pizza pizza2 = PizzaIngredientFactory.getIngredient(Dough.NORMAL, Sauce.TOMATO);
pizza2.setName("특별한 피자");
pizza2.setPrice(15000);
System.out.println("pizza2 = " + pizza2);
}
}
이렇게 출력해보면 pizza1과 pizza2가 같은 값이 나옴을 알 수 있다.
근데 여기서 이런 생각이 들 수 있다. 같은 메모리를 쓰는데 서로 다른 두개의 변수가 나올 수 있나?
System.out.println(pizza1.getName() + " " + pizza2.getName());
당연히 나올 수 없다. 두개의 이름을 가진 두개의 변수이지만, 메모리를 공유하기 때문에 값은 동일하다. pizza1과 pizza2 변수를 모두 선언한 뒤에 이렇게 출력해보면 특별한 피자
가 두 번 출력된다. 근데 사실 이렇게 단순한 상황에서 플라이 웨이트 패턴을 사용하는건 오히려 메모리를 더 낭비하는 게 될 수도 있다.
public class App {
public static void main(String[] args) {
Pizza pizza = PizzaIngredientFactory.getIngredient(Dough.NORMAL, Sauce.TOMATO);
pizza.setName("일반 피자");
pizza.setPrice(10000);
System.out.println("pizza = " + pizza);
pizza = PizzaIngredientFactory.getIngredient(Dough.NORMAL, Sauce.TOMATO);
pizza.setName("특별한 피자");
pizza.setPrice(15000);
System.out.println("pizza = " + pizza);
}
}
위처럼 사용할 바에는 그냥 이렇게 쓰는게 낫다. 실제로 코드를 짤 때는 저 Pizza 변수를 여러 클래스에서 호출해서 써야하거나, Key값이 되는 enum값에 따라서 결정되는 다른 복잡한 정보가 있을때 플라이웨이트 패턴을 고려할 수 있을 것 같다.
이 링크로 가면 코드를 볼 수 있다.