자기개발/검색한 자료 정리

Java에서 디자인 패턴 실전 적용 사례

실버블렛 2023. 4. 23. 19:00
반응형

Java에서 디자인 패턴 실전 적용 사례

디자인 패턴은 소프트웨어 개발에서 반복적으로 발생하는 문제를 해결하기 위한 표준화된 방법입니다. 이 글에서는 Java를 사용하여 다양한 디자인 패턴을 실전에 적용하는 사례를 살펴봅니다.

1. 싱글턴 패턴 (Singleton Pattern)

싱글턴 패턴은 클래스의 인스턴스가 하나만 생성되고, 애플리케이션 전체에서 공유되도록 보장하는 패턴입니다. 대표적으로 로그 관리, 설정 정보 관리 등에 사용됩니다.

예제:

public class Logger {
    private static Logger instance;

    private Logger() {
    }

    public static synchronized Logger getInstance() {
        if (instance == null) {
            instance = new Logger();
        }
        return instance;
    }

    public void log(String message) {
        System.out.println("Log: " + message);
    }
}

2. 팩토리 메서드 패턴 (Factory Method Pattern)

팩토리 메서드 패턴은 객체 생성 로직을 서브 클래스로 분리하여, 클라이언트가 직접 객체를 생성하지 않고, 팩토리를 통해 객체를 생성하는 패턴입니다. 이 패턴은 객체 생성 로직의 변경에 유연하게 대응할 수 있습니다.

예제:

public interface Animal {
    void speak();
}

public class Dog implements Animal {
    @Override
    public void speak() {
        System.out.println("Woof!");
    }
}

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

public abstract class AnimalFactory {
    public abstract Animal createAnimal();
}

public class DogFactory extends AnimalFactory {
    @Override
    public Animal createAnimal() {
        return new Dog();
    }
}

public class CatFactory extends AnimalFactory {
    @Override
    public Animal createAnimal() {
        return new Cat();
    }
}

javaCopy code

3. 옵저버 패턴 (Observer Pattern)

옵저버 패턴은 객체 간의 일대다 의존 관계를 구현하여, 한 객체의 상태가 변경되면 의존하는 다른 객체들에게 알림을 전달하는 패턴입니다. 이 패턴은 이벤트 처리, 데이터 바인딩 등에서 사용됩니다.

예제:

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

public class Subscriber implements Observer {
    private String name;

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

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

public class Publisher {
    private List < Observer > observers = new ArrayList < > ();

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

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

    public void notifyObservers(String message) {
        for (Observer observer: observers) {

            observer.update(message);
        }
    }
}

4. 전략 패턴 (Strategy Pattern)

전략 패턴은 객체의 행위를 동적으로 변경할 수 있도록, 알고리즘을 클래스로 캡슐화하는 패턴입니다. 이 패턴은 동적으로 알고리즘을 교체해야 하는 경우에 사용됩니다.

예제:

public interface PaymentStrategy {
    void pay(int amount);
}

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

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

public class ShoppingCart {
    private List<Item> items;
    private PaymentStrategy paymentStrategy;

    public ShoppingCart(PaymentStrategy paymentStrategy) {
        this.items = new ArrayList<>();
        this.paymentStrategy = paymentStrategy;
    }

    public void addItem(Item item) {
        items.add(item);
    }

    public void pay() {
        int total = items.stream().mapToInt(Item::getPrice).sum();
        paymentStrategy.pay(total);
    }

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

5. 데코레이터 패턴 (Decorator Pattern)

데코레이터 패턴은 객체에 새로운 기능을 동적으로 추가할 수 있도록, 객체를 다른 객체로 감싸는 패턴입니다. 이 패턴은 상속 대신 기능을 확장하고 싶은 경우에 사용됩니다.

예제:

public interface Coffee {
    String getDescription();
    double getCost();
}

public class SimpleCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Simple Coffee";
    }

    @Override
    public double getCost() {
        return 1.0;
    }
}

public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee decoratedCoffee) {
        this.decoratedCoffee = decoratedCoffee;
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost();
    }
}

public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", Milk";
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.5;
    }
}

public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", Sugar";
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.2;
    }
}

이러한 디자인 패턴들은 개발자들이 효율적이고 안정적인 소프트웨어를 개발할 수 있도록 도와줍니다. 패턴을 잘 이해하고 실전에 적용하는 것이 중요합니다.

반응형