파란실버라이트 2013. 1. 8. 10:45

 

기본 아이디어

State 패턴은 객체의 내부 상태에 따라 행위를 변경할 수 있도록 해준다. 이렇게 하면 객체는 마치 클래스를 바꾸는 것처럼 보인다.

 

 

- 오토 커밋과 같은 행위를 Gof의 State 패턴을 사용하여 구현, State 패턴은 상태가 바뀔 때 행위가 바뀌게 되는 객체들을 조직화할 수 있는 방법을 제공한다. 아래 예제에서는 내 개 메소드(close,commit, rollback, setAutoCommit) 행위가 커넥션이 오토 커밋되어 있는지 아닌지 따라 바뀌게 된다.

 

 

상태에 따라 변화하는 행위를 구현하는 가장 단순한 방법은 현재 상태를 나타내는 필드를 두고, 각 메소드에서 필드 값을 처리하는 switch 문을 두어 case 문이 현재 상태화 관련된 행위를 제공하는 것이다.

 

 class ChangesBehaviorWithState
{
    private static int state = 0;


    public void methodOne()
    {

    switch( state )
    {
        case 0: /* behavior for state 0 goes here */ break;
        case 1: /* behavior for state 1 goes here */ break;
        case 2: /* behavior for state 2 goes here */ break;
    }
}


public void methodTwo()
{

switch( state )
{
    case 0: /* behavior for state 0 goes here */ break;
    case 1: /* behavior for state 1 goes here */ break;
    case 2: /* behavior for state 2 goes here */ break;
}

}
//...

}

 

위 접근 방법은 2가지 문제점이 있다.

1. Swich 문은 코드를 읽기 어렵게 만든다

2. 특정 상태와 연관되어 있는 해위를 제공하는 부분이 코드 전반에 흩어져 있기 때문에 유지보수가 어렵게 된다.

그러므로 내부의 상태머신(어떻게 한 상태에서 다른 상태로 바뀌는지, 그리고 각상태와 연관된 행위를 결정하는 룰)을 바꾸는 것이 굉장히 어렵게 된다. 

=> State 패턴으로 이 문제를 해결하자.

 

 

 

 

 

 

 //State Pattern을 사용한 JDBCconnection 객체

public class JDBCConnection extends ConnectionAdapter
{


 private Database database;


 public JDBCConnection(String uri) throws SQLException,
            URISyntaxException,
            IOException
 {

 this( new URI(uri) );
 }

 

 public JDBCConnection(URI uri) throws SQLException,
           IOException
 { 

database = new Database( uri );
 }

 


//State Interface 와 Concrete State 객체 생성

private interface AutoCommitBehavior
{

void close() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void setAutoCommit( boolean enable ) throws SQLException;
}

private AutoCommitBehavior enabled =
new AutoCommitBehavior()
{

public void close() throws SQLException {/* nothing to do */}
public void commit() {/* nothing to do */}
public void rollback() {/* nothing to do */}
public void setAutoCommit( boolean enable )
{ if( enable == false )
{ database.begin();
autoCommitState = disabled;
}
}
};

private AutoCommitBehavior disabled =
new AutoCommitBehavior()
{

public void close() throws SQLException
{ try
{ database.commit();
}
catch( ParseFailure e )
{ throw new SQLException( e.getMessage() );
}
}
public void commit() throws SQLException
{ try
{ database.commit();
database.begin();
}
catch( ParseFailure e )
{ throw new SQLException( e.getMessage() );
}
}
public void rollback() throws SQLException
{ try
{ database.rollback();
database.begin();
}
catch( ParseFailure e )
{ throw new SQLException( e.getMessage() );
}
}
public void setAutoCommit( boolean enable ) throws SQLException
{ try
{ if( enable == true )
{ database.commit();
autoCommitState = enabled;
}
}
catch( ParseFailure e )
{ throw new SQLException( e.getMessage() );
}
}
};


 

//AutoCommitState을 Enabled State 객체로 설정

private AutoCommitBehavior autoCommitState = enabled;

 

 public Statement createStatement() throws SQLException
 { 

return new JDBCStatement(database);
 }

 

public void close() throws SQLException
{

try
{
  autoCommitState.close();

  database.dump();
  database=null; // make the memory reclaimable and
}
catch(IOException e)
{

throw new SQLException( e.getMessage() );

}

}

 

 public void commit() throws SQLException
 { 

autoCommitState.commit();
 }


 public void rollback() throws SQLException
 { 

autoCommitState.rollback();
 }


 public void setAutoCommit( boolean enable ) throws SQLException
 { 

autoCommitState.setAutoCommit(enable);
 }

 

- JDBCConnection 클래스의 네 개 메소드 (Close, commit, rollback, setAutoCommit)의 행위가

커넥션이 오토 커밋 되어 있는지(AutoCommitBehavior enabled) 아닌지(AutoCommitBehavior disabled)에 따라 바뀌게 된다.

=> 새로운 State가 추가되거나 State의 code가 변경되더라고 JDBCconnection Class는 수정이 필요 없어 진다.

 

 

 /** Return true if auto-commit mode is enabled */
 public boolean getAutoCommit() throws SQLException
 { 

return autoCommitState == enabled;

}

}