정의

flyweight는 다른 유사한 개체와 가능한 한 많은 데이터를 공유하여 메모리 사용을 최소화하는 패턴

하나의 인스턴스만으로 상태값만 변경해가며, 사용

주 목적은 메인메모리(RAM) 절약

 

코드

import java.time.LocalDate;
import java.time.Month;

public class FlyweightEx {
	//공통 인터페이스
	static interface Tree {
		public void display(int x, int y);
		public default boolean isWithinRange(LocalDate aDate) {
			Month month = aDate.getMonth();
			return (month.getValue() > 2) && (month.getValue() < 11);
		}
	}
	static class ConiferTree implements Tree {
		public void display(int x, int y) {
			System.out.println("침엽수 위치 : " + x + ", " + y);
		}
	}
	static class DeciduousTree implements Tree {
		public void display(int x, int y) {
			System.out.println("낙엽수 위치 : " + x + ", " + y);
			if (!this.isWithinRange(LocalDate.now())) {
				System.out.println("현재 계절엔 낙엽이 없습니다.");
			}
		}
	}
	//팩토리로 생성을 관리
	static class TreeFactory {
		Tree d, c = null;
		public TreeFactory() {
			this.d = new DeciduousTree();
			this.c = new ConiferTree();
		}
		public Tree getTree(String type) throws Exception {
			if (type.equals("침엽수")) {
				return this.d;
			} else if (type.equals("낙엽수")) {
				return this.c;
			} else {
				throw new Exception("지원하지 않는 종류");
			}
		}
	}
	
	public static void main(String[] args) {
		//플레이웨이트 클래스에서 상태만을 때어 별도로 관리
		//상태가 바뀔때마다 마치 새로운 인스턴스 처럼 보이지만, 하나의 인스턴스
		int[][] deciduousLocations = {{1, 1}, {33, 50}, {100, 90}};
		int[][] coniferLocations = {{10, 87}, {24, 76}, {2, 64}};
		
		TreeFactory treeFactory = new TreeFactory();
		Tree d, c;
		try {
			d = treeFactory.getTree("낙엽수");
			c = treeFactory.getTree("침엽수");
			for (int[] location : deciduousLocations) {
				d.display(location[0],  location[1]);
			}
			for (int[] location : coniferLocations) {
				c.display(location[0],  location[1]);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

결과

침엽수 위치 : 1, 1
침엽수 위치 : 33, 50
침엽수 위치 : 100, 90
낙엽수 위치 : 10, 87
낙엽수 위치 : 24, 76
낙엽수 위치 : 2, 64

웹 프로그래밍에선 쓰이는 곳을 찾기가 더 힘들 듯하다.

정의

복잡한 객체들을 단계별로 생성할 수 있도록 하는 생성 디자인 패턴입니다.

이 패턴을 사용하면 같은 제작 코드를 사용하여 객체의 다양한 유형들과 표현을 제작할 수 있습니다.

 

빌더 패턴은 텔레스코핑 생성자 안티 패턴에 대한 해결책을 제공합니다.

public class BuilderEx {
	static class House{
		String interior; //내장재
		String exterior; //외장재
		int floor; // 층수
		int room;  // 방수
		int window;// 창문수
		
		public House(String interior, String exterior, int floor, int room, int window) {
			super();
			this.interior = interior;
			this.exterior = exterior;
			this.floor = floor;
			this.room = room;
			this.window = window;
		}

		@Override
		public String toString() {
			return "House [interior=" + interior + ", exterior=" + exterior + ", floor=" + floor + ", room=" + room
					+ ", window=" + window + "]";
		}
	}
	
	public static void main(String[] args) {
		System.out.println(new House("황토", "나무", 2, 4, 10));
        //이 코드 한줄만 보고 뭐가 내장재인지, 외장재인지
        //뭐가 층수, 방수, 창문수인지 알 수가 없다.
	}

위 처럼 생성자 인수가 늘어날 수록 순서를 신경써야한다.

 

빌더 적용

	//빌더
	static interface Builder{
		//편의상 메서드 체이닝을 적용한 경우가 많다.
		Builder setInterior(String interior);
		Builder setExterior(String exterior);
		Builder setFloor(int floor);
		Builder setRoom(int room);
		Builder setWindow(int window);
		//생성 메서드는 단일 계층 구조엔 상관없으나, 다중 계층구조이면 서브클래스에 위임한다.
		//리턴하는 객체 타입이 다르기 때문이다.
		House build();
	}
	
	static class HouseBuilder implements Builder{
		//기존 코드가 설정자 메서드를 지원한하기에 임시로 값을 저장해둔다.
		String interior; //내장재
		String exterior; //외장재
		int floor; // 층수
		int room;  // 방수
		int window;// 창문수
		
		public Builder setInterior(String interior) {
			this.interior = interior;
			return this;
		}
		public Builder setExterior(String exterior) {
			this.exterior = exterior;
			return this;
		}
		public Builder setFloor(int floor) {
			this.floor = floor;
			return this;
		}
		public Builder setRoom(int room) {
			this.room = room;
			return this;
		}
		public Builder setWindow(int window) {
			this.window = window;
			return this;
		}
		@Override
		public House build() {
			//검증 로직이 존재할 수도 있을 것, 혹은 기본값으로 설정되지 않은 값을 대신 할 수도 있다.
			if(interior == null && exterior == null 
					&& floor == 0&& room == 0&& window == 0) {
				//기본적으로 빌더 패턴은 사용자가 생성구조를 잘 파악하고 있어야 함을 전제로 한다.
				throw new IllegalArgumentException("인수가 부족합니다.");
			}
			try {
				return new House(interior, exterior, floor, room, window);
			}finally {
				interior = null;
				exterior = null;
				floor = 0;
				room = 0;
				window = 0;
			}
		}
	}
	
	public static void main(String[] args) {
		Builder houseBuilder = new HouseBuilder();
		houseBuilder.setExterior("나무");
		houseBuilder.setInterior("대리석");
		houseBuilder.setFloor(2);
		houseBuilder.setRoom(5);
		houseBuilder.setWindow(10);
		System.out.println(houseBuilder.build());
		houseBuilder.setExterior("콘크리트")
					.setInterior("유리")
					.setFloor(1)
					.setRoom(1)
					.setWindow(10);
		System.out.println(houseBuilder.build());
	}

빌더 패턴에선 기본적으로 사용자가 생성할 객체 구성을 직접 디자인한다. 따라서 잘알고 있다고 가정을 한다.

이렇게 객체 구조를 잘 알아야한다는 것이 단점이다.

디렉터

디렉터 클래스를 두어 자주쓰는 구성 정보를 재사용할 수도 있다.

	static class HouseDirector{
		public void house1(Builder builder) {
			builder.setExterior("나무");
			builder.setInterior("대리석");
			builder.setFloor(2);
			builder.setRoom(5);
			builder.setWindow(10);
		}
		public void house2(Builder builder) {
			builder.setExterior("콘크리트")
				.setInterior("유리")
				.setFloor(1)
				.setRoom(1)
				.setWindow(10);
		}
	}
	
	public static void main(String[] args) {
		Builder houseBuilder = new HouseBuilder();
		HouseDirector houseDirector = new HouseDirector();
		houseDirector.house1(houseBuilder);
		System.out.println(houseBuilder.build());
		houseDirector.house2(houseBuilder);
		System.out.println(houseBuilder.build());
	}

builder.zip
0.00MB

자주 보는 클래스에 빌더 패턴 사례

 

+ Recent posts