본문 바로가기

코드 디자인 패턴/java

[java & 디자인 패턴] flyweight 패턴 학습 내용 정리

 

주제
플라이웨이트 패턴의 개념
자바에서 기본적인 플라이 웨이트 패턴 구현 사례
직접 구현 예제

 

 

개념


플라이웨이트 패턴은 인스턴스의 불필요한 중복 생성을 제한하고 전역에서 인스턴스를 공유함으로써 메모리 절약과 성능 향상을 목적으로 한다.

이를 위해서 객체를 "인스턴스마다 고유한 부분"과 "다른 인스턴스와 동일한 부분"으로 구분하고 후자를 별도의 객체로 만들어 공유하는 방식이다. 이 공유 객체를 플라이웨이트라 부른다.

 

싱글톤 패턴과의 차이점

  • 인스턴스 개수
    • 싱글 톤 패턴 | 인스턴스의 개수 자체를 1개 혹은 n개로 제한
    • 플라이 웨이트 패턴 | 인스턴스의 생성을 제한하지 않되. 중복되는 데이터를 공유 객체로 분리하여 관리
  • 변형(mutate)
    • 싱글 톤 패턴 | 공유하는 객체를 변형 가능 (mutable)
    • 플라이 웨이트 패턴 | 공유하는 객체의 변형 불가 (immutable)

예시


게임 개발자로서 유저가 볼 수 있는 나무의 클래스를 만든다고 가정하자.

 

객체 내 상태 규정

나무는 위치, 높이, 두께, 텍스처, 이벤트의 속성을 가진다.

 

비반복(가변적) 상태 탐색

위치, 높이, 두께는 인스턴스마다 상이한 값을 가진다.

 

반복(불변적) 상태 탐색

텍스처, 이벤트는 인스턴스 간에 동일한 데이터를 가질 것 같다.

이 경우에는 트리 클래스는 반복 상태를 트리 모델 클래스라는 타입으로 스태틱 변수로 가지고 공유할 수 있다.

 

자바에서 볼 수 있는 플라이 웨이트 패턴 사례

  • String 타입 객체
    • 문자열 리터럴(따옴표)로 생성 시, 풀(pool)로 관리
    • 동일한 값의 데이터를 생성하면 동일한 문자열 객체를 참조하여 재사용
package case1.step1;

public class TestPattern {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String str0 = "홍길동";
		String str1 = new String("홍길동");
		String str2 = new String("홍길동");
		String str3 = "홍길동";
		String str4 = "홍길동";

// 디버그 모드 breakpoint 설정
// str0, str3, str4 는 문자열 템플릿 리터럴로 생성되었으며 동일 데이터로 동일 참조값
		System.out.println("패턴");

	}

}

 

구현


팩토리, 풀

flyweight 패턴의 내용 중 하나는 동일한 데이터를 참조할 때는 객체를 생성하지 않고 재활용한다는 것이다.

이는 다음같이, 데이터 클래스와 팩토리 클래스로 가능하다.

// 불변성 데이터, 공유할 대상
public class Subject {
	private String name;

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

}

// 객체의 생성 및 재사용을 관리할 팩토리
public class SubjectFactory {
	private static Map<String, Subject> subjects = new HashMap<String, Subject>();

	public Subject getSubject(String name) {
		Subject subject = subjects.get(name);

		if (subject == null) {
			subject = new Subject(name);
			subjects.put(name, subject);
		}
		return subject;
	}
}

//클라이언트의 사용
// a,a2는 동일한 데이터 참조
// b,b2는 동일한 데이터 참조
public class Client {
public static void main(String[] args) {
	Subject a=  SubjectFactory.getSubject("a");
	Subject a2= SubjectFactory.getSubject("a");
	Subject b= SubjectFactory.getSubject("b");
	Subject b2= SubjectFactory.getSubject("b");
	
	System.out.println("g");
}
}

 

불변성 데이터,  가변성 데이터 분리

특정 상황에서는 많은 객체 인스턴스를 생성해야 한다. 이럴 때, 공유가능한 불변성 데이터는 플라이웨이트 데이터를 적용해서 별도의 클래스로 분리할 수 있다. 그러면 메모리를 절약할 수 있다.


앞서 본 트리 객체를 만드는 예시에 적용해보자.

package case2.prac2;

import java.util.HashMap;
import java.util.Map;

// 공유할 불변성 데이터
public class TreeModel {
    private String type;
    private String texture;

    public TreeModel(String type, String texture) {
        this.type = type;
        this.texture = texture;
    }

    // Getters for type and texture (if needed)
    public String getType() {
        return type;
    }

    public String getTexture() {
        return texture;
    }
}

// 불변성 데이터의 생성 및 재사용을 관리
public class TreeModelFactory {
    private static Map<String, TreeModel> map = new HashMap<>();

    public static TreeModel getTreeModel(String type) {
        TreeModel model = map.get(type);

        if (model == null) {
            model = new TreeModel(type, type);
            map.put(type, model);
        }

        return model;
    }

    public static int getTreeModelCount() {
        return map.size();
    }
}

// 가변성 데이터를 담는 인스턴스 생성
public class Tree {
    private String x;
    private String y;
    private TreeModel treeModel;

    public Tree(String x, String y, String type) {
        this.x = x;
        this.y = y;
        this.treeModel = TreeModelFactory.getTreeModel(type);
    }

    // Getters for x, y, and treeModel (if needed)
    public String getX() {
        return x;
    }

    public String getY() {
        return y;
    }

    public TreeModel getTreeModel() {
        return treeModel;
    }

    public void display() {
        System.out.println("Tree of type " + treeModel.getType() + " with texture " + treeModel.getTexture() + " is at (" + x + ", " + y + ")");
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        Tree t1 = new Tree("10", "20", "oak");
        Tree t2 = new Tree("15", "30", "oak");
        Tree t3 = new Tree("20", "40", "maple");
        Tree t4 = new Tree("25", "45", "maple");

// t1과 t2는 동일한 treeModel 공유
// t3와 t4는 동일하 treeModel 공유

        t1.display();
        t2.display();
        t3.display();
        t4.display();

        System.out.println("Total TreeModel objects created: " + TreeModelFactory.getTreeModelCount());
    }
}

 

참고자료


https://refactoring.guru/ko/design-patterns/flyweight