[자바 객체 지향] 스프링이 사랑한 디자인 패턴 2

2021. 9. 6. 23:09CSE/JAVA

1. 템플릿 메서드 패턴 (Template Method Pattern)

public abstract class Animal {

    //템플릿 메서드
    public void playWithOwner(){
        System.out.println("귀염둥이 이리온...");
        play();
        runSomething();
        System.out.println("잘했어");
    }

    //추상 메서드
    abstract void play();

    // Hook(갈고리) 메서드
    void runSomething(){
        System.out.println("꼬리 살랑 살랑~");
    }
}

- 상위 클래스인 Animal에는 템플릿을 제공하는 playWithOwner() 메서드와 하위 클래스에게 구현을 강제하는 play() 추상 메서드, 하위 클래스가 선택적으로 오버라이딩 할 수 있는 runSomething()메서드가 있다.

public class Dog extends Animal{
    @Override
    void play() {
        System.out.println("멍! 멍!");
    }

    @Override
    void runSomething(){
        System.out.println("멍! 멍! 꼬리 살랑살랑~");
    }
}
public class Cat extends Animal{
    @Override
    void play(){
        System.out.println("야옹 야옹~");
    }
}
public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();

        dog.playWithOwner();

        System.out.println();
        System.out.println();

        cat.playWithOwner();
    }
}
/*
귀염둥이 이리온...
멍! 멍!
멍! 멍! 꼬리 살랑살랑~
잘했어


귀염둥이 이리온...
야옹 야옹~
꼬리 살랑 살랑~
잘했어
*/

=> 이처럼 상위 클래스에 공통 로직을 수행하는 템플릿 메서드와 하위 클래스에 오버라이딩을 강제하는 추상 메서드 또는 선택적으로 오버라이딩 할 수 있는 훅(Hook) 메서드를 두는 패턴을 템플릿 메서드 패턴이라고 한다.

 

* 상위 클래스의 견본 메서드에서 하위 클래스가 오버라이딩한 메서드를 호출하는 패턴

* 의존 역전 원칙(DIP)을 활용하고 있다.

 

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

- 팩터리 메서드 : 객체를 생성 반환하는 메서드

- 팩터리 메서드 패턴 : 하위 클래스에서 팩터리 메서드를 오버라이딩 해서 객체를 반환하게 하는 것

public abstract class AnimalToy {
    abstract void identify();
}
public class DogToy extends AnimalToy{
    @Override
    void identify() {
        System.out.println("나는 테니스공! 강아지 장난감!");
    }
}
public class CatToy extends AnimalToy{
    @Override
    void identify() {
        System.out.println("나는 캣타워! 고양이 장난감!");
    }
}
public abstract class Animal {
    //추상 팩터리 메서드
    abstract AnimalToy getToy();
}
public class Dog extends Animal{
    //추상 팩터리 메서드 오버라이딩
    @Override
    AnimalToy getToy(){
        return new DogToy();
    }
}
public class Cat extends Animal{
    //추상 팩터리 메서드 오버라이딩
    @Override
    AnimalToy getToy() {
        return new CatToy();
    }
}
public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();

        AnimalToy dogToy = dog.getToy();
        AnimalToy catToy = cat.getToy();

        dogToy.identify();
        catToy.identify();
    }
}

* 오버라이드된 메서드가 객체를 반환하는 패턴

* 의존 역전 원칙(DIP)를 활용하고 있음

 

3. 전략 패턴(Strategy Pattern)

- 전략 패턴을 구성하는 세 요소

  • 전략 메서드를 가진 전략 객체
  • 전략 객체를 사용하는 컨텍스트(전략 객체의 사용자/소비자)
  • 전략 객체를 생성해 컨텍스트에 주입하는 클라이언트(제3자, 전략 객체의 공급자)
public interface Strategy {
    public abstract void runStrategy();
}
public class StrategyGun implements Strategy{
    @Override
    public void runStrategy() {
        System.out.println("총을 쏜다");
    }
}
public class StrategySword implements Strategy{
    @Override
    public void runStrategy() {
        System.out.println("칼을 쓴다");
    }
}
public class StrategyBow implements Strategy{
    @Override
    public void runStrategy() {
        System.out.println("활을 쏜다");
    }
}
public class Soldier {
    void runContext(Strategy strategy){
        System.out.println("전투 시작");
        strategy.runStrategy();
        System.out.println("전투 종료");
    }
}
public class Client {
    public static void main(String[] args) {
        Strategy strategy = null;
        Soldier soldier = new Soldier();

        strategy = new StrategyGun();
        soldier.runContext(strategy);

        strategy = new StrategySword();
        soldier.runContext(strategy);

        strategy = new StrategyBow();
        soldier.runContext(strategy);
    }
}

- 템플릿 메서드 패턴과 유사한데?

=> 같은 문제의 해결책으로 상속을 이용하는 템플릿 메서드 패턴과 객체 주입을 통한 전략 패턴 중에서 선택/적용할 수 있다. (주로 전략 패턴 많이 사용)

 

* 클라이언트가 전략을 생성해 전략을 실행할 컨텍스트에 주입하는 패턴

* 개방 폐쇄 원칙 (OCP)과 의존 역전 원칙(DIP)이 적용됨

 

4. 템플릿 콜백 패턴(Template Callback Pattern - 견본/회신 패턴)

- 전략 패턴의 변형으로, 스프링의 3대 프로그래밍 모델 중 하나인 DI(의존성 주입)에서 사용하는 특별한 형태의 전략 패턴

=> 전략을 익명 내부 클래스로 정의해서 사용한다는 특징이 있다.

public interface Strategy {
    public abstract void runStrategy();
}
public class Soldier {
    void runContext(String weaponSound){
        System.out.println("전투 시작");
        executeWeapon(weaponSound).runStrategy();
        System.out.println("전투 종료");
    }

    private Strategy executeWeapon(final String weaponSound){
        return new Strategy() {
            @Override
            public void runStrategy() {
                System.out.println(weaponSound);
            }
        };
    }
}
public class Client {
    public static void main(String[] args) {
        Soldier soldier = new Soldier();
        
        soldier.runContext("총을 쏜다");

        System.out.println();
        
        soldier.runContext("칼을 쓴다");

        System.out.println();
        
        soldier.runContext("활을 쏜다");
    }
}

* 전략을 익명 내부 클래스로 구현한 전략 패턴

* 전략 패턴과 마찬가지로 개방 폐쇄 원칙(OCP)과 의존 역전 원칙(DIP)이 적용된 설계 패턴이다.