#PARAN SILVERLIGHT#
  • Tistory
    • 관리자
    • 글쓰기
Carousel 01
Carousel 02
Previous Next

'DESIGN PATTERN/실용주의 디자인패턴'에 해당되는 글 29건

  • 2013.01.08 Chapter 4- Adaptor 패턴
  • 2013.01.08 Chapter 4- State 패턴
  • 2013.01.08 Chapter 4- Interpreter 패턴
  • 2013.01.08 Chapter 4- Proxy 패턴
  • 2013.01.08 Chapter 4- Chain Of Responsiblity 패턴
  • 2013.01.08 Chapter 4- FlyWeight 패턴
  • 2013.01.08 Chapter 4- Decorator 패턴
  • 2013.01.08 Chapter 4- Strategy 패턴
  • 2013.01.08 Chapter 4- Command 패턴(Undo)
  • 2013.01.07 Chapter 4 - Iterator Pattern

Chapter 4- Adaptor 패턴

DESIGN PATTERN/실용주의 디자인패턴 2013. 1. 8. 10:46

 

기본 아이디어

- Adapter 패턴은 클래스의 인터페이스를 클라이언트가 기대하는 다른 인터페이스로 변화(어댑팅)한다. 이는 호환성 없는 인터페이스 때문에 함께 사용할 수 없는 클래스를 개조하여 함께 작동하도록 해준다.

 

 

 

 

// ResultSet interface를 구현한 ResultSetAdapter

public class ResultSetAdapter implements java.sql.ResultSet 

{

public ResultSetAdapter() {}
public boolean next() throws SQLException {throw new SQLException("ResultSet.next() unsupported");}

public int findColumn(String columnName)throws SQLException 

{throw new SQLException("ResultSet.findColumn(String columnName) unsupported");}


public void updateObject(String colIndex, Object obj) throws SQLException {throw new SQLException("ResultSet.updateObject(String colIndex, Object obj) unsupported");}


public void updateObject(String colIndex, Object obj, int s) throws SQLException {throw new SQLException("ResultSet.updateObject(String colIndex, Object obj, int s) unsupported");}
public Statement getStatement() throws SQLException {throw new SQLException("ResultSet.getStatement() unsupported");}
public void close() throws SQLException {throw new SQLException("ResultSet.close() unsupported");}
public void checkClosed() throws SQLException {throw new SQLException("ResultSet.checkClosed() unsupported");} 

}

 

// ResultSetAdapter를 상속받은 JDBCResultSet

public class JDBCResultSet extends ResultSetAdapter
{
 private     final Cursor cursor;


 private static final NumberFormat  format =
        NumberFormat.getInstance();

 

 /** Wrap a result set around a Cursor. The cursor
  *  should never have been advanced; just pass this constructor
  *  the return value from {@link Table#rows}.
  */


 public JDBCResultSet(Cursor cursor) throws SQLException
 {

        this.cursor = cursor;
 }

 public boolean next()
 { return cursor.advance();
 }

 public String getString(String columnName) throws SQLException
 { try
  { Object contents = cursor.column(columnName);
   return (contents==null) ? null : contents.toString();
  }
  catch( IndexOutOfBoundsException e )
  { throw new SQLException("column "+columnName+" doesn't exist" );
  }
 }

 public double getDouble(String columnName) throws SQLException
 { try
  { String contents = getString(columnName);
   return (contents == null)
     ? 0.0
     : format.parse( contents ).doubleValue()
     ;
  }
  catch( ParseException e )
  { throw new SQLException("field doesn't contain a number");
  }
 }

 public int getInt(String columnName) throws SQLException
 { try
  { String contents = getString(columnName);
   return (contents == null)
     ? 0
     : format.parse( contents ).intValue()
     ;
  }
  catch( ParseException e )
  { throw new SQLException("field doesn't contain a number");
  }
 }

 public long getLong(String columnName) throws SQLException
 { try
  { String contents = getString(columnName);
   return (contents == null)
     ? 0L
     : format.parse( contents ).longValue()
     ;
  }
  catch( ParseException e )
  { throw new SQLException("field doesn't contain a number");
  }
 }

 public void updateNull(String columnName )
 { cursor.update(columnName, null );
 }
 public void updateDouble(String columnName, double value)
 { cursor.update(columnName, format.format(value) );
 }
 public void updateInt(String columnName, long value)
 { cursor.update(columnName, format.format(value) );
 }
 public ResultSetMetaData getMetaData() throws SQLException
 { return new JDBCResultSetMetaData(cursor);
 }
}

 

- Coursor 와 java.sql.ResultSet는 적어도 핵심관점에서는 차이가 없다, 또한 JDBCResultSet class는 Cursor 객채를 래핑하고 있기 때문에 Coursor 객체가 인터페이스를 구현하고 있는 것처럼 보일 수도 있다. 하지만 JDBCResultSet은 Cursor(Adaptee)가 Taget 인터페이스(java.sql.ResultSet)를 구현하고 있는 것처럼 보이도록 하는 Adapter이다.

 

 public final class ArrayIterator implements Iterator
 {
   private int position = 0;
   private final Object[] items;
  
   public ArrayIterator(Object[] items){ this.items = items; }
 
   public boolean hasNext()
   {
     return ( position < items.length );
   }
 
   public Object next()
   {
     if( position >= items.length )
        throw new NoSuchElementException();
      return items[ position++ ];
   }
 
   public void remove()
   {
      throw new UnsupportedOperationException(
      "ArrayIterator.remove()");
   }
 
   /** Not part of the Iterator interface, returns the data
   * set in array form. Modifying the returned array will
   * not affect the iteration at all.
   */
   public Object[] toArray()
   {
      return (Object[]) items.clone();
   }
 }

 

  Arrayiterator 클래스(Adapter)는 배열(Adaptee)이  Iterator 인터페이스를 구현하고 있는 것 처럼 만들어 주기 때문에 배열을 Iterator를 통해 접근할 수 있게 해준다.

 

 

Bridge 패턴 VS Adapter 패턴

  Bridge : 서브시스템들을 분리시키는 거 , 대규모

 Adapter: 어떤 클래스를 이 클래스가 구현하고 있지 않은 인터페이스를 사용하여 작성한 프로그램에 적용시키기 위한 것, 소규모

 Decorator 패턴 VS Adapter 패턴

  Decorator :  어떤 클래스의 메소드를 행위를 상속(Extends)을 하지 않고 변경시키는 것

                    구조 측면에서 보자면 항상 자신이 데코레이팅하는 객체와 같은 인터페이스를 구현한다.

  Adapter :    대부분의 경우 래핑되는 객체의 인터페이스를 구현하지 않는다.

 


- Command pattern과 비슷하다는 생각이 들어서 찾아본 검색 결과 후 답변

The Command design pattern is used to solve problems like:
- How can an object be configured (customized) with a request?
- And how can the request be (ex)changed dynamically at run-time?

The point of Command is to decouple a request from its invoker and encapsulate it in a separate object (Command interface).
Invoker then delegates a request to a command object dynamically.

The Adapter design pattern (object adapter) is used to solve problems like:
- How can an object be accessed that has an incompatible interface
without changing existing interfaces?

The point of Adapter is to work through a separate object that adapts an incompatible interface, i.e., that implements our needed interface (Target) in terms of (by delegating to) the incompatible interface.

The Command pattern is more similar to the Strategy pattern, which decouples an algorithm from its context and encapsulates it in a separate object (Strategy).


 -  Can we say the two patterns have the same implementation but different aims ? 
  Maybe the implementation of the Adapter (object adapter) is somewhat easier: 
  The Adapter pattern implements an existing interface (Target). 
  Clients refer to this interface and needn't to be changed. 

  The Command pattern defines a new interface (Command) and implements different requests. 
  Clients must be designed/implemented/changed so that they delegate a request to one of different command      objects.



저작자표시 비영리 (새창열림)
블로그 이미지

파란실버라이트

To remember the time when I started learning Silver Light!

,

Chapter 4- State 패턴

DESIGN PATTERN/실용주의 디자인패턴 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;

}

}

저작자표시 비영리 (새창열림)
블로그 이미지

파란실버라이트

To remember the time when I started learning Silver Light!

,

Chapter 4- Interpreter 패턴

DESIGN PATTERN/실용주의 디자인패턴 2013. 1. 8. 10:44

 

저작자표시 비영리 (새창열림)
블로그 이미지

파란실버라이트

To remember the time when I started learning Silver Light!

,

Chapter 4- Proxy 패턴

DESIGN PATTERN/실용주의 디자인패턴 2013. 1. 8. 10:44

 

기본 아이디어

어떤 객체(Real Object)를 Real Subject와 동일한 인터페이스를 구현한 다른 중개 객체(Proxy)를 통해 접근하는 것이 땔는 편리할 수 있다는 것이다. 중개 객체는 게으를 초기화(값비싼 필드 등을 필요시 로딩)와 같은 일을 하게 된다.

 

 

/** A Map proxy that handles lazy instantiation of tables from the disk.**/

 

 private final class TableMap implements Map
 {
    private final Map realMap;
    public TableMap( Map realMap )
      {
        this.realMap = realMap;
      }

 }

 

TableMap이 자신이 포함하고 있는 realMap 객체의 프록시가 된다.

 

 

- 리모트 프록시(Remote proxy)

: 네트워크 연결의 한 종단에 존재하며 네트워크의 다른 쪽에 있는 객체와 동일한 인터페이스를 구현한다. 사용자는 프록시에 메시지를 보내며, 프록시가 이 메시지를 네트워크 너머에 있는 실제 객체로 전달하게 된다. 이때 사용자는 프록시를 네트워크 너머에 있는 리포트 객체라 생각한다. 프록시를 통해 일어나는 모든 통신은 투명하게 리포트 객체에 전달되기 때문이다. 자바 RME, COBRA 그리고 XML/SOAP 모두 리포트 프록시 아키택처를 사용하고 있다.

 

 

- 가상 프록시(virtual proxy)

: Java.awt.Image 클래스, 이는 로딩될 실제 이미지에 대한 프록시 기능한다. Runtime.getImage()는 백그라운드 스레드가 네트워크를 통해 실제 이미지를 다운로드 하고 있는 동안, 실제 이미지에 대한 프록시를 반환한다. 이미지가 전부 다운로드 되기 전에도 프록시(getImage()가 반환한 이미지)를 사용할 수 있다. 이미지 다운로드가 끝나년 getImage() 메소드는 프록시가 아닌 실제 이미지를 반환한다.

 

 

 

CF> Decorator 패턴, Proxy 패턴, Chain of Responsibility 패턴 :  정정구조 대신 의도를 보자

 

 

저작자표시 비영리 (새창열림)
블로그 이미지

파란실버라이트

To remember the time when I started learning Silver Light!

,

Chapter 4- Chain Of Responsiblity 패턴

DESIGN PATTERN/실용주의 디자인패턴 2013. 1. 8. 10:42

 

 

기본 정의

 일련의 객체 집합이 잘 정의된 통로(Chain)를 통해 메시지를 전달함으로써 하나 이상의 객체에 메시지를 처리할 수 있는 기회를 준다. 주어진 메시지에 가장 적합한 객체가 메시지를 처리한다. 또한 하나 이상의 객체가 메시지를 처리하는 것도 가능하다.

 

  • Handler : 이벤트 처리 인터페이스와 다음 이벤트 처리 객체로의 링크를 정의한다.
  • Concrete Handler : 요청을 처리, 혹은 아무 일도 하지 않고 이벤트를 다음 객체로 포워딩한다.
  • Dispacher : 이벤트를 각 핸들러에 차례로 전달한다. Handler가 링크드 리스트로 구성되어 있으면 필요하지 않다.

 


저작자표시 비영리 (새창열림)
블로그 이미지

파란실버라이트

To remember the time when I started learning Silver Light!

,

Chapter 4- FlyWeight 패턴

DESIGN PATTERN/실용주의 디자인패턴 2013. 1. 8. 10:42

 

저작자표시 비영리 (새창열림)
블로그 이미지

파란실버라이트

To remember the time when I started learning Silver Light!

,

Chapter 4- Decorator 패턴

DESIGN PATTERN/실용주의 디자인패턴 2013. 1. 8. 10:41

기본 아이디어

Decorator 패턴은 '구현 상속(Extends)' 대신 '인터페이스 상속(Implements)/위임'  전략을 사용하여

깨지기 쉬운 기반 클래스 문제를 해결한다.

 

 

 

 

 public Table select( Selector where )
 { 

Table resultTable = new ConcreteTable( null,
          (String[]) columnNames.clone() );

 

      //result table을 Selector의 전략에 맞게 꺼내오는 부분 생략 
      return new UnmodifiableTable(resultTable);
 }


 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 public Table select(Selector where, String[] requestedColumns )
 { 

 

      Table resultTable = new ConcreteTable( null,
         (String[]) requestedColumns.clone() );

 //result table을 Selector의 전략에 맞게 꺼내오는 부분 생략


       return new UnmodifiableTable(resultTable);
 }

 

 - ConcreteTable을 생성하고, 조회한 후에 UnmodifiableTalbe을 사용하여 실제 결과 테이블을 래핑하여 반환

 

 

 public class UnmodifiableTable implements Table
{ 

       //UnmodifialbeTalbe의 Wrapped 에 Assign 한다.

 private Table wrapped;

 

 public UnmodifiableTable( Table wrapped )
 { 

       this.wrapped = wrapped;
 }

 /** Return an UnmodifieableTable that wraps a clone of the
  *  currently wrapped table. (A deep copy is used.)
  */


 public Object clone() throws CloneNotSupportedException
 { UnmodifiableTable copy = (UnmodifiableTable) super.clone();
  copy.wrapped = (Table)( wrapped.clone() );
  return copy;
 }

 

 public int  insert(String[] c, Object[] v   ){ illegal(); return 0;}
 public int  insert(Object[] v        ){ illegal(); return 0;}
 public int  insert(Collection c,Collection v ){ illegal(); return 0;}
 public int  insert(Collection v      ){ illegal(); return 0;}
 public int  update(Selector w     ){ illegal(); return 0;}
 public int  delete( Selector w     ){ illegal(); return 0;}

 public void begin  (    ){ illegal(); }
 public void commit  (boolean all){ illegal(); }
 public void rollback (boolean all){ illegal(); }

 

 private final void illegal()
 { 

     throw new UnsupportedOperationException();
 }

 public Table select(Selector w,String[] r,Table[] o)
 { 

     return wrapped.select( w, r, o );
 }
 public Table select(Selector where, String[] requestedColumns)
 { 

     return wrapped.select(where, requestedColumns );
 }
 public Table select(Selector where)
 { 

     return wrapped.select(where);
 }
 public Table select(Selector w,Collection r,Collection o)
 { 

     return wrapped.select( w, r, o );
 }
 public Table select(Selector w, Collection r)
 { 

     return wrapped.select(w, r);
 }
 public Cursor rows()
 { 

     return wrapped.rows();
 }
 public void  export(Table.Exporter exporter) throws IOException
 { 

     wrapped.export(exporter);
 }

 

 public String toString()   { return wrapped.toString(); }
 public String name()   { return wrapped.name();   }
 public void  rename(String s){    wrapped.rename(s);  }
 public boolean isDirty()  { return wrapped.isDirty();  }


 public Table extract(){ return wrapped; }

}

 

- Decorater(UnmodifieableTable)은 Table을 수정하는 메소드(insert, update, delete)가 호출 될 때는 예외

- 나머지 메소드들은 래핑된 Decorater에 작업을 위임한다.

 

  정리

   UnmodifiableTable은 ConcreteTable에 있는 여러 메소드의 행위를 변화시켜 Table을 효과적으로 수정 불가능하게 만드는 것이다.

   1. IsImmutable 플래그를 넣어 이 플래그를 검사하게 할 수도 있지만 이 해결방법은 너무 복잡하다.

   2. 클래스를 상속하여 테이블을 수정할 수 있는 메소드들이 예외를 던지도록 오버라이딩할 수도 있었지만

      이는 깨지기 쉬운 기반 클래스 문제를 야기할 수 있다.

      a. 기반 클래스에 테이블을 수정할 수 있는 메소드를 추가했는데 , 파생 클래스에서 이 메소드를 오버라이딩하는 것을 깜빡?

      b. 컴파일 타임 에러를 런타임 에러로 바꾸기 때문에 유지보수하기 어려운 코드를 만들 위험에 노출시키게 된다.

   3. 구현 상속으로 발생할 수 있는 문제를 인터페이스 상속/위임 전략을 통해 해결

      래핑된 객체가 구현하고 있는 인터페이스와 똑같은 인터페이스를 구현한 수에 필요한 경우 연산을 래핑되어 있는 객체에 위임

      (래핑되는 객체와 인터페이스를 공유하지 못한 다면 해당 객체를 기반 클래스로 해야 할 것이다)

    => 이 방법은 Extends를 사용하지 않고 행위를 변경시킬 수 있도록 해주기 때문에 깨지기 쉬운 기반 클래스 문제를 해결해준다.

 

 

 Java I/O 클래스 Decorator 패턴 - 압축되어 있는 바이트 스트림을 읽을 때 데코레이터 체인을 사용한 예

 

 

 

  1. 바이트를 읽는다.

  2. 버퍼링을 사용하여 읽기 작업을 보다 효율적으로 한다.

  3. 바이트 스트림의 압축을 푼다.

 

 자바는 위 문제를 세 개의 클래스를 이용하여 해결한다.

 1. FileInputStream에서 시작하며, 다음과 같이 인스턴스화 한다.

 

try
{

       InputStream in = new FileInputStream( "file.name" );
}
catch( IOException e )
{

       System.err.println( "Couldn't open file.name" );
       e.printStackTrace();
}

 

   2. 버퍼링을 데코레이션(혹은 래핑) 전략을 사용하여 추가한다.

      즉 InputStream 객체를 바이트를 버퍼링하는 또다른 InputStream 구현체를 사용하여 래핑한다.

       래퍼에 바이트를 요청하면 래퍼는 다시 래핑되어 있는 스트림 객체에 여러 바이트를 요청한 후 이를 반환 한다.

 

 

try
{

      InputStream in = new FileInputStream( "file.name" );
      in = new BufferedInputStream( in );
}
catch( IOException e )
{

      System.err.println( "Couldn't open file.name" );
      e.printStackTrace();
} 

 

 3. 압축 해체는 또 다른 데코레이터를 사용하여 추가한다.

  try
{

      InputStream in = new FileInputStream( "file.name" );
      in = new BufferedInputStream( in );
      in = new GZipInputStream( in );
}
catch( IOException e )
{

      System.err.println( "Couldn't open file.name" );

      e.printStackTrace();
}

 

정리

 - 이러한 해결 방법은 유연하다. 원하는 기능을 위해 여러 가지 데코레이터를 조합하여 사용하면 되는 것인다.

 - 더 중요한 것은 각 데코레이터 자체는 단 하나의 문제를 해결하기 때문에 상대적으로 단순하다.

    => 결과적으로 데코레이터들은 작성 및 디버깅하기 쉬우며, 시스템에 다른 영향을 미치지 않으면서 수정이 용이

 - 여러 데코레이터들을 조합하면 여러 다양한 기능을 사용할 수 있다. 데코레이터 자체는 작지만, 합쳐지면 강력하다.

 - Decorator 패턴을 사용하면 향후 원하는 기능을 추가하기도 쉽다.

 - 주요 목적은 구현 상속의 대안을 제공하는 것이다. 기반 클래스의 행위를 수정하거나 기능을 추가하려 한다면  

    Decorator 패턴의   도입을 신중히 검토해 보자

-  Decorator들을 래핑하는 순서도 중요하다.

 

 

 

 Java Collection 클래스 decorator 패턴 사용의 예

 스레드 안정성을 추가하기 위해 핵심 컬렉션 클래스들을 어떻게 확장(Extends)했는지 보여준다.

 결과적으로 구체 클래스들의 숫자가 2배로 늘어났다.

 

 

 컬렉션에 불변 기능을 추가한다면 아마도 Lock() 메소드를 추가하여 컬렉션을 수정하는 메소드들이 예외를 던지게 할 수 있을 것이다.  하지만 구현 상속을 이용하여 이러한 수정을 했을 때 구체 클래스의 숫자는 다시 두 배가 된다.

 

 

 

  Collection 프레임워크 디자이너는 이와 같은 문제를 Decorator 패턴을 사용하여 해결하였다.

 

  Collection threadSafe =
                    Collections.synchronizedCollection( new LinkedList() );

 

 The Collections class looks something like this:

 

public class Collections // utility
{
//...
public static Collection synchronizedCollection(final Collection wrapped)
{

     return new SynchronizedCollection(wrapped);
}


private static class SynchronizedCollection implements Collection
{

     private Collection unsafe;

     public SynchronizedCollection( Collection unsafe )
{

     this.unsafe = unsafe;
}
public synchronized boolean add( Object toAdd )
{

    return unsafe.add( toAdd );
}
public synchronized boolean remove( Object toAdd )
{

     return unsafe.remove( toAdd );
}
// Implement synchronized version of all other Collection
// methods here ...
}
}

 

 

 다음과 같이 이중 래핑을 통해 동기화된 불편 컬렉션을 만들 수도 있다.

 

 Collection myList = new LinkedList();
//...
myList = Collections.synchronizedCollection
(

Collections.unmodifiableCollection
(

      myList
)
);

 

=> Decorator 패턴의 핵심은 총 2개의 클래스를 추가하는 것 만으로도 구현 상속 시 18개의 클래스를 필요로 했던 문제 해결

 

 

 정리

 - Decorator 패턴의 핵심은 객체에 새로운 능력을 추가하거나 객체의 행위를 변화시킬 수 있도록 해준다.

 - Decorator 패턴에서는 상속대신 합성을 사용하여 클래스 계층 구조를 단순화시킨다. 

저작자표시 비영리 (새창열림)
블로그 이미지

파란실버라이트

To remember the time when I started learning Silver Light!

,

Chapter 4- Strategy 패턴

DESIGN PATTERN/실용주의 디자인패턴 2013. 1. 8. 10:41

 

기본 아이디어

Strategy 패턴에서는 문제를 해결하여 여러 알고리즘이 존재할 경우 각 알고리즘을 별도의 클래스로 캡슐화하고, 이를 상호 교환 가능하도록 한다. 이때 각 알고리즘을 갭슐화한 클래스들(Concrete Strategy)은 알고리즘에 접속할 수 있는 인터페이스(Strategy)를 구현한다. 알고리즘을 사용하는 클라이언트(Context)는 Stategy 인터페이스를 통해 Concrete Strategy를 사용한다.

 

 

 

 

 

interface Selector
{
boolean approve( Cursor[] rows );


void modify( Cursor current );


public static class Adapter implements Selector
{

public boolean approve( Cursor[] tables )
{

return true;
}
public void modify( Cursor current )
{

throw new UnsupportedOperationException(
"Can't use a Selector.Adapter in an update");
}
}


public static final Selector ALL = new Selector.Adapter();

}

<Selector.Adapter를 확장>

 

people.delete ( new Selector.Adapter()
{

public boolean approve( Cursor[] tables )
{

 return tables[0].column("lastName").equals("Flintstone");

}

}

 

- Table에 어떤 로우를 선택해야 하는지를 알고 있는 객체를 넘기는 것이다.

   Selector가 바로 선택 전략을 캡슐화하고 있다.

- Delete() 메소드는 Strategy 객체의 approve() 메소드를 각 로우마다 호출

- Approve() 메소드는 해당로우가 삭제되어야 하는지에 대한 Boolean 값을 반환

 

   

<Selector 인터페이스를 직접 구현>

 

   address.update
    (     new Selector()
            {

public boolean approve( Cursor[] tables )
{

      return tables[0].column("state").equals("AZ");
}
public void modify( Cursor current )
{

       current.update("state", "AZ");
}

}

);

 

- 갱신 연산도 삭제와 동일한 방식으로 동작하지만 Selection의 approve() 메소드뿐 아니라 modify() 메소드도 오버라이딩해야

  한다는 점이 다르다.

 

<Concete Table에서 Strategy 객체(Selector)를 사용 하는 코드>

public int  update( Selector where )
 { 
      Results  currentRow = (Results)rows();
      Cursor[] envelope  = new Cursor[]{ currentRow };
      int   updated = 0;

  while( currentRow.advance() )
  { 

    if( where.approve(envelope) )
   { 

        where.modify(currentRow);
        ++updated;
   }
  }

  return updated;
 }


 //----------------------------------------------------------------------
 public int  delete( Selector where )
 { 

     int deleted = 0;

     Results  currentRow = (Results) rows();
     Cursor[] envelope = new Cursor[]{ currentRow };

   

     while( currentRow.advance() )
    { 

        if( where.approve( envelope) )
        { 

            currentRow.delete();
            ++deleted;

         }
  }
      return deleted;
 }

 

- Update()와 Delete()가 하는 일은 순회 코드를 감추고 적절한 Strategy 객체(Selector)를 호출 하는 것이다.

- 이 코드는 또한 Passive Iterator의 변형형을 보여준다. 순회알고리즘은 Concrete Table안에 있으므로

   Selector는 Table 객체들은 순회하는 Passive Iterator라고 할 수 있다.

 

 

 

 

저작자표시 비영리 (새창열림)
블로그 이미지

파란실버라이트

To remember the time when I started learning Silver Light!

,

Chapter 4- Command 패턴(Undo)

DESIGN PATTERN/실용주의 디자인패턴 2013. 1. 8. 09:46

 기본 아이디어

Command Pattern은 요청 혹은 작업의 단위를 캡슐화하여 연산을 이용(호출)하는 객체(예제의 controler, Invoker)와 연산을

실제로 수행하는 객체(위의 Background, Conrete Command) 객체 간의 결합도를 줄여준다.

 

//Runnable interface를 구현한 BackgrounTask Concrete Command 객체 

Runnable backgroundTask = new Runnable()
{
  public void run()
  {
    System.out.println("Hello World");
  }
};

and then pass it to a Thread object:

//Concrete command 객체의 Run을 실행시키는 Invoker(Controler) 객체

Thread controller = new Thread( backgroundTask);

 

You then start up the thread like this:
controller.start();

 

and the controller asks the Command object to do whatever it does by calling run().


 

마지막 수행문이 Command Pattern과 Command 객체를 사용하는 다른 패턴(Stratage pattern 등)을 구분짓게 해주는 핵심이다.

Command 패턴에서는 Invoker(Tread 객체)가 Concrete Command 객체(BackgroundTask)가 무엇을 하는지에 대해 알지 못한다.

 

 


 

 

- Undo System의 구현-

 스냅샷을 이용한  Undo 구현은 비효율적일 뿐만 아니라 연산의 부가 효과를 되돌릴 수 없다. 그러므로 Memento 패턴을 이용하여 Undo를 구현하는 것은 대부분의 경우 현실적이지 않다. Undo는 프로그램을 원래 상태로 되돌리는 것뿐 아니라 데이타베이스의 상태도 이전 상태로 되돌려 놓아야 한다.스냅샷은 프로그램의 상태만을 되돌린다. Command Pattern을 응용해서 사용하면 스냅샷으로 인한 부작용을 없애면서도 Undo 시스템을 멋지게 구현할 수 있다.

 

 

 

  //@cursor-end
 //----------------------------------------------------------------------
 // Undo subsystem.
 //

//Abtstract Command
 private interface Undo
 { 

void execute();
}

 //Concrete Command 객체들
 private class UndoInsert implements Undo
 { 

      private final Object[] insertedRow;
      public UndoInsert( Object[] insertedRow )
  { 

       this.insertedRow = insertedRow;
  }
  public void execute()
  { 

       rowSet.remove( insertedRow );
  }
 }


 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 private class UndoDelete implements Undo
 { 

       private final Object[] deletedRow;
       public UndoDelete( Object[] deletedRow )
  { 

        this.deletedRow = deletedRow;
  }
      public void execute()
  {

        rowSet.add( deletedRow );
  }
 }


 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 private class UndoUpdate implements Undo
 {
  private Object[] row;
  private int cell;
  private Object oldContents;

  public UndoUpdate( Object[] row, int cell, Object oldContents )
  {

   this.row    = row;
   this.cell   = cell;
   this.oldContents = oldContents;
  }

  public void execute()
  { 

         row[ cell ] = oldContents;
  }
 }


 

 

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
public void begin()
{

       transactionStack.addLast( new LinkedList() );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
private void register( Undo op )
{

        ((LinkedList) transactionStack.getLast()).addLast( op );
}
private void registerUpdate(Object[] row, int cell, Object oldContents)
{

     if( !transactionStack.isEmpty() )
        register( new UndoUpdate(row, cell, oldContents) );
}
private void registerDelete( Object[] oldRow )
{

      if( !transactionStack.isEmpty() )
         register( new UndoDelete(oldRow) );
}
private void registerInsert( Object[] newRow )
{

       if( !transactionStack.isEmpty() )
          register( new UndoInsert(newRow) );
}


 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 public void commit( boolean all ) throws IllegalStateException
 { 

if( transactionStack.isEmpty() )
   throw new IllegalStateException("No BEGIN for COMMIT");
  do
  { LinkedList currentLevel =
       (LinkedList) transactionStack.removeLast();

       if( !transactionStack.isEmpty() )
        ((LinkedList)transactionStack.getLast())
            .addAll(currentLevel);

      } while( all && !transactionStack.isEmpty() );
 }

 


 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 public void rollback( boolean all ) throws IllegalStateException
 { 

if( transactionStack.isEmpty() )
   throw new IllegalStateException("No BEGIN for ROLLBACK");
  do
  { LinkedList currentLevel =
       (LinkedList) transactionStack.removeLast();

       while( !currentLevel.isEmpty() )
        ((Undo) currentLevel.removeLast()).execute();

      } while( all && !transactionStack.isEmpty() );
 }
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 //@undo-end
 //-----------------------------------------------------------------

  1.  Begin() 메소드는 새로운 리스트를 스택에 넣는다.
  2.  Regester() 메소드는 Undo 객체를 생성한 뒤 스택의 최상단에 있는 리스트의 끝에 추가한다.(스택이 비어 있으면 아무일도 일어나지 않는다. Begin 메소드가 호출되지 않았다면 스택은 비어있다.)
  3. 마지막으로 Commit()과 Rollback() Method 앞에서 설명한 것과 같이 리스트를 수정하거나 Undo 객체들을 실행시키게 된다.                                  

 => 위 Code에 사용된 패턴은 C#의 Delegate, Event를 사용하는 것과 비슷하다. 등록된 모든 EventArg에게 Event 객체는 통보

http://paransilverlight.tistory.com/191

 

 

 정리

Concrete Command 객체는 Undo 연산을 위해 사용되는 메소드, 그리고 이에 필요한 상태 정보까지 정의 할 수 있다. (연산분 아니라 정보까지 캡슐화할 수 있다는 점이 C 언어의 함수 포인터와 다르다). 그러므로 command 패턴을 사용하여 스냅샷의 부작용인 비효율성을 제거하면서 연산으로 인한 부가 효과까지도 되롤릴 수 있다.

 

 

 

 

저작자표시 비영리 (새창열림)
블로그 이미지

파란실버라이트

To remember the time when I started learning Silver Light!

,

Chapter 4 - Iterator Pattern

DESIGN PATTERN/실용주의 디자인패턴 2013. 1. 7. 17:24

기본 아이디어

Iterator 패턴은 복합 객체 요소를 복합 객체의 구현을 노출시키지 않으면서 순회할 수 있는 방벙를 제공해준다.

 

Iterator의 실체화 코드

 

public final class ArrayIterator implements Iterator
{
     private int     position = 0;
     private final Object[] items;

 /**
  * Create and <code>ArrayIterator</code>.
  * @param items the array whose elements will be returned,
  *  in turn, by each {@link #next} call.
  */
 public ArrayIterator(Object[] items){ this.items = items; }

     public boolean hasNext()
     { 

return ( position < items.length );

 }

 public Object next()
 { 

if( position >= items.length )
   throw new NoSuchElementException();
  return items[ position++ ];
 }

 public void remove()
 { 

throw new UnsupportedOperationException(
        "ArrayIterator.remove()");
 }

 /** Not part of the Iterator interface, returns the data
  *  set in array form. A clone of the wrapped array
  *  is actually returned, so modifying the returned array
  *  will not affect the iteration at all.
  */
 public Object[] toArray()
 {

 return (Object[]) items.clone();
 }
} 

 

 

  •  Iterator 패턴을 원소를 특정 순서로 순회하도록 실체화할 수도 있다. 자료 구조의 내부 구현을 노출시키지 않으면서 순회할  수 있는 방법을 제공하는 것이 Iterator 패턴의 의도이면, 순회의 순서는 편의 따라 구현하면 된다.
  • Iterator가 내부 자료 구조를 수정할 수도 있다. 하지만 이러한 경우 Iterator가 내부 잘 구조를 망치치 않도록 주의해야 한다.

 

단순한 이진 코드를 Passive와 Active Iterator를 구현한 코드

 

import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;

import javax.print.DocFlavor.STRING;

public class Tree

{
 private Node root = null;

 

 private static class Node
 {
  public Node left, right;
  public Object item;
  public Node( Object item )
  { 
       this.item = item;
  }
 }

 

// A Passive (internal) iterator
public interface Examiner
{
public void examine( Object currentNode );
}

public void traverse( Examiner client )
{
traverseInOrder( root, client );
}

private void traverseInOrder(Node current, Examiner client)
{
if( current == null)
return;

traverseInOrder(current.left, client);
client.examine (current.item );
traverseInOrder(current.right, client);

}

  
 public void add( Object item )
 {
  if( root == null )
   root = new Node( item );
  else
   insertItem( root, item );
 }


//-------------------------------------------------------------
// An Active (external) iterator
//

 public Iterator iterator()
{
return new Iterator() //자바는 interface의 익명 impemetation이 가능한다. C#는 불가
{
private Node current = root;
private LinkedList stack = new LinkedList();

public Object next()
{
while( current != null )
{
stack.addFirst( current );
current = current.left;
}

if(stack.size() != 0 )
{
current = (Node)( stack.removeFirst() );
Object toReturn=current.item;
current = current.right;
return toReturn;
}

throw new NoSuchElementException();
}

public boolean hasNext()
{
return !(current==null && stack.size()==0);
}

public void remove()
{
throw new UnsupportedOperationException();
}
};
}


 


 private void insertItem( Node current, Object item )
 {
  if(current.item.toString().compareTo(item.toString())>0)
  {
   if( current.left == null )
    current.left = new Node(item);
   else
    insertItem( current.left, item );
  }
  else
  {
   if( current.right == null )
    current.right = new Node(item);
   else
    insertItem( current.right, item );
  }
 }
 
 
 public static void main( String[] args )
 {
    Tree t = new Tree();
    t.add("D");
    t.add("B");
    t.add("F");
    t.add("A");
    t.add("C");
    t.add("E");
    t.add("G");
  
    Iterator i = t.iterator();
   

 //Active Iterator를 사용
    while( i.hasNext() )
     System.out.print( i.next().toString() );
  
    System.out.println("");
  

  //Passive Itrerator를 사용
    t.traverse( new Examiner()
       { 
        public void examine(Object o)
        {
         System.out.print( o.toString() );
        }
       }
    );
    System.out.println("");
 }
}

 

트리를 방문하는 Active(External Iterator)를 구현한다고 생각해보자. Tree 클래스의 iterator() 메소드 는 트리의 각 노드를 중위(inorder) 순위로 방문하는 java.utiil.iterator를 반환한다. 이 코드는 매우 불분명하다. 알고리즘은 단순하지만 이해하기 어렵고 작성하기도 어렵다. (트리를 순회할 때 부모 노드를 기억하기 위해 스택을 사용)

 

하지만 Passive Iterator는 단순한 재귀 함수이며 작성하기 쉽다. TraverseInOrder 메소드는 Passive Iterator의 구현을 보여준다. 이 재귀 순회 알고리즘은 각 노드를 Examiner 객체에 차례로 전달할 뿐이다. 트리의 모든 노드를 Examiner 익명 인터페이스를 구현해서 다음과 같이 출력할 수 있다.

 

 

정리

  • External Iterator 혹은 Active Iterator는 자신이 순회하는 자료 구조와 분리되어 있다.
  • Passive Iterator는 혹은 Internal Iterator 객체를 생성하는 대신 자료 구조 내부에 순회 메커니즘을 구현한다.
  • Passive Iterator는 혹은 Internal Iterator 자료 구조가 본질적으로 순회하기 어려운 구조일 때 유용하다.

 

저작자표시 비영리 (새창열림)
블로그 이미지

파란실버라이트

To remember the time when I started learning Silver Light!

,
  • «
  • 1
  • 2
  • 3
  • »

카테고리

  • Inforamtion Technology (281)
    • DESIGN PATTERN (33)
      • 실용주의 디자인패턴 (29)
    • SOFTWARE ENGINEERING (21)
      • Art Of Readable Code (12)
      • Object Oriented Programming (6)
      • TDD (2)
    • FRAMEWORK (22)
      • Spring.net (2)
      • LightSwitch (20)
    • PROGRAMING (58)
      • C# (20)
      • .NET (6)
      • HTML5 (7)
      • ASP.NET (9)
      • SILVERLIGHT (7)
      • Ruby On Rails (6)
    • PROJECT MANAGEMENT (10)
      • SW Version Management (7)
      • Schedulring Management (1)
    • BOOKS (18)
    • MOBILE APP (1)
      • SENCHA TOUCH (1)
    • SECURITY (5)
    • MES (1)
    • B2B (14)
      • WEBMETHODS (4)
    • ERP (53)
      • SAP/R/3 (51)
    • ABOUT TOOLS (2)
    • FUNDAMENT CONCEPT (21)
    • SOA BPM (22)
    • PORTFOLIO (0)

태그목록

  • 프로그래밍
  • 동시성
  • 병렬

최근에 받은 트랙백

글 보관함

링크

파란실버라이트

블로그 이미지

To remember the time when I started learning Silver Light!

LATEST FROM OUR BLOG

RSS 구독하기

LATEST COMMENTS

BLOG VISITORS

  • Total :
  • Today :
  • Yesterday :

Copyright © 2015 Socialdev. All Rights Reserved.

티스토리툴바