학습일지/Language

[Design Pattern] Strategy

inspirit941 2020. 12. 24. 08:20
반응형

Strategy 패턴

여러 알고리즘을 하나의 추상적인 접근점 (인터페이스)을 만들어서, 인터페이스에서 알고리즘이 서로 교환 가능하도록 하는 패턴.

  • 동일한 목적의 알고리즘 여러 개 중 특정 알고리즘을 선택해야 할 때.

인터페이스는 '기능의 선언 (구현부와 분리)' 역할을 담당. 하나의 인터페이스로 여러 가지 기능을 구현하기 위한 단일 통로를 생성하는 것.

ex) 워드에서 프린터 명령어 -> 어느 기종의 프린터를 사용하건 상관없이 프린트 명령어가 실행됨

기능 위임 (Delegation) 예시코드.

// AObject : 객체 정의. 이 객체에서 수행할 기능을 인터페이스에 위임하는 방식
public class AObject {
    BInterface bInterface;

    // 객체 생성 시, 기능을 담당할 인터페이스 구현체를 등록
    public AObject() { bInterface = new BImplement(); }

    public void doFunc() {
        // 메소드 내부에서 직접 기능구현을 담당하지 않음
        // System.out.println("doFunction in AObject");

        // 기능을 실행할 인터페이스에 구현 위임 (Delegate)
        bInterface.funcA();

    }
}

public BInterface() {
    // 기능 선언
    void funcA();   
}
public class BImplement implements BInterface {
    @Override
    public void funcA() { System.out.println("doFunction in BImplement"); }
}

public class TestStrategyPattern {
    public static void main(String[] args) {
        AObject aobj = new AObject();
        // A Object 기능 호출
        aobj.doFunc(); // return doFunction in BImplement
    }
}

상황에 따라 데이터베이스를 바꿔 사용해야 하는 경우

public abstract class Database {
    public String name;
    public double rows;

    // DB마다 접속 라이브러리는 다름
    public abstract void connectDatabase();

    // 표준SQL 형태를 사용한다 (업무처리 방식이 같다)
    public void insertData() {

        System.out.println(name + "에 데이터를 추가했습니다.");
    }

    public void selectData() {

        System.out.println(rows + " rows are selected from " + name);
    }
}

// 각각의 DB종류마다 필요한 로직을 구현
public class MySQL extends Database {
    public MySQL() { name = "MySQL"; rows = 20;}
    @Override
    public void connectDatabase(){
        // MySQL의 DB Connection 로직 입력
    }
}

public class Infomix extends Database {
    public Infomix() { name = "MySQL"; rows = 20;}
    @Override
    public void connectDatabase(){
        // Infomix의 DB Connection 로직 입력
    }
}

enum DBTYPE { MySQL, Infomix }

public class DatabaseUse {
    private Database db;

    // 기능 선택하기
    public void connect(DBTYPE dbType) {
        if (dbType == DBTYPE.MySQL) {
            db = new MySQL();
        }
        else if (dbType == DBTYPE.Infomix) {
            db = new Infomix();
        }

        if (db == null) {
            System.out.println("DB를 선택하세요.");
        } else {
            db.connnectDatabase();
        }
    }

    public void select() { db.selectData(); }
}

public class TestStratgyPattern {
    public static void main(String[] args) {
        DatabaseUse myDB = new DatabaseUse();

        // 특정 DB 선택해서 작업 수행
        myDB.connect(DBTYPE.MYSQL);
        myDB.select();
        // db connection close 이후

        // 다른 DB를 사용하고 싶으면, 동일한 객체에 파라미터만 변경하면 됨
        myDB.connect(DBTYPE.Infomix);
        myDB.select();

    }
}

Oracle DB 연결로직을 구현해야 할 경우?

  1. Database 클래스를 상속받은 Oracle 객체 생성, Enum에 ORACLE 생성
  2. DatabaseUse에서 DBTYPE 파라미터가 ORACLE일 때의 로직 구현 (connect 메소드 수정)

즉, 매번 코드를 수정할 때마다 이곳저곳의 값을 변경해야 함

기능부 / 구현부를 보다 명확히 정의하는 식으로 해결

public class DatabaseUse {
    // 접근점 = 인터페이스
    private Database db;

    // DB 교환이 가능하도록 하는 메소드
    public void setDatabase(Database db) { this.db = db; }

    // 기능 사용
    public void connect() {
        if (db == null) System.out.println("데이터베이스를 선택하세요.");
        else {
            // 구체적인 DB종류를 입력받지 않아도 기능을 사용할 수 있도록.
            db.connectDatabase();
            // 이전 코드는 DBTYPE 변수값에 따라 직접 필요한 객체를 생성하는 식의 높은 결합도를 가진 코드
            // 이미 만들어진 db객체를 생성자로 주입받는 '낮은 결합도' 코드로 변경
        }
    }
    public void select() {
        db.selectData();
    }
}


public static void main(String[] args) {
        DatabaseUse myDB = new DatabaseUse();

        // 특정 DB 선택해서 작업 수행
        myDB.setDatabase(new MySQL());
        myDB.connect();
        myDB.select();
        // db connection close 이후

        // 다른 DB를 사용하고 싶으면, 동일한 객체에 파라미터만 변경하면 됨
        myDB.setDatabase(new Infomix());
        myDB.connect();
        myDB.select();        

        // Oracle을 추가하고 싶은 경우 - 아래 코드만 입력하면 실행되도록.
        myDB.setDatabase(new Oracle());
        myDB.connect();
        myDB.select();
    }
반응형