독서

[디자인 패턴의 아름다움] ch 7.5 ~ ch 7.7

오렌지색 귤 2025. 4. 20. 20:40
반응형

p. 348

어댑터 패턴과 퍼사드 패턴의 공통점은 설계가 좋지 않은 인터페이스를, 사용하기 용이한 인터페이스로 만든다는 점이다.

 

내가 이해하기로 퍼사드 패턴은 여러 서브시스템을 묶어 클라이언트에 노출할 단일, 간결한 인터페이스를 제공한다.

그래서 어댑터 패턴과는 다르게 좋지 않은 설계에만 사용하는 패턴은 아닌 것으로 보인다.

퍼사드는 단순히 부실한 설계를 감추는 꼼수가 아니라, 건전한 계층화와 관심사 분리를 위한 패턴이라고 생각한다.

 

 

p. 348

복합체 패턴은 주로 트리 구조의 데이터를 처리하는 데 사용된다.

 

Q. 실제 개발을 하면서 트리 구조의 자료 구조를 생성하고 복합체 패턴을 활용해보신 적이 있는지?

 

회계 시스템에 사용해본다면..?

  1. 계정과목 계층 구조
    • 계정과목 (Asset, Liability, Equity 등) 과 하위 세부 계정 (Current Assets, Cash 등) 을 하나의 인터페이스 (AccountComponent)로 다루기
    • 잔액 합계, 잔액 이동, 재귀적 집계 등을 Composite 노드 (그룹 계정)와 Leaf 노드 (개별 계정)에 동일하게 적용
  2. 재무제표 구성 요소
    • 재무상태표나 손익계산서의 항목을 챕터 -> 섹션 -> 라인 아이템 구조로 표현
    • 각 항목의 금액 집계 (getAmount())를 구현체마다 다르게 처리하되, 클라이언트에서는 인터페이스만 호출
  3. 분개 묶음 처리
    • 하루치 분개, 월말 분개, 연말 조정 분개 등을 CompositeEntry로 묶고, 개별 분개는 JournalEntry Leaf로 구현
    • 일괄 승인, 일괄 롤백, 검증 로직을 Composite에 한 번만 구현
  4. 부서, 사업부 비용 집계
    • 법인 전체 -> 사업부 -> 부서 -> 팀 단위로 비용을 계층화
    • 부서별 비용 합계, 전사 비용 합계를 동일한 코드 (CostComponent.calculate())로 처리
  5. 결산 작업 워크플로우
    • 여러 단계 (전표검증 -> 조정분개 -> 시산표 생성 -> 재무제표 작성)를 Step 인터페이스로 공통화
    • 전체 결산 작업은 CompositeStep으로, 개별 단계는 LeafStep으로 구현

 

 

p. 362

플라이웨이트 패턴은 JVM의 가비지 컬렉션 기능과는 상성이 좋지 않다.

 

Q. 언제 플라이웨이트를 써야 할까?

A. 1번의 경우에만 사용하자

  1. 메모리 절약 효과가 더 큰 경우 : 힙 사용량이 크게 내려가면, GC 부하 증가보다 이득이 크다
    • 객체 인스턴스를 수백만 개 생성해야 하는 상황
    • 인스턴스당 메모리 크기가 크고, 동일한 속성의 객체가 매우 자주 반복될 때
  2. GC 부담이 더 큰 경우 : 오히려 GC가 더 자주, 길게 돌아가 전체 성능이 떨어진다
    • 풀에 들어가는 플라이웨이트 객체가 너무 많이 Old 영역이 포화될 때
    • 객체 수가 그리 많지 않아 메모리 절약 효과가 미미할 때

 

 

p. 368

 

Q. String은 가비지 컬렉션 대상인가?

 

1. 새로 생성된 String 객체 (new 생성자)

String s = new String("hello");

 

힙에 할당된 일반 객체이므로, 참조가 끊기면 GC 대상이 된다

 

2. 리터럴이나 intern() 된 String 객체

String a = "hello";
String b = new String("hi").intern();

 

문자열 풀에 저장되어 강한 참조로 유지되므로, 클래스 로더가 언로드되기 전까지는 GC되지 않는다

JVM 7 이후에는 이 풀이 힙 영역으로 옮겨졌지만, 여전히 풀 내부에 강한 참조로 남아 있어 쉽게 회수되지 않는다

 

 

Q. 그렇다면 리터럴이 너무 많으면 메모리 에러가 발생하나?

A. 맞다. 다만 리터럴이 수만 개 이상이 아니라면 보통 문제가 되지 않는다. 동적으로 생성해서 intern()을 남발하거나 대량의 클래스를 런타임에 생성, 로딩 (예: 리플렉션 코드 생성 라이브러리) 할 때 리터럴이 폭증하면 문제가 될 수 있따.

 

  1. JDK6 이하 (PermGen OOM)
    • 문자열 리터럴은 PermGen 영역에 올라간다
    • 클래스 로딩 시 리터럴이 모두 풀에 적재되고, 해제되지 않으므로 PermGen이 가득차면 java.lang.OutOfMemoryError: PermGen space 에러 발생
  2. JDK7 이상 (힙 OOM)
    • PermGen이 사라지고 메타스페이스(Metaspace)로 대체되면서, 상수 풀은 힙 영역에 위치한다
    • 리터럴이나 intern()된 문자열이 너무 많아 힙을 과도하게 차지하면 java.lang.OutOfMemoryError: Java heap space 에러 발생
반응형