다양한 패턴

회사에서 다양한 패턴에 대해 공부하는 시간이 있어
시간을 들여 정리해 보았습니다.

 

템플릿 메서드 패턴

틀은 내가 정해줄게! 너는 세부만 채워~!

 

상위 클래스(추상 클래스) 에 알고리즘의 전체 구조를 정의해 놓고, 세부 단계는 하위 클래스가 구현하도록 하는 패턴입니다.

예시

1. 게임 로딩
2. 로그인
3. 캐릭터 생성 or 불러오기 ← 이것만 다름!
4. 게임 시작

 

// 추상 클래스 (틀)
abstract class GameTemplate {
    public final void startGame() {
        load();
        login();
        characterSelect(); // <- 자식이 구현
        play();
    }

    private void load() {
        System.out.println("🔃 게임 로딩");
    }

    private void login() {
        System.out.println("🔐 로그인");
    }

    protected abstract void characterSelect();

    private void play() {
        System.out.println("🎮 게임 시작!");
    }
}


// 자식 클래스 A
class WarriorGame extends GameTemplate {
    protected void characterSelect() {
        System.out.println("🗡️ 전사 캐릭터 선택");
    }
}

// 자식 클래스 B
class MageGame extends GameTemplate {
    protected void characterSelect() {
        System.out.println("🪄 마법사 캐릭터 선택");
    }
}


// 실행
public class Main {
    public static void main(String[] args) {
        GameTemplate game = new WarriorGame();
        game.startGame();

        System.out.println("\n-----\n");

        game = new MageGame();
        game.startGame();
    }
}

 

전략 패턴

전략(방법)은 바꿔 쓸 수 있어!!

 

알고리즘을 인터페이스로 분리해서, 필요에 따라 런타임에 서로 다른 전략으로 바꿔서 쓸 수 있는 패턴입니다.

예시

몬스터가 공격할 때, 전략을 다르게 할 수 있습니다.
근접 공격
원거리 공격
마법 공격

 

// 전략 인터페이스
interface AttackStrategy {
    void attack();
}



// 전략 구현체들
class MeleeAttack implements AttackStrategy {
    public void attack() {
        System.out.println("👊 근접 공격!");
    }
}

class RangedAttack implements AttackStrategy {
    public void attack() {
        System.out.println("🏹 원거리 공격!");
    }
}



// 전략을 사용하는 캐릭터
class Character {
    private AttackStrategy strategy;

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

    public void performAttack() {
        strategy.attack();
    }
}



// 실행
public class Main {
    public static void main(String[] args) {
        Character character = new Character();

        character.setStrategy(new MeleeAttack());
        character.performAttack(); // 👊

        character.setStrategy(new RangedAttack());
        character.performAttack(); // 🏹
    }
}

 

순수 프록시 패턴

진짜 객체 앞에 대리인을 세워서 부가기능을 넣자

 

원래 기능을 가진 객체 앞에 프록시 객체를 만들어서 로깅, 캐싱, 보안 등 추가 기능을 붙이는 방식입니다.

예시

데이터를 처리하는 Service 가 있는데 실행 전에 로그만 찍고 싶다!

 

// 실제 서비스
interface Service {
    void process();
}



class RealService implements Service {
    public void process() {
        System.out.println("📦 데이터 처리 중");
    }
}



// 프록시
class LoggingProxy implements Service {
    private Service target;

    public LoggingProxy(Service target) {
        this.target = target;
    }

    public void process() {
        System.out.println("📝 [프록시] 처리 전 로그!");
        target.process();
        System.out.println("📝 [프록시] 처리 후 로그!");
    }
}



// 실행
public class Main {
    public static void main(String[] args) {
        Service realService = new RealService();
        Service proxy = new LoggingProxy(realService);

        proxy.process(); // 프록시를 통해 실행
    }
}

 

데코레이터 패턴

원래 객체의 기능을 감싸서 계속 추가해나가는 패턴

 

클래스의 기능을 상속 없이 동적으로 확장하고 싶을 때 사용합니다.

예시

케이크를 만들었는데, 위에 딸기, 초콜릿, 생크림을 얹고 싶다!
점점 하나씩 감싸면서 기능을 추가

 

// 기본 케이크
interface Cake {
    String make();
}



class BasicCake implements Cake {
    public String make() {
        return "🎂 케이크";
    }
}



// 데코레이터 추상 클래스
abstract class CakeDecorator implements Cake {
    protected Cake cake;

    public CakeDecorator(Cake cake) {
        this.cake = cake;
    }
}



// 딸기 추가
class Strawberry extends CakeDecorator {
    public Strawberry(Cake cake) {
        super(cake);
    }

    public String make() {
        return cake.make() + " + 🍓 딸기";
    }
}

// 초콜릿 추가
class Chocolate extends CakeDecorator {
    public Chocolate(Cake cake) {
        super(cake);
    }

    public String make() {
        return cake.make() + " + 🍫 초콜릿";
    }
}



// 실행
public class Main {
    public static void main(String[] args) {
        Cake cake = new BasicCake();
        cake = new Strawberry(cake);
        cake = new Chocolate(cake);

        System.out.println(cake.make());
        // 🎂 케이크 + 🍓 딸기 + 🍫 초콜릿
    }
}