Builder 패턴

Builder 패턴은 복잡한 객체의 생성 과정을 단계별로 분리하여 동일한 생성 과정에서 다른 표현을 만들 수 있게 합니다.

사용 시기

  • 생성자에 많은 매개변수가 필요할 때
  • 선택적 매개변수가 많을 때
  • 불변 객체를 생성할 때

구현 예제 (Java)

public class User {
    private final String name;
    private final int age;
    private final String email;
    private final String phone;

    private User(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.email = builder.email;
        this.phone = builder.phone;
    }

    public static class Builder {
        // 필수 매개변수
        private final String name;

        // 선택적 매개변수
        private int age = 0;
        private String email = "";
        private String phone = "";

        public Builder(String name) {
            this.name = name;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder email(String email) {
            this.email = email;
            return this;
        }

        public Builder phone(String phone) {
            this.phone = phone;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }
}

// 사용
User user = new User.Builder("John")
    .age(25)
    .email("john@example.com")
    .phone("010-1234-5678")
    .build();

Kotlin에서의 Builder

Kotlin에서는 named parameters와 default values로 간단히 구현 가능:

data class User(
    val name: String,
    val age: Int = 0,
    val email: String = "",
    val phone: String = ""
)

// 사용
val user = User(
    name = "John",
    age = 25,
    email = "john@example.com"
)

Factory 패턴

Factory 패턴은 객체 생성 로직을 캡슐화하여 클라이언트로부터 구체적인 클래스를 숨깁니다.

Factory Method 패턴

// 인터페이스
interface Animal {
    void speak();
}

// 구현 클래스
class Dog implements Animal {
    public void speak() {
        System.out.println("Woof!");
    }
}

class Cat implements Animal {
    public void speak() {
        System.out.println("Meow!");
    }
}

// Factory
class AnimalFactory {
    public static Animal createAnimal(String type) {
        switch (type) {
            case "dog": return new Dog();
            case "cat": return new Cat();
            default: throw new IllegalArgumentException("Unknown type");
        }
    }
}

// 사용
Animal dog = AnimalFactory.createAnimal("dog");
dog.speak();  // Woof!

Abstract Factory 패턴

// Abstract Factory
interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

// Concrete Factory - Windows
class WindowsFactory implements GUIFactory {
    public Button createButton() {
        return new WindowsButton();
    }
    public Checkbox createCheckbox() {
        return new WindowsCheckbox();
    }
}

// Concrete Factory - Mac
class MacFactory implements GUIFactory {
    public Button createButton() {
        return new MacButton();
    }
    public Checkbox createCheckbox() {
        return new MacCheckbox();
    }
}

Builder vs Factory 비교

특성 Builder Factory
목적 복잡한 객체의 단계적 생성 객체 생성 로직 캡슐화
반환 하나의 복잡한 객체 다양한 타입의 객체
사용 시점 많은 속성을 가진 객체 객체 타입을 런타임에 결정
메서드 체이닝 일반적으로 사용 보통 단일 메서드

MVP (Model-View-Presenter) 패턴

MVP는 프레젠테이션 로직을 분리하는 아키텍처 패턴입니다.

구성 요소

  • Model: 데이터와 비즈니스 로직
  • View: UI 표시, 사용자 입력 처리 (Presenter에 위임)
  • Presenter: View와 Model 사이의 중재자

MVC vs MVP vs MVVM

패턴 특징 데이터 바인딩
MVC Controller가 View 선택 없음
MVP Presenter가 View 업데이트 없음
MVVM ViewModel이 View와 양방향 바인딩 있음

Android에서 MVP 구현

// Contract 정의
interface UserContract {
    interface View {
        void showUser(User user);
        void showError(String message);
        void showLoading();
        void hideLoading();
    }

    interface Presenter {
        void loadUser(String userId);
        void onDestroy();
    }
}

// Presenter 구현
class UserPresenter implements UserContract.Presenter {
    private UserContract.View view;
    private UserRepository repository;

    public UserPresenter(UserContract.View view, UserRepository repository) {
        this.view = view;
        this.repository = repository;
    }

    @Override
    public void loadUser(String userId) {
        view.showLoading();
        repository.getUser(userId, new Callback<User>() {
            @Override
            public void onSuccess(User user) {
                view.hideLoading();
                view.showUser(user);
            }

            @Override
            public void onError(String error) {
                view.hideLoading();
                view.showError(error);
            }
        });
    }

    @Override
    public void onDestroy() {
        view = null;
    }
}

// Activity (View) 구현
class UserActivity extends AppCompatActivity implements UserContract.View {
    private UserContract.Presenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        presenter = new UserPresenter(this, new UserRepository());
        presenter.loadUser("123");
    }

    @Override
    public void showUser(User user) {
        // UI 업데이트
    }

    @Override
    public void showError(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showLoading() {
        // 로딩 표시
    }

    @Override
    public void hideLoading() {
        // 로딩 숨김
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.onDestroy();
    }
}

기타 유용한 패턴

Singleton 패턴

// Thread-safe Singleton (Kotlin)
object DatabaseConnection {
    fun query(sql: String): Result { ... }
}

// Java Double-Check Locking
public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Observer 패턴

// Observable
interface Subject {
    void attach(Observer o);
    void detach(Observer o);
    void notifyObservers();
}

// Observer
interface Observer {
    void update(String message);
}

// 구현
class NewsPublisher implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String latestNews;

    public void attach(Observer o) { observers.add(o); }
    public void detach(Observer o) { observers.remove(o); }

    public void notifyObservers() {
        for (Observer o : observers) {
            o.update(latestNews);
        }
    }

    public void publishNews(String news) {
        this.latestNews = news;
        notifyObservers();
    }
}

Strategy 패턴

// Strategy Interface
interface PaymentStrategy {
    void pay(int amount);
}

// Concrete Strategies
class CreditCardPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " with credit card");
    }
}

class PayPalPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " with PayPal");
    }
}

// Context
class ShoppingCart {
    private PaymentStrategy paymentStrategy;

    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }

    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}

패턴 선택 가이드

상황 추천 패턴
복잡한 객체 생성 Builder
객체 타입 결정 위임 Factory
UI 로직 분리 MVP/MVVM
전역 인스턴스 Singleton
상태 변경 알림 Observer
알고리즘 교체 Strategy
기존 클래스 인터페이스 변환 Adapter
객체 구조 순회 Iterator