Fly Weight Pattern
태그 :
- 개념
- - 인스턴스를 가능한 한 공유시켜 쓸데없이 new를 하지 않도록 하는 패턴
I. 의도
대규모의 작은 객체들을 효과적으로 사용하기 위해 공유 개념 도입
본질적 상태(공유 가능)와 부가적 상태(공유 불가)를 구분
정의: 인스턴스를 가능한 한 공유시켜 쓸데없이 new를 하지 않도록 하는 패턴
: 어떤 클래스의 인스턴스 한개만 가지고 여러 개의 "가상 인스턴스"를 제공하고 싶다면 사용한다.
이해: 논리적으로 글자 하나를 객체로서 관리한다고 하면, 어떤 글자가 나타날 때마다 해당하는 객체가 존재해야 함. 이렇게 하지 않고 공유된 풀(POOL)에 존재하는 인스턴스에 참조자를 갖도록 관리하여 글씨 개별로 저장하지 않고, 타입이나 특징이 바뀔 때 인지하여 저장하여 빈도수를 대폭 줄여준다.
II. 활용
- 애플리케이션이 대량의 객체를 사용해야 할 때
- 객체 수가 너무 많아져 저장 비용이 너무 높아질 때
- 대부분의 객체 상태를 부가적인 것으로 만들 수 있을 때
- 애플리케이션이 객체 식별자에 비중을 두지 않는 경우 – 식별자를 둔다는 것은 서로 다른 객체로 구별해야 한다는 것이고 이때는 flyweight 객체를 사용할 수 없다
III. 결과
- 공유해야 하는 인스턴스의 전체 수를 줄일 수 있다
- 객체별 본질적 상태의 양을 줄일 수 있다
- 부가적인 상태는 연산되거나 저장될 수 있다
관련 패턴: Flyweight 패턴은 Composite 패턴과 함께 사용되는 데 이는 논리적으로 계층 구조를 형성하기 위해서 그래프를 이용하기 때문이다. 이때 그래프의 단말 노드가 공유하는 객체이다
State나 Strategy 패턴을 Flyweight로 구현할 수 있다
- Flyweight: Flyweight 가 받아들일 수 있고, 부가적 상태에서 동작해야 하는 인터페이스를 선언하고 있다.
- ConcreteFlyweight: Flyweight 인터페이스를 구현하고 내부적으로 갖고 있어야 하는 본질적 상태에 대한 저장소를 정의하고 있다. ConcreteFlyweight 객체는 공유할 수 있는 것이어야 한다. 그러므로 관리하는 어떤 상태라도 본질적인 것이어야 한다.
- UnsharedConcreteFlyweight: 모든 Flyweight 서브클래스들이 공유될 필요는 없다. Flyweight 인터페이스는 공유를 가능하게 하지만 그것을 강요해서는 안 된다. UnsharedConcreteFlyweight 객체가 ConcreteFlyweight 객체를 자신의 자식으로 갖는 것은 흔한 일이다.
- FlyweightFactory: Flyweight 객체를 생성하고 관리한다. Flyweight 가 적절히 공유되도록 보장해야 한다. 클라이언트가 Flyweight 를 요청하면 FlyweightFactory 객체는 이미 존재하는 인스턴스를 제공하거나, 만약 존재하지 않는다면 생성해야 한다.
- Client: Flyweight 에 대한 참조자를 관리하고 Flyweight 의 부가적 상태를 저장한다.
소스코드
// Flyweight object
public interface CoffeeOrder {
public void serveCoffee(CoffeeOrderContext context);
}
// ConcreteFlyweight object that creates ConcreteFlyweight
public class CoffeeFlavor implements CoffeeOrder {
private String flavor;
public CoffeeFlavor(String newFlavor) {
flavor = newFlavor;
}
public String getFlavor() {
return this.flavor;
}
public void serveCoffee(CoffeeOrderContext context) {
System.out.println("Serving Coffee flavor " + flavor + " to table number " + context.getTable());
}
}
public class CoffeeOrderContext {
private int tableNumber;
public CoffeeOrderContext(int tableNumber) {
this.tableNumber = tableNumber;
}
public int getTable() {
return this.tableNumber;
}
}
public class CoffeeOrderContext {
private int tableNumber;
public CoffeeOrderContext(int tableNumber) {
this.tableNumber = tableNumber;
}
public int getTable() {
return this.tableNumber;
}
}
public class TestFlyweight {
/** The flavors ordered. */
private static CoffeeFlavor[] flavors = new CoffeeFlavor[100];
/** The tables for the orders. */
private static CoffeeOrderContext[] tables = new CoffeeOrderContext[100];
private static int ordersMade = 0;
private static CoffeeFlavorFactory flavorFactory;
public static void takeOrders(String flavorIn, int table) {
flavors[ordersMade] = flavorFactory.getCoffeeFlavor(flavorIn);
tables[ordersMade++] = new CoffeeOrderContext(table);
}
public static void main(String[] args) {
flavorFactory = new CoffeeFlavorFactory();
takeOrders("Cappuccino", 2);
takeOrders("Cappuccino", 2);
takeOrders("Frappe", 1);
takeOrders("Frappe", 1);
takeOrders("Xpresso", 1);
takeOrders("Frappe", 897);
takeOrders("Cappuccino", 97);
takeOrders("Cappuccino", 97);
takeOrders("Frappe", 3);
takeOrders("Xpresso", 3);
takeOrders("Cappuccino", 3);
takeOrders("Xpresso", 96);
takeOrders("Frappe", 552);
takeOrders("Cappuccino", 121);
takeOrders("Xpresso", 121);
for (int i = 0; i < ordersMade; ++i) {
flavors[i].serveCoffee(tables[i]);
}
System.out.println(" ");
System.out.println("total CoffeeFlavor objects made: " + flavorFactory.getTotalCoffeeFlavorsMade());
}
}