본문 바로가기

[JAVA] 프로그래밍에서 사용할 수 있는 디자인 패턴 10가지 예시

1. 싱글톤 패턴 (Singleton Pattern)

이 패턴은 어플리케이션 내에서 특정 클래스의 인스턴스가 하나만 생성되도록 보장합니다. 주로 리소스를 공유해야 할 때 사용됩니다.

public class Singleton {
    // 정적 멤버 변수로 유일한 인스턴스를 저장합니다.
    private static Singleton instance;

    // 생성자를 private으로 선언하여 외부에서 인스턴스화를 막습니다.
    private Singleton() {
    }

    // 인스턴스를 반환하는 정적 메서드를 정의합니다.
    public static Singleton getInstance() {
        // 인스턴스가 없는 경우에만 인스턴스를 생성합니다.
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    // 싱글톤 객체의 동작을 정의합니다.
    public void doSomething() {
        System.out.println("Singleton instance is doing something.");
    }
}
public class Main {
    public static void main(String[] args) {
        // 싱글톤 인스턴스를 가져옵니다.
        Singleton singleton = Singleton.getInstance();

        // 동일한 인스턴스임을 확인합니다.
        Singleton anotherSingleton = Singleton.getInstance();
        System.out.println(singleton == anotherSingleton); // 출력: true

        // 싱글톤 객체의 동작을 호출합니다.
        singleton.doSomething();
    }
}

 

2. 팩토리 패턴 (Factory Pattern)

객체 생성을 하위 클래스로 분리하여 객체를 생성하는 부분을 캡슐화합니다. 이는 코드의 유연성을 높이고 객체 생성에 대한 의존성을 낮출 수 있습니다.

// 제품을 나타내는 인터페이스
interface Product {
    void operation();
}

// 구체적인 제품 클래스 1
class ConcreteProduct1 implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProduct1 operation");
    }
}

// 구체적인 제품 클래스 2
class ConcreteProduct2 implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProduct2 operation");
    }
}

// 팩토리 인터페이스
interface Factory {
    Product createProduct();
}

// 각 제품에 대한 팩토리 클래스
class ConcreteFactory1 implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProduct1();
    }
}

class ConcreteFactory2 implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProduct2();
    }
}

// 클라이언트 코드
public class Main {
    public static void main(String[] args) {
        Factory factory1 = new ConcreteFactory1();
        Product product1 = factory1.createProduct();
        product1.operation();

        Factory factory2 = new ConcreteFactory2();
        Product product2 = factory2.createProduct();
        product2.operation();
    }
}

 

3. 추상 팩토리 패턴 (Abstract Factory Pattern)

관련된 객체들의 집합을 생성하기 위한 인터페이스를 제공하며, 각각의 구상 클래스의 생성을 위임합니다. 이는 객체들 간의 조합이나 의존성을 만들어 내는데 사용됩니다.

// 버튼을 나타내는 인터페이스
interface Button {
    void paint();
}

// 윈도우 버튼 클래스
class WinButton implements Button {
    @Override
    public void paint() {
        System.out.println("Windows Button");
    }
}

// 맥 버튼 클래스
class MacButton implements Button {
    @Override
    public void paint() {
        System.out.println("Mac Button");
    }
}

// 텍스트 필드를 나타내는 인터페이스
interface TextField {
    void paint();
}

// 윈도우 텍스트 필드 클래스
class WinTextField implements TextField {
    @Override
    public void paint() {
        System.out.println("Windows TextField");
    }
}

// 맥 텍스트 필드 클래스
class MacTextField implements TextField {
    @Override
    public void paint() {
        System.out.println("Mac TextField");
    }
}

// 추상 팩토리 인터페이스
interface GUIFactory {
    Button createButton();
    TextField createTextField();
}

// 윈도우용 팩토리 클래스
class WinFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WinButton();
    }

    @Override
    public TextField createTextField() {
        return new WinTextField();
    }
}

// 맥용 팩토리 클래스
class MacFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new MacButton();
    }

    @Override
    public TextField createTextField() {
        return new MacTextField();
    }
}

// 클라이언트 코드
public class Main {
    public static void main(String[] args) {
        GUIFactory factory1 = new WinFactory();
        Button button1 = factory1.createButton();
        button1.paint();
        TextField textField1 = factory1.createTextField();
        textField1.paint();

        GUIFactory factory2 = new MacFactory();
        Button button2 = factory2.createButton();
        button2.paint();
        TextField textField2 = factory2.createTextField();
        textField2.paint();
    }
}

 

4. 스트래티지 패턴 (Strategy Pattern)

알고리즘군을 정의하고, 각각을 캡슐화하며, 이들을 교환하여 사용합니다. 이는 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘을 변경할 수 있도록 합니다.

// 전략을 나타내는 인터페이스
interface Strategy {
    void execute();
}

// 구체적인 전략 클래스 1
class ConcreteStrategy1 implements Strategy {
    @Override
    public void execute() {
        System.out.println("Executing strategy 1");
    }
}

// 구체적인 전략 클래스 2
class ConcreteStrategy2 implements Strategy {
    @Override
    public void execute() {
        System.out.println("Executing strategy 2");
    }
}

// 컨텍스트 클래스
class Context {
    private Strategy strategy;

    // 전략을 설정하는 메서드
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    // 전략을 실행하는 메서드
    public void executeStrategy() {
        if (strategy != null) {
            strategy.execute();
        } else {
            System.out.println("No strategy set.");
        }
    }
}

// 클라이언트 코드
public class Main {
    public static void main(String[] args) {
        // 컨텍스트 생성
        Context context = new Context();

        // 첫 번째 전략을 설정하고 실행
        Strategy strategy1 = new ConcreteStrategy1();
        context.setStrategy(strategy1);
        context.executeStrategy();

        // 두 번째 전략을 설정하고 실행
        Strategy strategy2 = new ConcreteStrategy2();
        context.setStrategy(strategy2);
        context.executeStrategy();
    }
}

 

5. 옵저버 패턴 (Observer Pattern)

객체 사이에 일대다 의존 관계를 정의합니다. 어떤 객체의 상태가 변하면, 그 객체에 의존하는 다른 객체들에게 자동으로 알림이 가도록 합니다.

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

// 주제를 나타내는 인터페이스
interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

// 옵저버를 나타내는 인터페이스
interface Observer {
    void update(String message);
}

// 실제 주제 클래스
class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String message;

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

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

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }

    // 상태 변경 메서드
    public void setMessage(String message) {
        this.message = message;
        notifyObservers();
    }
}

// 실제 옵저버 클래스
class ConcreteObserver implements Observer {
    private String name;

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

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

// 클라이언트 코드
public class Main {
    public static void main(String[] args) {
        // 주제 생성
        ConcreteSubject subject = new ConcreteSubject();

        // 옵저버들 생성 및 등록
        Observer observer1 = new ConcreteObserver("Observer 1");
        Observer observer2 = new ConcreteObserver("Observer 2");
        subject.registerObserver(observer1);
        subject.registerObserver(observer2);

        // 상태 변경 및 옵저버들에게 알림
        subject.setMessage("New message!");
    }
}

 

6. 빌더 패턴 (Builder Pattern)

복잡한 객체의 생성 과정을 캡슐화하여, 동일한 생성 과정에서 서로 다른 표현 결과를 만들어 낼 수 있도록 합니다.

// 복잡한 객체를 나타내는 클래스
class Product {
    private String part1;
    private String part2;
    private String part3;

    // 생성자를 private로 선언하여 외부에서 직접 객체 생성을 막고, 빌더를 통해 생성하도록 합니다.
    private Product(Builder builder) {
        this.part1 = builder.part1;
        this.part2 = builder.part2;
        this.part3 = builder.part3;
    }

    // 복잡한 객체를 생성하는 빌더 클래스
    static class Builder {
        private String part1;
        private String part2;
        private String part3;

        // 필수 파라미터를 받는 생성자
        public Builder(String part1) {
            this.part1 = part1;
        }

        // 선택적 파라미터를 설정하는 메서드들
        public Builder part2(String part2) {
            this.part2 = part2;
            return this;
        }

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

        // 복잡한 객체를 생성하는 메서드
        public Product build() {
            return new Product(this);
        }
    }

    // 객체의 정보를 출력하는 메서드
    public void showInfo() {
        System.out.println("Part 1: " + part1);
        System.out.println("Part 2: " + part2);
        System.out.println("Part 3: " + part3);
    }
}

// 클라이언트 코드
public class Main {
    public static void main(String[] args) {
        // 빌더를 사용하여 복잡한 객체를 생성합니다.
        Product product = new Product.Builder("Part 1")
                                    .part2("Part 2")
                                    .part3("Part 3")
                                    .build();
        
        // 생성된 객체의 정보를 출력합니다.
        product.showInfo();
    }
}

 

7. 프록시 패턴 (Proxy Pattern)

다른 객체에 대한 인터페이스를 제공하여, 해당 객체에 대한 접근을 제어하거나 추가 기능을 제공합니다. 예를 들어, 원격 서비스에 대한 접근을 제어하는 데 사용될 수 있습니다.

// 주제를 나타내는 인터페이스
interface Subject {
    void request();
}

// 실제 주제 클래스
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 프록시 클래스
class Proxy implements Subject {
    private RealSubject realSubject;

    @Override
    public void request() {
        // 실제 주제 객체를 생성하기 전에 필요한 작업을 수행할 수 있습니다.
        if (realSubject == null) {
            realSubject = new RealSubject();
        }

        // 실제 주제 객체에 요청을 전달합니다.
        realSubject.request();

        // 실제 주제 객체에 요청 후에 추가 작업을 수행할 수 있습니다.
    }
}

// 클라이언트 코드
public class Main {
    public static void main(String[] args) {
        // 프록시를 통해 주제 객체에 접근합니다.
        Proxy proxy = new Proxy();
        proxy.request();
    }
}

 

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

객체에 추가적인 기능을 동적으로 부여할 수 있도록 합니다. 이는 상속을 통해 기능을 확장하는 것보다 유연하고 강력한 방법을 제공합니다.

// 컴포넌트 인터페이스
interface Component {
    void operation();
}

// 구체적인 컴포넌트 클래스
class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("ConcreteComponent operation");
    }
}

// 데코레이터 클래스
abstract class Decorator implements Component {
    protected Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        component.operation();
    }
}

// 구체적인 데코레이터 클래스 1
class ConcreteDecorator1 extends Decorator {
    public ConcreteDecorator1(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        addedBehavior();
    }

    private void addedBehavior() {
        System.out.println("Added behavior from ConcreteDecorator1");
    }
}

// 구체적인 데코레이터 클래스 2
class ConcreteDecorator2 extends Decorator {
    public ConcreteDecorator2(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        addedBehavior();
    }

    private void addedBehavior() {
        System.out.println("Added behavior from ConcreteDecorator2");
    }
}

// 클라이언트 코드
public class Main {
    public static void main(String[] args) {
        // 기본 컴포넌트 객체 생성
        Component component = new ConcreteComponent();

        // 데코레이터를 통해 기본 컴포넌트를 감싸고 추가 기능을 부여
        Component decoratedComponent1 = new ConcreteDecorator1(component);
        Component decoratedComponent2 = new ConcreteDecorator2(decoratedComponent1);

        // 실행
        decoratedComponent2.operation();
    }
}

 

9. 팩사드 패턴 (Facade Pattern)

서브 시스템에 있는 인터페이스 집합에 대한 통합된 인터페이스를 제공합니다. 이는 복잡한 시스템을 더 쉽게 사용할 수 있도록 만듭니다.

// 서브시스템의 복잡한 구조를 단순화하는 팩사드 클래스
class CarFacade {
    private Engine engine;
    private FuelInjector fuelInjector;
    private AirConditioner airConditioner;

    public CarFacade() {
        this.engine = new Engine();
        this.fuelInjector = new FuelInjector();
        this.airConditioner = new AirConditioner();
    }

    // 자동차 시동을 켜는 메서드
    public void startCar() {
        engine.start();
        fuelInjector.injectFuel();
        airConditioner.turnOn();
        System.out.println("Car started!");
    }

    // 자동차 시동을 끄는 메서드
    public void stopCar() {
        engine.stop();
        airConditioner.turnOff();
        System.out.println("Car stopped!");
    }
}

// 엔진 클래스 (서브시스템 구성원)
class Engine {
    public void start() {
        System.out.println("Engine started");
    }

    public void stop() {
        System.out.println("Engine stopped");
    }
}

// 연료 주입기 클래스 (서브시스템 구성원)
class FuelInjector {
    public void injectFuel() {
        System.out.println("Fuel injected");
    }
}

// 에어컨 클래스 (서브시스템 구성원)
class AirConditioner {
    public void turnOn() {
        System.out.println("Air conditioner turned on");
    }

    public void turnOff() {
        System.out.println("Air conditioner turned off");
    }
}

// 클라이언트 코드
public class Main {
    public static void main(String[] args) {
        // 팩사드를 통해 자동차를 조작
        CarFacade carFacade = new CarFacade();
        carFacade.startCar();
        System.out.println("-----");
        carFacade.stopCar();
    }
}

 

10. 커맨드 패턴 (Command Pattern)

요청을 객체로 캡슐화하여 매개변수화하고, 메서드 호출, 큐 또는 로깅과 같은 기능을 지원합니다. 이는 요청을 서로 다른 사용자에게 전달하거나 요청의 순서를 지정하거나 취소할 수 있도록 합니다.

// 커맨드 인터페이스
interface Command {
    void execute();
}

// 수신자 클래스
class Receiver {
    public void action() {
        System.out.println("Receiver: Performing action");
    }
}

// 구체적인 커맨드 클래스
class ConcreteCommand implements Command {
    private Receiver receiver;

    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.action();
    }
}

// 인보커 클래스
class Invoker {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void executeCommand() {
        command.execute();
    }
}

// 클라이언트 코드
public class Main {
    public static void main(String[] args) {
        // 수신자 객체 생성
        Receiver receiver = new Receiver();

        // 커맨드 객체 생성 및 수신자와 연결
        Command command = new ConcreteCommand(receiver);

        // 인보커 객체 생성 및 커맨드 설정
        Invoker invoker = new Invoker();
        invoker.setCommand(command);

        // 커맨드 실행
        invoker.executeCommand();
    }
}

 

 

 

반응형
그리드형