본문 바로가기
GD's IT Lectures : 기초부터 시리즈/자바(JAVA) 기초부터 ~

[자바(JAVA)] 자바와 디자인 패턴

by GDNGY 2023. 4. 30.

39. 자바와 디자인 패턴

39.1. 디자인 패턴 개요

디자인 패턴(Design Pattern)은 소프트웨어 개발에서 특정 문제를 해결하는 데 효과적인 설계 방식으로, 재사용 가능한 해결책을 제공합니다. 디자인 패턴은 크게 생성(Creational), 구조(Structural), 행위(Behavioral) 패턴으로 나눌 수 있습니다.

 

39.2. 생성 패턴 (Singleton, Factory, Builder 등)

생성 패턴은 객체 생성에 관련된 패턴으로, 객체 생성 과정을 캡슐화하거나 더 유연한 객체 생성 방식을 제공합니다.

 

  • Singleton 패턴: 클래스의 인스턴스가 단 하나만 존재하도록 보장하는 패턴입니다.
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

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

 

  • Factory 패턴: 객체 생성 로직을 별도의 클래스로 분리하여 캡슐화하는 패턴입니다.
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!");
    }
}

class AnimalFactory {
    public static Animal createAnimal(String type) {
        if (type.equalsIgnoreCase("Dog")) {
            return new Dog();
        } else if (type.equalsIgnoreCase("Cat")) {
            return new Cat();
        }
        return null;
    }
}

public class FactoryExample {
    public static void main(String[] args) {
        Animal dog = AnimalFactory.createAnimal("Dog");
        dog.speak();

        Animal cat = AnimalFactory.createAnimal("Cat");
        cat.speak();
    }
}

 

  • Builder 패턴: 복잡한 객체 생성 과정을 단계별로 나누어 구현하는 패턴입니다.
class Car {
    private String make;
    private String model;
    private int year;

    private Car(Builder builder) {
        this.make = builder.make;
        this.model = builder.model;
        this.year = builder.year;
    }

    public static class Builder {
        private String make;
        private String model;
        private int year;

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

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

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

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

public class BuilderExample {
    public static void main(String[] args) {
        Car car = new Car.Builder()
                .setMake("Toyota")
                .setModel("Camry")
                .setYear(2020)
                .build();
    }
}

 

39.3. 구조 패턴 (Adapter, Decorator, Proxy 등)

구조 패턴은 클래스나 객체들의 구조를 조직하는 패턴으로, 클래스와 객체의 구성을 통해 더 큰 구조를 만들거나 유연성을 제공합니다.

  • Adapter 패턴: 서로 다른 인터페이스를 갖는 클래스들이 함께 작동할 수 있도록 인터페이스를 변환하는 패턴입니다.
 
interface MediaPlayer {
    void play(String fileType, String fileName);
}

interface AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}

class VlcPlayer implements AdvancedMediaPlayer {
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file. Name: " + fileName);
    }

    public void playMp4(String fileName) {
        // Do nothing
    }
}

class Mp4Player implements AdvancedMediaPlayer {
    public void playVlc(String fileName) {
        // Do nothing
    }

    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file. Name: " + fileName);
    }
}

class MediaAdapter implements MediaPlayer {
    private AdvancedMediaPlayer advancedMediaPlayer;

    public MediaAdapter(String fileType) {
        if (fileType.equalsIgnoreCase("vlc")) {
            advancedMediaPlayer = new VlcPlayer();
        } else if (fileType.equalsIgnoreCase("mp4")) {
            advancedMediaPlayer = new Mp4Player();
        }
    }

    public void play(String fileType, String fileName) {
        if (fileType.equalsIgnoreCase("vlc")) {
            advancedMediaPlayer.playVlc(fileName);
        } else if (fileType.equalsIgnoreCase("mp4")) {
            advancedMediaPlayer.playMp4(fileName);
        }
    }
}

public class AdapterExample {
    public static void main(String[] args) {
        MediaPlayer mediaPlayer = new MediaAdapter("vlc");
        mediaPlayer.play("vlc", "example.vlc");

        mediaPlayer = new MediaAdapter("mp4");
        mediaPlayer.play("mp4", "example.mp4");
    }
}

 

  • Decorator 패턴: 기존 객체에 동적으로 새로운 기능을 추가하는 패턴입니다.
interface Coffee {
    double cost();
}

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

abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

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

    public double cost() {
        return decoratedCoffee.cost();
    }
}

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

    public double cost() {
        return super.cost() + 0.50;
    }
}

public class DecoratorExample {
    public static void main(String[] args) {
        Coffee coffee = new SimpleCoffee();
        coffee = new Milk(coffee);
        System.out.println("Total cost: " + coffee.cost());
    }
}

 

  • Proxy 패턴: 실제 객체에 대한 참조를 제공하여 객체의 일부 기능을 대신 수행하는 패턴입니다.
interface Image {
    void display();
}

class RealImage implements Image {
    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk(fileName);
    }

    private void loadFromDisk(String fileName) {
        System.out.println("Loading " + fileName);
    }

    public void display() {
        System.out.println("Displaying " + fileName);
    }
}

class ProxyImage implements Image {
    private RealImage realImage;
    private String fileName;

    public ProxyImage(String fileName) { 
        this.fileName = fileName; 
    }
    
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }
        realImage.display();
    }
}

public class ProxyExample { 
    public static void main(String[] args) { 
        Image image = new ProxyImage("example.jpg");

        // 이미지 로드 후 출력
        image.display();
        System.out.println("");

        // 이미지 로드 없이 출력
        image.display();
    }
}

 

 

39.4. 행위 패턴 (Observer, Strategy, State 등)

행위 패턴은 객체들 사이의 알고리즘, 상호작용, 책임 분배 등을 정의하는 패턴입니다.

 

  • Observer 패턴: 객체 상태 변경 시 관련 객체들에게 알림을 전달하는 패턴입니다.
import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String message);
}

class Subscriber implements Observer {
    private String name;

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

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

class Channel {
    private List<Observer> subscribers = new ArrayList<>();

    public void subscribe(Observer subscriber) {
        subscribers.add(subscriber);
    }

    public void unsubscribe(Observer subscriber) {
        subscribers.remove(subscriber);
    }

    public void notifySubscribers(String message) {
        for (Observer subscriber : subscribers) {
            subscriber.update(message);
        }
    }
}

public class ObserverExample {
    public static void main(String[] args) {
        Channel channel = new Channel();

        Subscriber subscriber1 = new Subscriber("Subscriber 1");
        Subscriber subscriber2 = new Subscriber("Subscriber 2");

        channel.subscribe(subscriber1);
        channel.subscribe(subscriber2);

        channel.notifySubscribers("New video uploaded!");
    }
}

 

  • Strategy 패턴: 알고리즘을 캡슐화하여 런타임에 동적으로 알고리즘을 변경할 수 있는 패턴입니다.
interface SortingStrategy {
    void sort(int[] numbers);
}

class BubbleSortStrategy implements SortingStrategy {
    public void sort(int[] numbers) {
        // Bubble sort implementation
    }
}

class QuickSortStrategy implements SortingStrategy {
    public void sort(int[] numbers) {
        // Quick sort implementation
    }
}

class Sorter {
    private SortingStrategy strategy;

    public void setStrategy(SortingStrategy strategy) {
        this.strategy = strategy;
    }

    public void sort(int[] numbers) {
        strategy.sort(numbers);
    }
}

public class StrategyExample {
    public static void main(String[] args) {
        Sorter sorter = new Sorter();

        sorter.setStrategy(new BubbleSortStrategy());
        sorter.sort(new int[]{3, 1, 4, 1, 5, 9});

        sorter.setStrategy(new QuickSortStrategy());
        sorter.sort(new int[]{3, 1, 4, 1, 5, 9});
    }
}

 

  • State 패턴: 객체의 내부 상태에 따라 동작을 변경하는 패턴입니다.
interface State {
    void doAction(Context context);
}

class StartState implements State {
    public void doAction(Context context) {
        System.out.println("Start state");
        context.setState(this);
    }
}

class EndState implements State {
    public void doAction(Context context) {
        System.out.println("End state");
        context.setState(this);
    }
}

class Context { 
    private State state;
    public Context() {
        state = null;
    }

    public void setState(State state) {
        this.state = state;
    }

    public State getState() {
        return state;
    }
}

public class StateExample { 
    public static void main(String[] args) { 
        Context context = new Context();

        StartState startState = new StartState();
        startState.doAction(context);
        System.out.println("Current state: " + context.getState().getClass().getSimpleName());

        EndState endState = new EndState();
        endState.doAction(context);
        System.out.println("Current state: " + context.getState().getClass().getSimpleName());
    }
}

 

이렇게 디자인 패턴을 활용하면 코드의 유지보수성, 확장성, 재사용성이 향상됩니다. 자바 초보자라도 이러한 디자인 패턴을 이해하고 적용하면서 실력을 향상할 수 있습니다.

반응형

댓글