1. Factory Pattern
- 요약: 객체 생성 로직을 별도의 메서드로 캡슐화하여, 객체 생성 과정을 쉽게 관리할 수 있는 패턴입니다. 객체를 생성할 때 직접 클래스를 호출하지 않고, 팩토리 메서드를 통해 객체를 반환받습니다.
- 왜 사용하는지?: 객체 생성 방식이 복잡하거나, 여러 종류의 객체를 유연하게 생성해야 할 때 사용합니다.
- 예시:
class Car {
constructor(brand) {
this.brand = brand;
}
drive() {
console.log(`${this.brand} is driving`);
}
}
class CarFactory {
static createCar(type) {
switch (type) {
case 'Tesla':
return new Car('Tesla');
case 'BMW':
return new Car('BMW');
default:
return new Car('Generic Car');
}
}
}
const myCar = CarFactory.createCar('Tesla');
myCar.drive(); // Tesla is driving
2. Singleton Pattern
- 요약 : 특정 클래스의 인스턴스가 하나만 생성되도록 보장하는 패턴입니다. 애플리케이션 전체에서 하나의 객체를 공유해야 할 때 유용합니다.
- 왜 사용하는지?: 전역적인 상태 관리나, 하나의 객체로 모든 곳에서 동일한 인스턴스를 참조해야 할 때 사용합니다.
- 예시:
class Singleton {
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
this.data = [];
Singleton.instance = this;
}
addData(item) {
this.data.push(item);
}
getData() {
return this.data;
}
}
const instance1 = new Singleton();
const instance2 = new Singleton();
instance1.addData('Item 1');
console.log(instance2.getData()); // ['Item 1'] -> 두 인스턴스가 동일 객체를 참조
3. Observer Pattern
- 요약 : 객체 간의 의존성을 정의하여, 한 객체의 상태 변화가 다른 객체에게 자동으로 전파되도록 만드는 패턴입니다. Publisher-Subscriber 모델로도 불립니다.
- 왜 사용하는지?: 객체 상태 변화가 발생할 때, 그 변화를 다른 객체들에게 통지하고 싶을 때 사용합니다. 예를 들어 이벤트 시스템에 많이 사용됩니다.
- 예시:
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
update(data) {
console.log(`Received update: ${data}`);
}
}
const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify('New Data'); // 모든 관찰자가 업데이트 받음
4. Strategy Pattern
- 요약 : 여러 알고리즘을 정의하고, 런타임에 하나의 알고리즘을 선택해서 사용하는 패턴입니다. 전략(Strategy)들을 캡슐화하여, 클라이언트 코드가 특정 전략에 종속되지 않도록 합니다.
- 왜 사용하는지?: 동일한 작업을 수행하지만 다양한 방식(알고리즘)을 제공해야 할 때 사용됩니다.
- 예시:
class PaymentStrategy {
pay(amount) {
throw new Error('This method should be overridden');
}
}
class CreditCardPayment extends PaymentStrategy {
pay(amount) {
console.log(`Paid ${amount} using Credit Card`);
}
}
class PayPalPayment extends PaymentStrategy {
pay(amount) {
console.log(`Paid ${amount} using PayPal`);
}
}
class PaymentProcessor {
constructor(strategy) {
this.strategy = strategy;
}
process(amount) {
this.strategy.pay(amount);
}
}
const creditCardPayment = new PaymentProcessor(new CreditCardPayment());
creditCardPayment.process(100); // Paid 100 using Credit Card
const payPalPayment = new PaymentProcessor(new PayPalPayment());
payPalPayment.process(200); // Paid 200 using PayPal
5. Decorator Pattern
- 요약 : 기존 객체의 기능을 확장할 때 상속을 사용하지 않고, 객체를 동적으로 감싸서 새로운 기능을 추가하는 패턴입니다.
- 왜 사용하는지?: 객체의 기능을 변경하거나 확장할 때 원본 객체를 수정하지 않고 기능을 추가하고 싶을 때 사용합니다.
- 예시:
class Coffee {
cost() {
return 5;
}
}
class MilkDecorator {
constructor(coffee) {
this.coffee = coffee;
}
cost() {
return this.coffee.cost() + 2;
}
}
class SugarDecorator {
constructor(coffee) {
this.coffee = coffee;
}
cost() {
return this.coffee.cost() + 1;
}
}
let myCoffee = new Coffee();
myCoffee = new MilkDecorator(myCoffee);
myCoffee = new SugarDecorator(myCoffee);
console.log(`Total cost: $${myCoffee.cost()}`); // Total cost: $8
6. Repository Pattern
- 요약: 데이터 접근 로직을 비즈니스 로직과 분리하여, 데이터베이스와의 상호작용을 관리하는 패턴입니다. 데이터 접근과 관련된 CRUD(Create, Read, Update, Delete) 연산을 Repository 객체에서 처리함으로써 코드의 유지보수성과 테스트 가능성을 높입니다.
- 왜 사용하는지 ?: 데이터베이스 관련 로직을 비즈니스 로직에서 분리하여 모듈화하고, 데이터베이스나 스토리지 시스템을 유연하게 변경할 때 사용합니다.
- 예시:
class UserRepository {
constructor(databaseConnection) {
this.db = databaseConnection;
}
async findById(id) {
const result = await this.db.query('SELECT * FROM users WHERE id = ?', [id]);
return new User(result.id, result.name, result.email);
}
async save(user) {
await this.db.query('INSERT INTO users (name, email) VALUES (?, ?)', [user.name, user.email]);
}
}
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}
async getUserInfo(userId) {
return await this.userRepository.findById(userId);
}
async createUser(name, email) {
const newUser = new User(null, name, email);
await this.userRepository.save(newUser);
}
}
오늘의 배움
오늘은 Factory Pattern, Singleton Pattern, Observer Pattern, Strategy Pattern, Decorator Pattern, Repository Pattern 과 같은 다양한 디자인 패턴들을 학습했습니다. 각 패턴은 다양한 상황에서 코드의 확장성과 유연성을 높여주며, 상황에 맞게 적절히 사용함으로써 더 견고한 애플리케이션을 개발할 수 있다는 점을 깨달았습니다.
'TIL' 카테고리의 다른 글
REST, REST API, RESTful 특징 (간단 글 소개) (1) | 2024.09.30 |
---|---|
express.urlencoded()에 대한 간단한 조사 (0) | 2024.09.28 |
OSI 7계층 응용 계층 (Application Layer)에 대하여 (자세하게) (0) | 2024.09.25 |
OSI 7계층 응용 계층 (Application Layer)에 대하여 (간단 소개) (0) | 2024.09.24 |
Status Code 종류별로 한줄로 설명하기 - 틀린 부분 또는 추가할 부분 지적 O (0) | 2024.09.24 |