Command 패턴은 빌딩 블록 패턴이며, Staratage 패턴은 Command 패턴의 특별한 경우이다.
Command pattern : '무엇을 어떻게" 해야 하는지에 대한 지식을 객체에 캡슐화하여 전달한다.
=> 무엇을 어떻게는 Command interface에 정의 되어 있지 않을까요.? ^^
Ex) 자바 쓰레드 : commnad 패턴의 구현체
class CommandObject implements Runnable
{
public void run()
{
// stuff to do on the thread goes here
}
};
Thread controller = new Thread( new CommandObject()); //쓰레드가 어떤 Command Object 사용할지 전달
controller.start(); // fire up the thread : Run 함수를 실행
Command pattern에서는 Command 객체를 사용하는 'Client' 클래스가 Command 객체가 무엇을 어떻게 할지(run())에 대해 아무 것도 알지 못한다. 무엇을 어떻게 해야 하는지에 대한 정보가 Commnand 객체에 캡슐화 되어 있기 때문이다.
Stratagy 패턴은 특정 연산을 '어떻게' 수행할 것인지에 대한 전략을 캡슐화한 Strategy 객체를 전달한다.
이때 Starategy 객체는 무엇을 수행해야 하는가가 좀 더 명확한 Command 객체라 할 수 있다.
Ex1) Arrays을 이용하여 배열을 정렬하는 문제를 생각해보자.
public void main( String[] args )
{
// sort the command-line arguments:
Arrays.sort
( args,
new Comparator()
{
public int compare( Object o1, Object o2 )
{
// By using a minus sign to reverse the sign
// of the result, "larger" items will be
// treated as if they're "smaller."
return -( o1.compareTo(o2) );
}
}
);
}
Sort 메소드에 두 개의 배열 원소를 비교하는 전략을 정의한 객체를 넘겨주면 , sort 메소드는 Strategy 객체를 통해 객체를 비교한다.
즉 객체 비교전략을 Stratagy 객체에 위임하는 것이다.
아래는 비교를 위한 Strategy 객체를 이용하는 Shell 정렬 알고리즘
: 내부 클래스를 사용해서 구현 하였지만 , 클래스를 사용하였다면 compare() 를 구현하는 Comparator interface를 상속
import java.io.IOException;
public class URL
{
private String spec;
public URL( String spec )
{
this.spec = spec;
}
public URLConnection openConnection() throws IOException
{
// Assemble the class name by prefixing a package
// and appending URLConnection to the protocol.
// The first character of the protocol is mapped to
// uppercase.
}
public static void shellSort(Object[] base, Comparator compareStrategy)
{
int i, j;
int gap;
Object p1, p2;
for( gap=1; gap <= base.length; gap = 3*gap + 1 )
;
for( gap /= 3; gap > 0 ; gap /= 3 )
for( i = gap; i < base.length; i++ )
for( j = i-gap; j >= 0 ; j -= gap )
{
if( compareStrategy.compare( base[j],base[j+gap] ) <= 0 )
break;
Object t = base[j];
base[j] = base[j+gap];
base[j+gap] = t;
}
}
//...
private static class Test
{
public static void main( String[] args )
{
String array[] = { "b", "d", "e", "a", "c" };
Sorters.shellSort(array, new Comparator()
{
public int compare( Object o1, Object o2 )
{
// sort in reverse order
return - ((String)o1).compareTo((String)o2) );
}
}
);
for( int i = 0; i < array.length; ++i )
System.out.println( array[i] );
}
}
}
Comparator 인터페이스 구현체를 정렬(무엇)을 어떻게 해야 하는지에 대한 전략을 캡술화한 Strategy 객체이다.
Startagy 객체의 구현에 따라 다양한 전략으로 정렬을 할 수 있다.
Ex2) Java.awt.Container의 LayoutManager와 이의 구현체
Frame container = new Frame();
container.setLayout( new FlowLayout() );
container.add( new Button("1") );
container.add( new Button("2") );
container.add( new Button("3") );
container.add( new Button("3") );
You can lay the buttons out in a two-by-two grid like this:
Frame container = new Frame();
container.setLayout( new GridLayout(2,2) );
container.add( new Button("1") );
container.add( new Button("2") );
container.add( new Button("3") );
container.add( new Button("3") );
막약 Strategy 대신 구현 상속을 사용했다면
1. 버튼을 나란히 배열하기 위해 Frame을 확장한 FlowFrame을,
2. 버튼을 격자로 배열하기 위해 Frame을 확앙한 GridFrame을 만들어야 했을 것이다.
LayoutManager Stratagy 객체(LayoutManager 인터페이스를 구현하지만 어느 것도 확장하지 않는다)는
구현 상속을 제거하고 구현을 간단하게 해준다.
대부분의 상황에서 Strategy 패턴은 Factory Method 패턴의 좋은 대안이 된다.
가능한 구현 상속을 이용하는 Factory Method 패턴 대신 인터페이스 상속을 하는 Strategy 패턴을 사용하기를 바란다