기본 아이디어
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;
}
}