🗂️ Etc

디자인 패턴

loose 2023. 8. 6. 05:38
반응형

개인적으로 개발자가 직접 구현할만한 패턴이라고 느끼는 것들.

팩토리 메소드, 템플릿 메소드, 전략 패턴, 싱글톤, 컴포지트 패턴

전략 패턴

 

요약 - 결제 방식 동적으로 변경해서 사용하기.

interface PaymentStrategy {
    void pay(int amount);
}

class CreditCardPayment implements PaymentStrategy {
    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using Credit Card.");
    }
}

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

class ShoppingCart {
    private PaymentStrategy paymentStrategy;

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

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

public class StrategyPatternExample {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        PaymentStrategy creditCardPayment = new CreditCardPayment();
        cart.setPaymentStrategy(creditCardPayment);
        cart.checkout(100);

        PaymentStrategy paypalPayment = new PayPalPayment();
        cart.setPaymentStrategy(paypalPayment);
        cart.checkout(200);
    }
}

Interface를 사용하는 디자인 패턴이다. State 패턴이랑 크게 다를건 없다.

대표적으로 List 처럼 쓰는 것을 말하는데 ArrayList, LinkedList 등등을 끼워서 사용할 수 있는 패턴을 말한다.

 

Interface는 Abstract와는 다른데, Abstract는 추상메소드가 아닌 메소드도 가질 수 있지만 Interface는 오로지 추상메소드로만 이루어져있기 때문에 상속을 받으면 항상 같은 메소드만을 구현해줘야 하는 특성 때문에 '전략'에 맞춰서 다른 클래스를 끼워서 쓸 수 있다는 점에서 전략 패턴이라고 불린다.

 

팩토리 메소드 패턴

요약 - 객체 생성 위임

 

팩토리 메소드 패턴(Factory Method Pattern)은 객체 생성을 위한 패턴 중 하나로, 객체 생성을 별도의 메소드(팩토리 메소드)로 분리하는 디자인 패턴입니다.

정적 팩토리 메소드 패턴과 같은걸 말한다.

단점이 있다면 객체 생성이 이것저것 너무 많아져서 복잡해질 수 있다라는 점.

 

템플릿 메소드 패턴

 

요약 - 순서유지!

템플릿 메소드 패턴은 메소드 순서에 대한 강제화가 필요할 때 조금 더 유용하다. 그렇다고 해서 템플릿 메소드 패턴의 추상화가 만능이라고 생각해서 모든 것을 추상화 처리하는 것은 좋지 않다. 전체-부분에 대한 개념에서는 추상화보단 컴포지트 패턴이 좋다.

 

템플릿 메소드 패턴의 단점은 장점이자 단점인건데, 알고리즘 구조를 고정시키므로, 서브클래스에서의 변화를 제한할 수 있다. 즉, 알고리즘의 수정이 어려울 수 있다.

 

전략 패턴은 하나의 메소드가 여러 방식으로 사용되는 방식이었다면 템플릿 메소드 패턴은 여러개의 메소드가 순서대로 처리 될 때 쓴다. 가령 운동이라고 치면 스트레칭 -> 운동이라는 순서를 지켜주는 것과 마찬가지이다. 스트레칭과 운동의 방식은 달라질 수 있어도 순서는 달라질 수 없을 때 사용한다.

abstract class CoffeeMaker {
    // 템플릿 메소드
    public final void makeCoffee() {
        boilWater();
        brewCoffeeGrounds();
        pourInCup();
        addCondiments();
    }

    protected abstract void boilWater();
    protected abstract void brewCoffeeGrounds();
    protected abstract void pourInCup();

    // 훅 메소드 (선택적으로 오버라이딩 가능)
    protected void addCondiments() {
        // 기본적으로 아무것도 추가하지 않음
    }
}

class AmericanoMaker extends CoffeeMaker {
    protected void boilWater() {
        System.out.println("물 끓이기");
    }

    protected void brewCoffeeGrounds() {
        System.out.println("커피를 내리다");
    }

    protected void pourInCup() {
        System.out.println("컵에 따르다");
    }
}

class LatteMaker extends CoffeeMaker {
    protected void boilWater() {
        System.out.println("물 끓이기");
    }

    protected void brewCoffeeGrounds() {
        System.out.println("커피를 내리다");
    }

    protected void pourInCup() {
        System.out.println("컵에 따르다");
    }

    protected void addCondiments() {
        System.out.println("우유 추가");
    }
}

 

커맨드 패턴

요약 - 직접 구현을 객체 안에 숨겨서 사용한다. 안써도 구현이 돼서 중요도는 떨어진다.

 

기능들을 명령화 시켜서 사용하는 것을 의미한다. 명령들이 캡슐화 되어 보안에 좋은 장점이 있다.


퍼사드 패턴

요약 - 잘 안쓴다. 안 써도 그리 문제가 없다.

퍼사드 패턴은 여러 클래스를 동시 사용해야 가능한 구조 일 때 여러 클래스를 다시 하나의 클래스로 묶는 행위를 말한다.
그렇게 하면 개발자는 하나의 클래스만 사용하면 되는데 이 클래스가 퍼사드(프랑스어로 외벽) 패턴으로 만들어진 클래스라고 한다.

사실 장점이라고 한다면 클래스를 하나만 사용한다는 특징인데, 굳이 이걸 써서 얻을 장점이 거의 없다고 한다.

옵저버 패턴

 

옵저버 패턴은 한 객체의 상태 변경을 다른 객체들에게 통지하는 패턴입니다. 주로 이벤트 처리나 상태 감시에 사용됩니다. 아래는 간단한 코드 예시입니다:

import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String message);
}

class ConcreteObserver implements Observer {
    private String name;

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

    public void update(String message) {
        System.out.println(name + " received message: " + message);
    }
}

class Subject {
    private List<Observer> observers = new ArrayList<>();
    private String message;

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    public void setMessage(String message) {
        this.message = message;
        notifyObservers();
    }

    private void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

public class ObserverPatternExample {
    public static void main(String[] args) {
        Subject subject = new Subject();
        Observer observer1 = new ConcreteObserver("Observer 1");
        Observer observer2 = new ConcreteObserver("Observer 2");

        subject.addObserver(observer1);
        subject.addObserver(observer2);

        subject.setMessage("Hello, observers!");
    }
}

데코레이터 패턴

 

데코레이터 패턴은 객체에 추가적인 기능을 동적으로 덧붙이는 패턴으로, 상속 없이도 기능 확장이 가능합니다. 예를 들어, 커피에 다양한 토핑을 추가할 수 있는 커피 메뉴를 만들 때, 데코레이터 패턴을 사용할 수 있습니다.

 

interface Coffee {
    double cost();
    String description();
}

class SimpleCoffee implements Coffee {
    public double cost() {
        return 2.0;
    }

    public String description() {
        return "커피";
    }
}

class MilkDecorator implements Coffee {
    private Coffee coffee;

    public MilkDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    public double cost() {
        return coffee.cost() + 0.5;
    }

    public String description() {
        return coffee.description() + " + 우유";
    }
}

 

어댑터 패턴

 

스프링에서 거의 쓸 일 없다고 보면 된다.

 

728x90