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

'DESIGN PATTERN'에 해당되는 글 33건

  • 2017.09.21 Model-View-View Model과 이식 가능한 클래스 라이브러리
  • 2017.09.19 Winform , MVC, MVP, MVVM
  • 2013.03.29 Factory Method Pattern VS Strategy Pattern
  • 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 패턴

Model-View-View Model과 이식 가능한 클래스 라이브러리

DESIGN PATTERN 2017. 9. 21. 15:00

지금 개발 중인 프로그램과 관련해서 도움이 될만한 글이 있어 실습해보았다.

- UI로 부터 독립적인 클래스라이브러리(Viewmodel) 개발 

- UI없이 독립적으로 UnitTest가 가능 


Sourecode 

https://github.com/paranpiano/CrossPlattformLib.git




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

파란실버라이트

To remember the time when I started learning Silver Light!

,

Winform , MVC, MVP, MVVM

DESIGN PATTERN 2017. 9. 19. 14:06

MVVM Pattern으로 개발을 다시 하기 전에 개념을 정리하고 싶었다. :)




Winform , Behind code 

- View로 요청을 받음

- View의 control을 Behind code에서 바로 binding    

- Model을 사용해서 business logic을 처리하더라도 View와 강하게 결합 

                      

Model - View(n) - Controller(1) 

- Contoller로 요청을 받음 

- Contoleer는 Model에게 Bisiness logic 처리 요청

- view는 모델을 참조하여 업데이트 함, 

- View와 model과 의존성이 없앨수 없다.

- Model과 view의 의존성을 낮게하는 패턴을 사용하면 좋은 MVC가 된다


Model - view(1) - Presenter(1)

- View로 요청을 받음

- Presenter는 Model에게 Bisiness logic 처리 요청

- 그 결과는 View에게 알려준다. 

- Model와 View과 완벽하게 분리

- View와 Presenter의 의존성이 강하다.(1:1결합)


Model - view(N) - Viewmodel(1)

- View로 요청을 받음

- Command : View의 Behavior를 Command로 View model에게 요청

- View Model은 Model에게 Bisness log을 요청 

- Data binding : View Model의 속성 값이 변경되면 binding된 View control에게 notify가 가면서 자동 업데이트

- Model은 View로 부터 완벽히 분리

- MVP와 달리 Viewmodel은 여러 View에서 재사용이 가능하다.

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

파란실버라이트

To remember the time when I started learning Silver Light!

,

Factory Method Pattern VS Strategy Pattern

DESIGN PATTERN 2013. 3. 29. 15:26
 Factory Method Pattern (Extend의 잘못 사용)이 어떤 문제를 일으키고

Strategy Pattern을 어떻게 대안이 될 수 있는가? ^^

 

- Factory Method pattern Diagram

 

 

    class FLEX :Equipment
    {
        public override void Start()
        {
            //VK93과 같은 일을 한다(중복코드 발생)
            //do someting V93K about start
        }

        public override void Load()
        {
            //CATALIST와 같은 일을 한다(중복코드 발생)
            //do someting CATALIST about load
        }

        public override void Modify()
        {
            //do someting FLEX about modify
        }
    }

 

  문제점 : VK93의 Start() or Catalist 의 Load() 함수가 변경된다면 같이 수정이 필요

 

 

    class D10 : Equipment
    {

        public override void Start()
        {
            //do someting D10 about load
        }

        public override void Load()
        {
            //do someting D10 about load
        }

        //LSP(서브타입은 언제나 자신이 기반타입으로 교체할 수 있어야 한다) 위반
        public override void Modify()
        {
            throw new NotImplementedException();
        }
    }

 

 

    public partial class Form1 : Form
    {
        EquipmentCommander _ec = new EquipmentCommander();

        public Form1()
        {
            InitializeComponent();

            Equipment vk93 = EquipmentFactory.CreateEquipment("V93K");
            Equipment Catalist = EquipmentFactory.CreateEquipment("Catalist");
            Equipment D10 = EquipmentFactory.CreateEquipment("D10");
            Equipment Flex = EquipmentFactory.CreateEquipment("FLEX");

 

            _ec.Excute(vk93);
            _ec.Excute(Catalist);
            _ec.Excute(Flex);

 

            //서브클래스 인스턴스를 파라미터로 전달 했을 때 메소드가 이상하게 작동 
            //Modify function이 제대로 구현되지 않음

             _ec.Excute(D10);
        }
    }

 

 문제점 : LSP를 위반한다. 즉 기반 클래스로 서브클래스의 사용을 하지 못한다.

 

 

 

 

 

- Strategy Pattern Diagram

  

 

 

    public class Equipment
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Location { get; set; }
        public string PropertyforManager { get; set; }

 

        //각각 Strategy 를 가지고 있다.

        public IStart start { get; set; }
        public IModify modify { get; set; }
        public ILoad load { get; set; }
    }

 

 Factory 에서 생성 시에  맞는 전략을 구현한다.

 

    class EquipmentFactory
    {
        public static Equipment CreateEquipment(string EqpName)
        {

            Equipment eqp = new Equipment();
            eqp.Name = EqpName;

            if (EqpName.Equals("V93K"))
            {
                eqp.start = new StartV93K();
                eqp.load = new LoadV93K();
                eqp.modify = new ModifyV93K();
            }
            else if (EqpName.Equals("Catalist"))
            {
                eqp.start = new StartCatalist();
                eqp.load = new LoadCatalist();
                eqp.modify = new ModifyCatalist();
            }
            else if (EqpName.Equals("D10"))
            {
                eqp.start = new StartD10();
                eqp.load = new LoadD10 ();


                //modify 가 필요없는 Equipment는 ModifyNothing을 구현한다.
                eqp.modify = new ModifyNothing();
            }
            else if (EqpName.Equals("FLEX"))
            {

                // Flex는 V93의 Start와 Catalist의 Load를 구현함으로 중복 코드의 없음
                eqp.start = new StartV93K();
                eqp.load = new LoadCatalist();
                eqp.modify = new ModifyFlex();
            }
            else
            {
                throw new Exception("unregisted Eqp Class");
            }

            return eqp;
        }
    }

 

 문제점 해결 : 전략 객체를 이용해서 장비가 행위를 한다.

                      중복코드를 만들지 않는다.(수정이 필요하면 전략객체만을 수정하면 된다.)

                      새로운 행위가 필요하면 기반 클래스의 수정없이 전략 클래스를 확장(OCP를 지킴)

 

 

    public partial class Form1 : Form
    {
        EquipmentCommander _ec = new EquipmentCommander();

        public Form1()
        {
            InitializeComponent();

            Equipment vk93 = EquipmentFactory.CreateEquipment("V93K");
            Equipment Catalist = EquipmentFactory.CreateEquipment("Catalist");
            Equipment D10 = EquipmentFactory.CreateEquipment("D10");
            Equipment Flex = EquipmentFactory.CreateEquipment("FLEX");

            _ec.Excute(vk93);
            _ec.Excute(Catalist);
            _ec.Excute(Flex);

 

             //D10은 modify를 하지 않지만 ModifyNothing()을 구현해서 문제를 해결한다.

=> Equipment Generator Code 

 

            else if (EqpName.Equals("D10"))
            {
                eqp.start = new StartD10();
                eqp.load = new LoadD10 ();


                eqp.modify = new ModifyNothing();
            }

 

_ec.Excute(D10);

 

 

            //Flex장비의 행동을vk93과 같게 변경하고 싶다면??
            //클래스를 생성해서 위임해야 겠지만 예제 이므로 여기서 테스트 하였음
            Flex.start = new StartV93K();
            Flex.load = new LoadV93K();
            Flex.modify = new ModifyV93K();

            _ec.Excute(Flex);

 

 

  }

}

 

문제점 해결 : EquipmentCommander 의 Execute가 기반 타입으로 작동하는데 문제가 없다

                     즉 LSP를 위반하지 않는다 ^^

 

 

FactoryMethodPattern.zip

 

 

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

파란실버라이트

To remember the time when I started learning Silver Light!

,

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!

,
  • «
  • 1
  • 2
  • 3
  • 4
  • »

카테고리

  • 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.

티스토리툴바