싱글톤 및 팩토리 패턴: 제어된 객체 생성
I 창의적인 디자인 패턴 객체 생성을 유연하게 처리 그리고 재사용이 가능합니다. 하나씩 일어나는 것 클래스의 인스턴스는 하나만 보장하지만 공장 객체 생성을 특수 메서드에 위임합니다. 둘 다 견고한 소프트웨어 아키텍처의 기본입니다.
🎯 무엇을 배울 것인가
- 싱글톤 패턴: 전역 고유 인스턴스
- 팩토리 메소드: 생성을 서브클래스에 위임
- 추상 팩토리(Abstract Factory): 관련 개체군
- 각 패턴을 사용하는 경우
싱글톤 패턴
Il 하나씩 일어나는 것 클래스가 단일 인스턴스 이에 대한 글로벌 액세스 포인트를 제공합니다. 연결과 같은 공유 리소스에 유용합니다. 데이터베이스, 로거, 캐시.
class DatabaseConnection {{ '{' }}
private static instance: DatabaseConnection;
private connected: boolean = false;
// Costruttore privato: impedisce `new DatabaseConnection()`
private constructor() {{ '{' }}
console.log("DatabaseConnection created");
{{ '}' }}
// Metodo statico per ottenere l'istanza unica
public static getInstance(): DatabaseConnection {{ '{' }}
if (!DatabaseConnection.instance) {{ '{' }}
DatabaseConnection.instance = new DatabaseConnection();
{{ '}' }}
return DatabaseConnection.instance;
{{ '}' }}
public connect(): void {{ '{' }}
if (!this.connected) {{ '{' }}
console.log("Connecting to database...");
this.connected = true;
{{ '}' }}
{{ '}' }}
public query(sql: string): void {{ '{' }}
console.log(`Executing: ${{ '{' }}sql{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Utilizzo
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true - stessa istanza!
db1.connect();
db2.query("SELECT * FROM users"); // Usa la stessa connessione
지연 초기화를 사용하는 싱글톤
인스턴스는 애플리케이션이 시작될 때가 아니라 필요할 때만 생성됩니다.
class Logger {{ '{' }}
private static instance: Logger;
private logs: string[] = [];
private constructor() {{ '{' }}{{ '}' }}
public static getInstance(): Logger {{ '{' }}
// Creato solo alla prima chiamata
if (!Logger.instance) {{ '{' }}
console.log("Logger instance created");
Logger.instance = new Logger();
{{ '}' }}
return Logger.instance;
{{ '}' }}
public log(message: string): void {{ '{' }}
const timestamp = new Date().toISOString();
this.logs.push(`[${{ '{' }}timestamp{{ '}' }}] ${{ '{' }}message{{ '}' }}`);
console.log(this.logs[this.logs.length - 1]);
{{ '}' }}
public getLogs(): string[] {{ '{' }}
return [...this.logs];
{{ '}' }}
{{ '}' }}
// Prima chiamata: crea istanza
const logger = Logger.getInstance(); // "Logger instance created"
logger.log("Application started");
// Chiamate successive: riutilizza istanza
const logger2 = Logger.getInstance(); // Nessun log di creazione
logger2.log("User logged in");
console.log(logger.getLogs());
// ["[2024-12-26T...] Application started", "[2024-12-26T...] User logged in"]
싱글톤: 장점과 단점
✅ 장점:
- 단일 인스턴스 보장
- 글로벌 액세스
- 지연 초기화
- 공유 리소스 제어
❌ 단점:
- 전역 상태(테스트하기 어려움)
- 비올라 단일 책임
- 강력한 결합
- 멀티스레딩 문제
⚠️ 싱글톤의 대안
많은 경우, 의존성 주입 싱글톤보다 더 좋습니다. Angular와 같은 프레임워크는 DI를 사용하여 싱글톤 패턴의 문제 없이 단일 인스턴스를 제공합니다.
팩토리 메소드 패턴
Il 팩토리 메소드 객체를 생성하기 위한 인터페이스를 정의하지만 위임합니다. 서브클래스는 인스턴스화할 구체적인 클래스를 결정합니다.
// Interfaccia prodotto
interface Vehicle {{ '{' }}
drive(): void;
getType(): string;
{{ '}' }}
// Prodotti concreti
class Car implements Vehicle {{ '{' }}
drive(): void {{ '{' }}
console.log("Driving a car 🚗");
{{ '}' }}
getType(): string {{ '{' }}
return "Car";
{{ '}' }}
{{ '}' }}
class Motorcycle implements Vehicle {{ '{' }}
drive(): void {{ '{' }}
console.log("Riding a motorcycle 🏍️");
{{ '}' }}
getType(): string {{ '{' }}
return "Motorcycle";
{{ '}' }}
{{ '}' }}
class Truck implements Vehicle {{ '{' }}
drive(): void {{ '{' }}
console.log("Driving a truck 🚚");
{{ '}' }}
getType(): string {{ '{' }}
return "Truck";
{{ '}' }}
{{ '}' }}
// Factory astratta
abstract class VehicleFactory {{ '{' }}
// Factory method (da implementare nelle sottoclassi)
abstract createVehicle(): Vehicle;
// Metodo che usa il factory method
public deliverVehicle(): void {{ '{' }}
const vehicle = this.createVehicle();
console.log(`Delivering a ${{ '{' }}vehicle.getType(){{ '}' }}`);
vehicle.drive();
{{ '}' }}
{{ '}' }}
// Factory concrete
class CarFactory extends VehicleFactory {{ '{' }}
createVehicle(): Vehicle {{ '{' }}
return new Car();
{{ '}' }}
{{ '}' }}
class MotorcycleFactory extends VehicleFactory {{ '{' }}
createVehicle(): Vehicle {{ '{' }}
return new Motorcycle();
{{ '}' }}
{{ '}' }}
class TruckFactory extends VehicleFactory {{ '{' }}
createVehicle(): Vehicle {{ '{' }}
return new Truck();
{{ '}' }}
{{ '}' }}
// Utilizzo
function clientCode(factory: VehicleFactory) {{ '{' }}
factory.deliverVehicle();
{{ '}' }}
clientCode(new CarFactory());
// "Delivering a Car"
// "Driving a car 🚗"
clientCode(new MotorcycleFactory());
// "Delivering a Motorcycle"
// "Riding a motorcycle 🏍️"
심플 팩토리(팩토리 패턴)
더 간단한 변형: 단일 팩토리 메소드가 인스턴스화할 클래스를 결정합니다.
class VehicleSimpleFactory {{ '{' }}
public static createVehicle(type: string): Vehicle {{ '{' }}
switch (type) {{ '{' }}
case 'car':
return new Car();
case 'motorcycle':
return new Motorcycle();
case 'truck':
return new Truck();
default:
throw new Error(`Unknown vehicle type: ${{ '{' }}type{{ '}' }}`);
{{ '}' }}
{{ '}' }}
{{ '}' }}
// Utilizzo
const car = VehicleSimpleFactory.createVehicle('car');
car.drive(); // "Driving a car 🚗"
const truck = VehicleSimpleFactory.createVehicle('truck');
truck.drive(); // "Driving a truck 🚚"
추상 팩토리 패턴
추상 공장 만들다 관련 객체 패밀리 없이 구체적인 클래스를 지정합니다. UI 테마, 다중 공급업체 데이터베이스 등에 유용합니다.
// Interfacce prodotti
interface Button {{ '{' }}
render(): void;
{{ '}' }}
interface Checkbox {{ '{' }}
render(): void;
{{ '}' }}
// Prodotti concreti - Tema Dark
class DarkButton implements Button {{ '{' }}
render(): void {{ '{' }}
console.log("Rendering dark button");
{{ '}' }}
{{ '}' }}
class DarkCheckbox implements Checkbox {{ '{' }}
render(): void {{ '{' }}
console.log("Rendering dark checkbox");
{{ '}' }}
{{ '}' }}
// Prodotti concreti - Tema Light
class LightButton implements Button {{ '{' }}
render(): void {{ '{' }}
console.log("Rendering light button");
{{ '}' }}
{{ '}' }}
class LightCheckbox implements Checkbox {{ '{' }}
render(): void {{ '{' }}
console.log("Rendering light checkbox");
{{ '}' }}
{{ '}' }}
// Abstract Factory
interface UIFactory {{ '{' }}
createButton(): Button;
createCheckbox(): Checkbox;
{{ '}' }}
// Factory concrete
class DarkThemeFactory implements UIFactory {{ '{' }}
createButton(): Button {{ '{' }}
return new DarkButton();
{{ '}' }}
createCheckbox(): Checkbox {{ '{' }}
return new DarkCheckbox();
{{ '}' }}
{{ '}' }}
class LightThemeFactory implements UIFactory {{ '{' }}
createButton(): Button {{ '{' }}
return new LightButton();
{{ '}' }}
createCheckbox(): Checkbox {{ '{' }}
return new LightCheckbox();
{{ '}' }}
{{ '}' }}
// Client code
class Application {{ '{' }}
private button: Button;
private checkbox: Checkbox;
constructor(factory: UIFactory) {{ '{' }}
this.button = factory.createButton();
this.checkbox = factory.createCheckbox();
{{ '}' }}
render(): void {{ '{' }}
this.button.render();
this.checkbox.render();
{{ '}' }}
{{ '}' }}
// Utilizzo - Switch tema senza modificare Application
const darkApp = new Application(new DarkThemeFactory());
darkApp.render();
// "Rendering dark button"
// "Rendering dark checkbox"
const lightApp = new Application(new LightThemeFactory());
lightApp.render();
// "Rendering light button"
// "Rendering light checkbox"
각 패턴을 사용하는 경우
✅ 다음과 같은 경우에 싱글톤을 사용하세요:
- 유용하다 단일 인스턴스 전역(로거, 캐시, 구성)
- 공유자원 제어(DB 연결)
- 더 나은: 가능하면 대신 종속성 주입을 사용하세요.
✅ 다음과 같은 경우에 팩토리 메소드를 사용하세요:
- 수업은 사전에 알 수 없습니다 어떤 객체를 생성할지
- 생성을 위임하시겠습니까? 서브클래스
- 확장성이 필요함(기존 코드를 변경하지 않고 새로운 유형)
✅ 다음과 같은 경우 추상 팩토리를 사용하세요.
- 우리는 창조해야 합니다 관련 객체 패밀리
- 객체는 반드시 함께 사용해야 합니다(UI 테마, 크로스 플랫폼 UI)
- 구현 세부정보를 숨기고 싶습니다.
실제 예: 알림 시스템
// Singleton Logger
class NotificationLogger {{ '{' }}
private static instance: NotificationLogger;
private constructor() {{ '{' }}{{ '}' }}
static getInstance(): NotificationLogger {{ '{' }}
if (!this.instance) this.instance = new NotificationLogger();
return this.instance;
{{ '}' }}
log(msg: string): void {{ '{' }}
console.log(`[NOTIFICATION] ${{ '{' }}msg{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Factory per Notifiche
interface Notification {{ '{' }}
send(message: string): void;
{{ '}' }}
class EmailNotification implements Notification {{ '{' }}
send(message: string): void {{ '{' }}
NotificationLogger.getInstance().log(`Email sent: ${{ '{' }}message{{ '}' }}`);
{{ '}' }}
{{ '}' }}
class SMSNotification implements Notification {{ '{' }}
send(message: string): void {{ '{' }}
NotificationLogger.getInstance().log(`SMS sent: ${{ '{' }}message{{ '}' }}`);
{{ '}' }}
{{ '}' }}
class NotificationFactory {{ '{' }}
static create(type: 'email' | 'sms'): Notification {{ '{' }}
switch (type) {{ '{' }}
case 'email':
return new EmailNotification();
case 'sms':
return new SMSNotification();
{{ '}' }}
{{ '}' }}
{{ '}' }}
// Utilizzo
const emailNotif = NotificationFactory.create('email');
emailNotif.send("Welcome!"); // [NOTIFICATION] Email sent: Welcome!
const smsNotif = NotificationFactory.create('sms');
smsNotif.send("Code: 1234"); // [NOTIFICATION] SMS sent: Code: 1234
결론
싱글톤과 팩토리는 기본적인 생성 패턴입니다. 하나씩 일어나는 것 보증 단일 글로벌 인스턴스인 반면 공장 유연성을 위한 대리자 생성. 추상 공장 객체군에 대한 개념을 확장합니다. 신중하게 사용하세요: 종속성 주입은 싱글톤보다 선호되는 경우가 많으며 대신 Simple Factory로 충분할 수 있습니다. 복잡한 팩토리 방법.
🎯 핵심 포인트
- 싱글톤 = 하나의 전역 인스턴스(개인 생성자)
- 팩토리 메소드 = 생성을 서브클래스에 위임
- Simple Factory = 정적 메소드가 무엇을 생성할지 결정합니다.
- 추상 팩토리(Abstract Factory) = 관련 개체군
- 가능할 때마다 싱글톤보다 종속성 주입을 선호합니다.







