2011년 5월 23일 월요일

The Strategy Pattern

21. The Strategy Pattern

Strategy 패턴은 State 패턴과는 대략적인 외형에서 매우 유사하지만, 목적은 약간 다르다. Strategy 패턴은 Context라는 드라이버 클래스 안에 캡슐화된 다수의 관련 알고리즘으로 구성되어 있다. 클라이언트 프로그램은 이런 알고리즘들 중에서 한 가지를 선택할 수 있거나 특별한 경우 Context 클래스가 적합한 알고리즘을 선정해 줄 수도 있다. 패턴의 목적은 이런 알고리즘의 성격을 변경할 수 있도록 설정하여 가장 적합한 성격을 선정할 수 있는 방법을 제공하는 것이다. State 패턴과 Strategy 패턴은 사용자가 일반적으로 어떤 전략을 적용해야 할지 선정한다는 점에서 그리고 한 번에 단 하나의 전략만 Context 클래스 안에서 초기화 및 활성화되는 경향이 있다는 점에서 차이가 있다. 이와는 대조적으로 모든 종류의 서로 다른 State 패턴은 한 번에 활성화되고, 해당 요소 간의 전화 작업이 자주 발생할 수 있다. 또한 Strategy 패턴은 많든지 적든지 동일한 작업을 수행하는 몇 가지의 알고리즘을 갴슐화한다. 그리고 State패턴은 서로 다른 작업을 수행하는 관련된 알고리즘들을 캡슐화한다는 점에서도 차이가 있다. 마지막으로 서로 다른 State 정보 간의 전이라는 개념은 Strategy 패턴에는 없는 개념이다.

구조


역할
· Strategy : 모든 제공된 알고리즘에 대한 일반적인 인터페이스를 선언한다. Context는 ConcreteStrategy에 의해 구현된 알고리즘들을 호출하기 위해 이 인터페이스를 이용한다.
· ConcreteStrategy : Strategy 인터페이스를 이용하여 알고리즘을 구현한다.
· Context : ConcreteStrategy 객체로 설정되어진다. Strategy 객체의 참조를 가진다. Strategy 가 context의 데이터를 접근할 수 있도록 인터페이스를 정의할 수 있다.

의도
비슷한 문제들을 해결할 수 있는 알고리즘의 군들을 정의하고, 각각의 알고리즘을 캡슐화하고, 그 알고리즘들을 교환할 수 있도록 한다. Strategy는 알고리즘들로 하여금 해당 알고리즘을 이용하는 클라이언트로부터 독립적일수 있도록 해준다..

적용시기
  • Strategy 패턴은 많은 행위중에 한가지로 상황에 따라 클래스을 설정해주는 방법을 제공해준다.
  • Strategy 패턴는 다양한 알고리즘의 계층 클래스를 구현할때 이용될 수 있다.
  • Strategy 패턴을 이용함으로써 복잡함이 노출되는 것과 알고리즘 구체적인 데이터 구조로 가는 것을 피할 수 있다.
  • 클래스가 많은 행위들을 정의한다. 이는 다중조건문들에 의해서 구현되곤 한다. 이러한 많은 조건문들 대신, 각각 관련된 조건들을 Strategy 클래스들에게로 이동시킬 수 있다.


결론
· Strategy 패턴은 개발자가 여러 개의 알고리즘에서 한 가지를 동적으로 선택할 수 있도록 한다. Context 클래스가 사용자의 요청에 따라 전략의 설정을 바꾸기 때문에 개발자가 단순히 원하는 파생 클래스만 호출한다면, 유연성을 얻게 된다.
· Strategy 패턴은 모든 것을 숨기는 것이 아니다. 클라이언트 코드는 보통 특정 전략에 대한 대체 방안이 존재한다는 것을 알고 있으며, 일반적으로 그것에 따라 대체 방안을 선택하는 기준도 설정해 놓는다. 그러므로 알고리즘이 담당하는 결정 문제가 클라이언트 개발자 및 사용자에게로 전가되는 경향이 있다.

예제소스


예제 소스
public interface awtList {
public void add(String s);
public void remove(String s);
public String[] getSelectedItems();
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
public class BarPlotPanel extends PlotPanel {
Color[] colors;
public BarPlotPanel() {
colors = new Color[6];
colors[0] = Color.red;
colors[1] = Color.blue;
colors[2] = Color.green;
colors[3] = new Color(0xff8000);
colors[4] = Color.lightGray;
colors[5] = Color.yellow;
}
public void paint(Graphics g) {
int xp, yp ;
int ypm = (int)(ypmax * 1.05f);
g.setColor(Color.white);
g.fillRect(0,0,getWidth(), getHeight());
g.setColor(Color.black);
g.drawRect(xpmin, ypmin, xpmax, ypm - ypmin);
g.setColor(colors[0]);
for(int i=0; i< x.length; i++) {
g.setColor(colors[i]);
xp = calcx(x[i]);
yp = calcy(y[i]);
g.fillRect(xp, yp, calcx(1.0f), ypm-yp);
}
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
public class BarPlotStrategy extends PlotStrategy {
BarPlotPanel bp;
public BarPlotStrategy() {
super("Bar 차트");
bp = new BarPlotPanel();
getContentPane().add(bp);
}
public void plot(float xp[], float yp[]) {
x = xp; y = yp;
findBounds();
setSize(width, height);
setVisible(true);
bp.setBackground(Color.white);
bp.setBounds(minX, minY, maxX, maxY);
bp.plot(x, y, Color.black);
}
}
public interface Command {
public void Execute();
}
import java.awt.*;
import java.util.*;
public class Context {
private PlotStrategy plotStrategy;
private float x[], y[];
String[] Data = null;
public Context(String[] newData) {
Data = newData;
setLinePlot();
}
public void setBarPlot() {
plotStrategy = new BarPlotStrategy();
}
public void setLinePlot() {
plotStrategy = new LinePlotStrategy();
}
public void plot() {
plotStrategy.plot(x, y);
}
public void setPenColor(Color c) {
plotStrategy.setPenColor(c);
}
public void readData() {
StringTokenizer tok;
Vector xv = new Vector();
Vector yv = new Vector();
String s ="";
for(int i=0; i < Data.length; i++) {
s =Data[i];
if (s != null) {
tok = new StringTokenizer(s);
xv.addElement(tok.nextToken());
yv.addElement(tok.nextToken());
}
}
x = new float[xv.size()];
y = new float[yv.size()];
for (int i=0; i< xv.size(); i++) {
x[i] = new Float((String)xv.elementAt(i)).floatValue();
y[i] = new Float((String)yv.elementAt(i)).floatValue();
}
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import javax.swing.border.*;
public class JawtList extends JScrollPane implements ListSelectionListener, awtList {
private JList listWindow;
private JListData listContents;
public JawtList(int rows) {
listContents = new JListData();
listWindow = new JList(listContents);
listWindow.setPrototypeCellValue("Abcdefg Hijkmnop");
getViewport().add(listWindow);
}
public void addListSelectionListener(ListSelectionListener iList) {
listWindow.addListSelectionListener(iList);
}
public void add(String s) {
listContents.addElement(s);
}
public void remove(String s) {
listContents.removeElement(s);
}
public void clear() {
listContents.clear();
}
public String[] getSelectedItems() {
Object[] obj = listWindow.getSelectedValues();
String[] s = new String[obj.length];
for (int i =0; i<obj.length; i++)
s[i] = obj[i].toString();
return s;
}
public void clearSelection() {
listWindow.clearSelection();
}
public Object getSelectedValue() {
return listWindow.getSelectedValue();
}
public void valueChanged(ListSelectionEvent e) {}
}
class JListData extends AbstractListModel {
private Vector data;
public JListData() {
data = new Vector();
}
public int getSize(){
return data.size();
}
public Object getElementAt(int index) {
return data.elementAt(index);
}
public void addElement(String s) {
data.addElement(s);
fireIntervalAdded(this, data.size()-1, data.size());
}
public void removeElement(String s) {
data.removeElement(s);
fireIntervalRemoved(this, 0, data.size());
}
public void clear() {
int size= data.size();
data = new Vector();
fireIntervalRemoved(this, 0, size);
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
public class JBarButton extends JButton implements Command {
Context context;
public JBarButton(ActionListener act, Context ctx) {
super("Bar 차트");
addActionListener(act);
context = ctx;
}
public void Execute() {
context.readData();
context.setBarPlot();
context.plot();
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
public class JGraphButton extends JButton implements Command {
Context context;
public JGraphButton(ActionListener act, Context ctx) {
super("Line 차트");
addActionListener(act);
context = ctx;
}
public void Execute() {
context.setPenColor(Color.red);
context.setLinePlot();
context.readData();
context.plot();
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
public class JxFrame extends JFrame {
public JxFrame(String title) {
super(title);
setCloseClick();
setLF();
}
private void setCloseClick() {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
});
}
private void setLF() {
String laf = UIManager.getSystemLookAndFeelClassName();
try {
UIManager.setLookAndFeel(laf);
} catch (UnsupportedLookAndFeelException exc) {
System.err.println("Warning: UnsupportedLookAndFeel: " + laf);
} catch (Exception exc) {
System.err.println("Error loading " + laf + ": " + exc);
}
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
public class LinePlotPanel extends PlotPanel {
public void paint(Graphics g) {
super.paint(g);
int xp = calcx(x[0]);
int yp = calcy(y[0]);
g.setColor(Color.white);
g.fillRect(0,0,getWidth(), getHeight());
g.setColor(Color.black);
g.drawRect(xpmin, ypmin, xpmax, ypmax);
g.setColor(color);
for (int i=1; i< x.length; i++) {
int xp1 = calcx(x[i]);
int yp1 = calcy(y[i]);
g.drawLine(xp, yp, xp1, yp1);
xp = xp1;
yp = yp1;
}
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
public class LinePlotStrategy extends PlotStrategy {
private LinePlotPanel lp;
public LinePlotStrategy() {
super("Line 차트");
lp = new LinePlotPanel();
getContentPane().add(lp);
}
public void plot(float[] xp, float[] yp) {
x = xp; y = yp;
findBounds();
setSize(width, height);
setVisible(true);
setBackground(Color.white);
lp.setBounds(minX, minY, maxX, maxY);
lp.plot(xp, yp, color);
repaint();
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
public class PlotPanel extends JPanel {
private float xfactor, yfactor;
protected int xpmin, ypmin, xpmax, ypmax;
private float minX, maxX, minY, maxY;
protected float x[], y[];
protected Color color;
public void setBounds(float minx, float miny, float maxx, float maxy) {
minX = minx;
maxX = maxx;
minY = miny;
maxY = maxy;
}
public void plot(float[] xp, float[] yp, Color c) {
x = xp;
y = yp;
color = c;
int w = getWidth() - getInsets().left - getInsets().right;
int h = getHeight() - getInsets().top - getInsets().bottom;
xfactor = (0.9f * w) / (maxX - minX);
yfactor = (0.9f * h)/ (maxY - minY);
xpmin = (int)(0.05f * w);
ypmin = (int)(0.05f * h);
xpmax = w - xpmin;
ypmax = h - ypmin;
repaint();
}
protected int calcx(float xp) {
return(int)((xp-minX) * xfactor + xpmin);
}
protected int calcy(float yp) {
int ypnt = (int)((yp-minY) * yfactor);
return ypmax - ypnt;
}
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
public abstract class PlotStrategy extends JFrame {
protected float[] x, y;
protected float minX, minY, maxX, maxY;
protected int width, height;
protected Color color;
public PlotStrategy(String title) {
super(title);
width = 300;
height =200;
color = Color.black;
addWindowListener(new WindAp(this));
}
public abstract void plot(float xp[], float yp[]);
public void setSize(Dimension sz) {
width = sz.width;
height = sz.height;
}
public void setPenColor(Color c) {
color = c;
}
public void findBounds() {
minX = minY = Float.MAX_VALUE;
maxX = maxY = Float.MIN_VALUE;
for (int i=0; i < x.length; i++) {
if (x[i] > maxX)
maxX = x[i];
if (x[i] < minX)
minX = x[i];
if (y[i] > maxY)
maxY = y[i];
if (y[i] < minY)
minY = y[i];
}
}
}
import java.awt.event.*;
import javax.swing.*;
public class WindAp extends WindowAdapter {
JFrame fr;
public WindAp(JFrame f) {
fr = f;
}
public void windowClosing(WindowEvent e) {
fr.setVisible(false);
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
public class StrategyPlot extends JxFrame implements ActionListener {
Context context;
public StrategyPlot() {
super("Strategy 패턴 Demo");
String[] Data = {"1 15","2 20","3 30","4 25","5 40", "6 5"};
JPanel jp = new JPanel();
getContentPane().add(jp);
context = new Context(Data);
jp.add(new JBarButton(this, context));
jp.add(new JGraphButton(this, context));
setSize(new Dimension(300, 200));
setVisible(true);
}
public void actionPerformed(ActionEvent e) {
Command comd =(Command)e.getSource();
comd.Execute();
}
static public void main(String[] argv) {
new StrategyPlot();
}
}


관련패턴
  • Flyweight 패턴 : ConcreteStategy 역할은 Flyweight 패턴을 사용해서 여러 곳에서 공유하게 할 수 있다.
  • Abstract Factory 패턴 : Strategy 패턴에서는 알고리즘을 Abstract Factory 패턴을 사용하여 모두 교체할 수 있다.
  • State 패턴 : Strategy 패턴과 State 패턴은 둘다 위임하는 곳을 교체하는 패턴이고 클래스간의 관계도 매우 비슷하다. 그러나 목적이 다르다. Strategy 패턴은 “알고리즘”을 표현하고, State 패턴은 “상태”을 표현한다.

댓글 없음:

댓글 쓰기

ETL 솔루션 환경

ETL 솔루션 환경 하둡은 대용량 데이터를 값싸고 빠르게 분석할 수 있는 길을 만들어줬다. 통계분석 엔진인 “R”역시 하둡 못지 않게 관심을 받고 있다. 빅데이터 역시 데이터라는 점을 볼때 분산처리와 분석 그 이전에 데이터 품질 등 데이...