자주 나오는 문제를 역할 나누는 방식으로 푸는 규약
클래스는 있지만, 인스턴스는 앱에서 하나만 쓰게 하는 패턴
MVC로 보면
Model / View / Controller가 같은 Config를 봄
각자 new하면 설정이 엇갈림
| 장점 | 단점 |
|---|---|
| 공유 상태가 한곳 | 전역처럼 퍼져서 추적 힘듦 |
| 생성 비용↓ (여러 번 new 안 함) | 테스트끼리 상태 오염 → TDD 걸림돌 |
| 의존이 코드에 안 보여서 결합↑ |
DB 연결처럼 “진짜 하나여야 할 때” 쓰기 좋음
남용하면 유지보수 독
Java에서 중첩 클래스 방식
class Singleton {
private Singleton() {}
private static class SingleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingleInstanceHolder.INSTANCE;
}
}
public class HelloWorld {
public static void main(String[] args) {
Singleton a = Singleton.getInstance();
Singleton b = Singleton.getInstance();
System.out.println(a == b); // true
}
}
포인트 :
1. Holder + static → 클래스 로딩 타이밍에 인스턴스 1개 보장, synchronized 없이도 스레드 이슈 덜함
2. final → INSTANCE 재할당 막음
3. lazy → getInstance() 처음 불릴 때 Holder가 로드되며 생성
DI
싱글톤에서 하나 가져오는 방식 대신,
만들어서 넘겨주는 방식으로 결합을 느슨하게 하는 것
new Latte(), new Americano()를 여기저기 뿌리지 않고,
생성 판단을 한곳(Factory)에 모으는 것
abstract class Coffee {
public abstract int getPrice();
@Override
public String toString() {
return "Hi this coffee is " + this.getPrice();
}
}
class CoffeeFactory {
public static Coffee getCoffee(String type, int price) {
if ("Latte".equalsIgnoreCase(type)) return new Latte(price);
if ("Americano".equalsIgnoreCase(type)) return new Americano(price);
return new DefaultCoffee();
}
}
// Latte / Americano / DefaultCoffee는 Coffee 상속
언제 쓰는지:
종류가 늘어날 때마다 if가 여기저기 생기기 시작할 때
전략(알고리즘)을 갈아끼울 수 있게 빼두는 패턴
클래스 안 if/else로 직접 고치지 않음
예시: 결제
PayByCard / PayByCash → 컨텍스트가 전략만 교체
기능 바꾸고 싶을 때, 분기 추가 대신 객체 교체
상태 바뀌면, 구독자들에게 알려주는 것
발행(Subject) — 구독(Observer)
예:
입력 바뀜 → 화면 다시 그림 / 이벤트 버스 / React 구독 느낌
진짜 객체 앞에 대리자를 두는 것
접근 제어, 캐시, 로깅, 원격 호출 등
프록시 서버는 네트워크 쪽 대리(nginx 등)라서
패턴이랑 이름은 비슷해도 레이어가 다름
→ 패턴 노트에선 “대리자”만, 서버는 인프라로 분리 가능
UI 구조를 나누는 아키텍처 패턴.
| 핵심 | |
|---|---|
| MVC | View ↔ Controller ↔ Model (변형 많음) |
| MVP | View는 수동, Presenter가 중개 |
| MVVM | View ↔ ViewModel 바인딩 (데이터 연결 중심) |