관리 메뉴

HAMA 블로그

JDBC 와 디자인패턴 - 3. State 패턴 본문

디자인패턴

JDBC 와 디자인패턴 - 3. State 패턴

[하마] 이승현 (wowlsh93@gmail.com) 2015. 7. 30. 21:54

순서


1. 소개 

2. Abstract Factory 패턴 

3. State 패턴

4. chain of Responsibility 패턴 

5. Adapter 패턴

6. Bridge 패턴 

 

http://brad2014.tistory.com/344  <---  1,2편 (abstract factory 패턴) 은 여기링크.


3. State 패턴



* JDBC state 패턴은 위 전체 JDBC 패턴 모식도중 빨강 네모상자 안에서만 이루어집니다.

JDBC state 패턴을 공부하기 전에 잠시 JDBC 의 트랜잭션을 살펴보도록합시다.  


 JDBC 트랜잭션 기초 

JDBC API의 Connection 객체는 트랜잭션을 위하여 commit() 메소드와 rollback() 메소드를 제공한다.

 Connection 객체에는 setAutoCommit  이란 메소드가 있는데 기본값이 true 로 설정이 되어 있다. 

하나의 쿼리당 자동시작~자동커밋이 일어난다는 이야기이다그러나  여러 개의 쿼리 문장이 하나의 작업으로 수행되어야
할경우에  각각의 문장이 자동으로 작동되지 못하게 해야한다.
오토커밋이 자동으로 작동되지 못하게 하려면 
setAutoCommit(false); 
로 지정해야 한다.


자 그럼 begin() 은 어딨느냐?? 


AutoCommit = true 일경우



암시적으로 각각의 액션시 (각각 SQL 문에서)  자동으로 BEGIN()



AutoCommit = false  일경우



이건 좀 희안하다. setAutoCommit(false) 를 호출하여 오토커밋코드를 끄면  시스템은 이 호출과 동시에 


BEGIN 요청을 하게 된다. 이후의 commit() 과 rollback() 함수안에서  각각 작업을 한후에 마지막


에  begin() 을 요청하며 함수가 끝난다. 



그 밖에 setAutoCommit 을 통해 모드를 바꾸면  동시에 commit() 1회 발생한다.




코드1) 

 Connection conn = DriverManager.getConnection(.....); // connection 객체생성

conn.setAutoCommit(false); // 오토커밋 꺼버리고  

Statement stmt = conn.createStatement(); // statement 객체생성 String SQL = "INSERT INTO Employees VALUES (106, 20, 'Rita', 'Tez')";  

stmt.executeUpdate(SQL); conn.commit(); // 수동 커밋 ㄱㄱ 씽~




위의 JDBC 소개를 왜 했냐면 



 JDBC 에는 오토커밋이라는 상태를 가질것이냐, 안가질것이냐~  2가지 상태(STATE) 가 있다라는것을 말하고자 한것이다.







쉽게 설명한 State 패턴~  




state 패턴은 "상태에 기반을 둔 행위를 클래스로 만들어 놓은것이다.  즉 상태별 클래스를 만든것"



무슨이야기인지 실생활에 맞추어 설명해보면 


보통 사람들은   밥을 먹고, 길을 걷고 , TV 를 보는 행위를 한다. <-- 일반적인 행동..(함수) 

하지만 상태에 따라서 조금씩 달라질수있는데,


몸이 굉장히 아픈 상태에서는 = 죽을 천천히 먹을것이며

몸이 가뿐하며 돈이 많은 상태에서는 = 소고기를 폭풍흡입할것이다.


 ( 이런것은 자동차게임같은것에서 '달리다' 는 같지만 부스터를 사용했다처럼 상태를 갖는곳에 모두 사용될수있다)


"먹는다" 라는 행위를 상태 ( 아프냐, 안아프냐 ) 에따라서 다르게 행동하는 함수를 만들자는것인데 ..


사람이라는 클래스가 있다고 해보고 , 다음 코드를 보자.


class Person {

    public eat(){

    }

}


아래처럼  상태에 따라 다른 코드를  모두 클래스 내부에 넣는다면 


class Person {

    boolean state = 0;  // true 은 멀쩡함 , false 은 아픔 

   

    public eat(){

       if(state == true){

           // 폭풍흡입

       }

       else {

           // 겨우 목구멍에 넘김

       }

    }

 

}


이렇게 될것이다.  보통 이렇게도 많이 짠다. 이게 가독성 및 유지보수에 더 좋을수도있다.디자인패턴을 써서 퍼트리는것보다


(무조건적인 패턴 남발이 좋은건 아니다. 잘 판단해서 사용하자) 


State 패턴에서는 아래와 같이 짜는데 


위에 언급했듯이 state 패턴은 "상태에 기반을 둔 행위를 클래스로 만들어 놓은것이다. "


따라서 상태 ( 아프냐, 안아프냐를 ) 를 클래스로 만들고 상태에 기반을 둔 행위( eat , walk ) 를 함수로 만들어보자.


interface State{

  

    public void eat();

    public void walk();


}


class PainState implements Eat{


public void eat(){

     // 죽먹기

}


public void walk(){

...

}


}


class NormalState implements Eat{


public vodi eat(){

    // 고기 폭풍흡입 

}

public void walk(){

...

}


}


이렇게 상태에 따라서 먹는 클래스를 만들게 되는데, 전체 코드를 대략 만들어보면~(익명내부클래스로 인터페이스 상속기법)


class Person {

   

    private interface BodyState {   // 상태마다 공통으로 가져야할 행위를 나타내는 인터페이스

void eat();

void walk();

void setBodyState(boolean state);

    }


    private BodyState normal = new BodyState() {    // 멀쩡할때의 행위를 구현한 클래스


public void eat(){ .... 잘먹는다 ....}

public void walk(){ .... 잘 걷는다 ....}

public void setBodyState(boolean state){

if(state == false){

currentBodyState = pain;   // 상태 바꾸기

}

}

   }


    private BodyState pain = new BodyState() {    // 아플때  행위를 구현한 클래스


public void eat(){ .... 못 먹는다 ....}

public void walk(){ .... 못 걷는다 ....}

public void setBodyState(boolean state){

if(state == true){

currentBodyState = normal;   // 상태 바꾸기

}

}

   }



    public eat(){

        currentBodyState.eat();   // 이렇게 if 혹은 switch 문을 제거하였다. 현재 상태에 따라서 행동하게됨.

    }


public walk(){

currentBodyState.walk();   // 이렇게 if 혹은 switch 문을 제거하였다. 현재 상태에 따라서 행동하게됨.

}


    public setBodyState(boolean state){   // 외부에서 상태를 바꿔줌 ( 이 예에서는 하느님이 바꾸어주시겠지요 ^^) 

         currentBodyState.setBodyState(state); 

    }

 

    private State  currentBodyState = normal;

}



자 간단한 State 패턴 활용예를 살펴보았다. 이제 본격적으로 JDBC 에서 어떻게 활용되는지 살펴보자.


일단 위의 예를 이해했다면 JDBC 예는 누워서 떡먹기이다.





 JDBC 에서의 State 패턴 



 JDBC 에는 오토커밋이라는 상태를 가질것이냐, 안가질것이냐~  2가지 상태(STATE) 가 있다. 를 먼저 상기해보자.


 JDBC 의 connection 클래스 내부에서 저 오토커밋의 상태에 따라서 rollback, commit 가 다르게 일어난다.


다음 클래스 다어그램을 살펴보자.



 

               (그림 1) 


 이것은 (그림1) 위의 일반적인 State 패턴 예에서의 클래스 다이어그램이다.  사람 클래스가 상태클래스를 포함하고 있으며 


상태클래스는 Normal , Pain 2가지를 가지고있다는것을 확인하자. 





                                                                      (그림 2)

      

    이것은 JDBC 에서의 State 패턴인데, 위의 그림 1 과 똑같다. JDBCConnection 은 AutoCommitBehavior 을 구현한 


         concrete State 들에 따라서 행동을 하게된다.


코드를 살펴보면 이해가 더 빠를것이다.


다시 JDBC 의 수동커밋에 대한 예제 코드를 다시 보자


Connection conn = DriverManager.getConnection(.....); // connection 객체생성

conn.setAutoCommit(false); // 오토커밋 꺼버리고  

Statement stmt = conn.createStatement(); // statement 객체생성 String SQL = "INSERT INTO Employees VALUES (106, 20, 'Rita', 'Tez')";  

stmt.executeUpdate(SQL); conn.commit(); // 수동 커밋 ㄱㄱ 씽~


 conn.setAutoCommit(false)이 부분이 어떻게 구현되었는지 JDBCConnection 코드를 통해 살펴보자.


        

 public class JDBCConnection extends java.sql.Connection {


     public void commit() {

         autoCommitState.commit();    // 오토커밋 상태에 따라서  commit 을 처리한다.

     }


     public void rollback(){

autoCommitState.rollback();    // 오토커밋 상태에 따라서  rollback을 처리한다.

    }


    publci void setAutoCommit( boolean enable ){      // 이게 위의 예제에서  conn.setAutoCommit(false)이다.

autoCommitState.setAutoCommit(enable);

    }


    ....


    // 내부에 상태 인터페이스 및 내부익명클래스로 상태 클래스 2개를  만들어준다.

    private interface AutoCommitBehavior{

       void close();

       void commit();

       void rollback();

       void setAutoCommit( boolean enable );

    }

   

    private AutoCommitBehavior  enabled = new AutoCommitBehavior{   

   void close() { /* 아무것도 안한다 */ }

       void commit() { /* 아무것도 안한다 */ }

       void rollback() { /* 아무것도 안한다 */ }

       void setAutoCommit( boolean enable ){

if(enable == false){

   database.begin();

   autoCommitState = disabled;

}

       }


   }


   private AutoCommitBehavior  disabled = new AutoCommitBehavior{   

   void close() { 

       database.commit();

   }

       void commit() {

           database.commit();

           database.begin();

        }

       void rollback() 

           database.rollback();

           database.begin();

        }

       void setAutoCommit( boolean enable ){

if(enable == true){

   database.begin();

   autoCommitState = enabled;

}

       }


   }


    private AutoCommitBehavior  autoCommitState = enabled;

 }


코드를 유심히 살펴보면, 사실 State 패턴에 관한건 금방 이해할것이다.  autoCommitState 의 상태에 따라서 


rollback, commit 등이 이루어지고있다.패턴과 상관없이 기능적으로 코드 몇군데를 보면 보통 우리는 원격의 


DB에 접근을 하기때문에 dabatase 로 RPC 콜을 날려줄 것지만, 1부에 말했다시피, 임베디드DB 이기때문에 


database.commit() 처럼 직접 호출해주는 부분이 있다.


그 밖에 헤깔릴수있는 부분은 , 


왜 database.begin(); 이 commit / rollback 함수 안에 있냐 ? 


왜 setAutoCommit 을 호출하면 database.begin(); 을 해주냐? 


인데, 이것이 궁금하다면  이 게시물 가장 처음에 있는  JDBC 트랜잭션 기초를 다시 읽어보자~


다음엔 Adapter 패턴에 대해서 알아보자.



레퍼런스 :  실용주의 디자인 패턴 (http://www.kangcom.com/sub/view.asp?sku=200607180006)




p.s  해당글에 대한 질문이 올라온것에 대한 답변 



질문: 



State 패턴의 의도가 상태별 클래스를 만들어두는 것이라고 하셨는데,

위 예제처럼 내부에서 사용보다 외부로 빼서 확장성있게도 사용이 이루어지나요?


답변:


보통 외부로 빼서 구현합니다. 본문의 예가 "일반적" 이라고 보기 힘듭니다.

자바라는 언어가 저렇게 내부클래스를 지원하니 그 기능을 십분 발휘한것일뿐이에요 ^^


사실 디자인패턴을 쓰면  소스를 너무 퍼트려 놓는데, 때로는 그게 가독성에 지장을 줄수도

있지 않겠습니까. (if , switch 문이 전체맥락보기 편할때가 많은거 같습니다. 소스수정도 IDE 가 워낙 강력해

서 뭐... 개인적인 생각입니다.) 


따라서 자바 언어의 기능을 살려서,  유연함을 주되,  소스를 찾으러 멀리가지 않게 하였다. 정도로 보시면 될거 같습니다. (클래스 내부에 있는 인터페이스를 보고 전체 상태별 기능을 한눈에 보게하자)


아래 코드처럼 외부 클래스를 컴포지션해서 할수도 있습니다. (DI  로도 )

class people {

    state n_state = new normalState;

    state p_state = new painState;

    state s;

    eat(){

         s.eat();

    }

    setState(int i){

         if(i ==  0)

         	s = n_state;

         else (i == 1)

                s = p_state;

    }

}

interface state{

   eat();

}

class normalState implements state{

     eat(){ ....}

}



class painState implements state{

     eat(){ ....}

}


원하는 답변인지 잘 모르겠네요 ;;;    

혹 다른 생각하시는 구현이 있으면 보여주시면 의사소통이 원할할듯 합니다. 감사합니다.

Comments