
https://refactoring.guru/ko/design-patterns/catalog
디자인 패턴 목록
refactoring.guru
생성패턴: 팩토리 메서드, 추상팩토리, 빌더
생성: 객체를 어떻게 생성을 할 것인가
구조: 어떻게 조합을 하는가
행동: 객체들간의 책임을 어떻게 분배할 것인가 (결합도)
생성패턴
팩토리 메서드 패턴
- 객체의 생성을 여러 가지로 다르게 생성을 하고 싶은데 매번 타입을 받을 때마다 바꿔줄 수가 없으니 특정 인터페이스를 생성하고 이를 구현하는 구현체에 따라서 내가 생성해서 가져온다는 것.
- 스프링내부에서 실제로 사용되고 있는 패턴 - bean
package io.study.creational;
public class FactoryMethodPattern {
public static void main(String[] args) {
// 클라이언트는 구체적인 io.study.creational.Latte, io.study.creational.Americano 클래스를 몰라도 됨
CoffeeFactory myFactory = new LatteFactory();
myFactory.takeOrder();
CoffeeFactory otherFactory = new AmericanoFactory();
otherFactory.takeOrder();
}
}
// 1. Product (인터페이스)
interface Coffee {
void brew();
}
// 2. ConcreteProduct (구체적 제품)
class Latte implements Coffee {
public void brew() { System.out.println("부드러운 라떼를 만듭니다."); }
}
class Americano implements Coffee {
public void brew() { System.out.println("진한 아메리카노를 만듭니다."); }
}
// 3. Creator (추상 클래스)
abstract class CoffeeFactory {
// 팩토리 메서드 (자식이 구현해야 함)
public abstract Coffee createCoffee();
// 팩토리 메서드를 사용하는 로직
public void takeOrder() {
Coffee coffee = createCoffee(); // 어떤 커피가 나올지 모름!
coffee.brew();
}
}
// 4. ConcreteCreator (구체적 생성자)
class LatteFactory extends CoffeeFactory {
public Coffee createCoffee() {
return new Latte(); // 라떼를 생성
}
}
class AmericanoFactory extends CoffeeFactory {
public Coffee createCoffee() {
return new Americano(); // 아메리카노를 생성
}
}
추상 팩토리
- 최소한의 집합체 (이거는 있어야해!)
- id, password, driver, port, db name → 인터페이스로 미리 선언을 해둠
- 어떤 db를 가져와도 인터페이스 구현체에 따라서 mysql, postsql이든 뭐든 사용할 수 있다.
- 구현체가 여러가지가 있다
package io.study.creational;
public class AbstractFactoryPattern {
public static void main(String[] args) {
// 클라이언트는 처음에 "어떤 스타일"의 팩토리를 쓸지만 결정하면 됨
FurnitureFactory factory = new ModernFactory();
// (만약 VintageFactory로 바꾸면, 아래 코드는 수정할 필요 없이 제품 스타일이 전부 바뀜)
// 클라이언트는 ModernChair, ModernSofa의 존재를 몰라도 됨
Chair chair = factory.createChair();
Sofa sofa = factory.createSofa();
chair.sitOn();
sofa.lieOn();
}
}
// 1. AbstractProduct (제품 인터페이스)
interface Chair { void sitOn(); }
interface Sofa { void lieOn(); }
// 2. ConcreteProduct (구체적인 제품)
class ModernChair implements Chair {
public void sitOn() { System.out.println("모던한 의자에 앉습니다."); }
}
class VintageChair implements Chair {
public void sitOn() { System.out.println("빈티지 의자에 앉습니다."); }
}
class ModernSofa implements Sofa {
public void lieOn() { System.out.println("모던한 소파에 눕습니다."); }
}
class VintageSofa implements Sofa {
public void lieOn() { System.out.println("빈티지 소파에 눕습니다."); }
}
// 3. AbstractFactory (추상 팩토리)
// "의자"와 "소파"를 만들어야 한다는 것을 정의
interface FurnitureFactory {
Chair createChair();
Sofa createSofa();
}
// 4. ConcreteFactory (구체적인 팩토리)
// "모던" 팩토리는 모던 제품만 생성
class ModernFactory implements FurnitureFactory {
public Chair createChair() {
return new ModernChair();
}
public Sofa createSofa() {
return new ModernSofa();
}
}
// "빈티지" 팩토리는 빈티지 제품만 생성
class VintageFactory implements FurnitureFactory {
public Chair createChair() {
return new VintageChair();
}
public Sofa createSofa() {
return new VintageSofa();
}
}
빌더
- 객체 생성할 때 가독성을 좋게 하기 위해서 사용하는 것
- user - setage, setname을 하기 귀찮으니까 생성자에 따라서 입력을 하는 것이 아니라 age, name은 몇 넣고 해서 가독성을 높임 ..
- 스프링에서 롬복으로 해당 작업을 진행함. - 장고는 어떻게 하나?
package io.study.creational;
public class BuilderPattern {
public static void main(String[] args) {
// 빌더 패턴 사용
User user1 = new User.UserBuilder("id123", "Alice")
.age(30)
.phone("010-1234-5678")
.build();
// 선택 항목(age)을 빼고 싶으면 메서드 호출만 안 하면 됨
User user2 = new User.UserBuilder("id456", "Bob")
.phone("010-9999-8888")
.build();
System.out.println( user1.getName() );
System.out.println( user2.getName() );
}
}
// 1. Product (제품) - User 클래스
class User {
// 필드를 final로 선언하여 불변성 확보
private final String id; // 필수
private final String name; // 필수
private final int age; // 선택
private final String phone; // 선택
// 생성자를 private으로! 오직 Builder만 호출 가능
private User(UserBuilder builder) {
this.id = builder.id;
this.name = builder.name;
this.age = builder.age;
this.phone = builder.phone;
}
public String getName() {
return name;
}
// 2. Builder (빌더) - static inner class
public static class UserBuilder {
// Product의 필드와 동일한 필드를 가짐
private final String id; // 필수
private final String name; // 필수
private int age; // 선택 (기본값 0)
private String phone; // 선택 (기본값 null)
// 필수 필드는 Builder의 생성자에서 받음
public UserBuilder(String id, String name) {
this.id = id;
this.name = name;
}
// 선택 필드는 메서드 체이닝으로 설정 (return this;)
public UserBuilder age(int age) {
this.age = age;
return this; // 자신을 반환하여 체이닝 가능
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
// 3. build() 메서드: 최종 객체를 생성
public User build() {
// 여기서 유효성 검사(validation)도 가능
// 예: if (age < 0) throw new IllegalStateException(...)
// private인 User 생성자를 호출
return new User(this);
}
}
}'Programming > Design Patterns' 카테고리의 다른 글
| 🚀 클린 코드의 비밀: 핵심 디자인 패턴 6가지와 헥사고날 아키텍처 (DIP의 힘) (0) | 2025.10.28 |
|---|