반복된 문제에 대한 재사용 가능한 해결책
객체 생성과 구현을 분리함.
클래스보다 훨씬 작은 면을 노출한다. 클래스는 확장되거나 조작될 수 있지만 팩토리는 단지 함수다.
그래서 더 적은 유연성을 제공하다.
new 또는 Object.create()를 통해 클래스로 객체를 만드는 것보다 팩토리를 호출하는 게 더 편리하고 유용하다?!
function createImage(name) {
return new Image(name)
}
const image = createImage('photo.jpg')
왜 new Image(name)를 사용하지 않을까?
new를 사용하면 코드를 특정 유형 객체에 바인딩 함.
감추기와 추상화: 팩토리 함수는 생성하려는 객체의 내부 구현을 숨기고, 사용자에게는 필요한 인터페이스만을 노출시킵니다. 이를 통해 구현 세부사항의 변경이 사용자 코드에 미치는 영향을 최소화할 수 있습니다.
유연성: 객체 생성 로직을 한 곳에 집중시키면, 나중에 객체를 생성하는 방법이나 생성되는 객체의 유형을 변경해야 할 때 쉽게 대응할 수 있습니다. 예를 들어, Image 클래스의 대안으로 새로운 AdvancedImage 클래스를 사용하기로 결정했을 때 팩토리 함수만 수정하면 되므로, 코드 수정이 한곳에 국한됩니다.
객체를 직접 생성하지 않고, 객체를 생성하는 별도 함수를 래핑함으로써 유지 보수성을 높힐 수 있다!

유창한 인터페이스를 제공, 복잡한 객체 생성을 단순화 하는 생성 디자인 패턴
단계별로 객체를 만들 수 있음.
유용한 상황은 매개변수가 많은 객체를 생성해야 할 때.
car를 생성하기 위해 carBuilder 함수를 사용
class Car {
constructor(builder) {
this.brand = builder.brand;
this.engine = builder.engine;
this.wheels = builder.wheels;
this.color = builder.color;
}
describe() {
return `A ${this.color} ${this.brand} with a ${this.engine} engine and ${this.wheels} wheels`;
}
}
class CarBuilder {
constructor(brand) {
this.brand = brand;
}
setEngine(engine) {
this.engine = engine;
return this; // Allows for chaining
}
setWheels(wheels) {
this.wheels = wheels;
return this; // Allows for chaining
}
setColor(color) {
this.color = color;
return this; // Allows for chaining
}
build() {
if (!('engine' in this) || !('wheels' in this) || !('color' in this)) {
throw new Error('Missing parts: cannot build');
}
return new Car(this);
}
}
// Usage:
const carBuilder = new CarBuilder('Toyota');
const car = carBuilder.setEngine('V8').setWheels(4).setColor('red').build();
console.log(car.describe()); // Outputs: A red Toyota with a V8 engine and 4 wheels
클래스의 생성자를 공개(public)로 설정하여, 다른 클래스나 메서드에서 새로운 인스턴스를 직접 생성
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
display() {
console.log(`Name: ${this.name}, Age: ${this.age}`);
}
}
// 객체 생성
const person1 = new Person("Alice", 30);
person1.display(); // 출력: Name: Alice, Age: 30
const person2 = new Person("Bob", 25);
person2.display(); // 출력: Name: Bob, Age: 25
잉? js에서 클래스 문법 쓰면 그게 바로 공개 생성자?
function Person(name, age) {
this.name = name;
this.age = age;
this.display = function() {
console.log(`Name: ${this.name}, Age: ${this.age}`);
};
}
// 객체 생성
var person1 = new Person("Alice", 30);
person1.display(); // 출력: Name: Alice, Age: 30
var person2 = new Person("Bob", 25);
person2.display(); // 출력: Name: Bob, Age: 25
클래스의 인스턴스가 하나만 존재하도록 접근을 중앙 집중화 하는 것
애플리케이션의 모든 컴포넌트가 단일 인스턴스를 사용하는 것
대표적으로 Database 인스턴스
왜냐면 디비 커넥션은 하나만 이루어지면 됨.
만약 디비 연결 인스턴스가 여러 개 생성되면 불필요한 디비 연결 증가함.
여러 번 생성자 호출해도 이미 있다면 이미 있는 걸 반환
class Database {
static instance;
constructor(connection) {
if (Database.instance) {
return Database.instance;
}
this.connection = connection;
Database.instance = this;
}
connect() {
console.log(`Connecting to the database: ${this.connection}`);
}
}
const db1 = new Database('MySQL');
db1.connect(); // 출력: Connecting to the database: MySQL
const db2 = new Database('SQLite');
db2.connect(); // 여전히 출력: Connecting to the database: MySQL
console.log(db1 === db2); // 출력: true
static instance; 이게 이미 인스턴스가 있는지 없는지 체크하는 것
객체가 자신의 의존성을 직접 생성하거나 검색하지 않고, 외부(주로 프레임워크나 컨테이너)로부터 주입받는 것
프로그램의 제어 흐름을 사용자 코드에서 프레임워크나 라이브러리로 옮기는 디자인 원칙
제어 역전의 가장 대표적인 구현은 의존성 주입입니다. 의존성 주입을 통해 객체는 필요한 의존성을 직접 관리하지 않고, 이러한 역할을 IoC 컨테이너에 맡깁니다.