다양한수준의설계패턴존재 객체지향프로그래밍에서, 클래스설계시활용할수있음. 일반개발자는새로운설계패턴을작성하기보다는이미정리되어있는설계패턴을활용하는것이바람직함 GoF (Gang of Four) 에서 23 개의설계패턴을 3 가지유형으로분류
Creational Pattern 객체를생성하는데관련된패턴들 객체가생성되는과정에유연성을높이고, 코드의유지가쉬워진다. Structural Pattern 프로그램의구조에관련된패턴들 프로그램내의자료구조나인터페이스구조등프로그램의구조를설계하는데많이활용될수있는패턴들 Behavioral Pattern 반복적으로사용되는객체들의상호작용을패턴화해놓은것
Creational Structural Behavioral Abstract Factory Prototype Singleton Factory Method Builder Adapter Bridge Composite Decorator Flyweight Façade Proxy Chain of Responsibility Command Interpreter Iterator Mediator Memento Observer State Strategy Template Method Visitor
용도 관찰대상의상태가변화했을때관찰자에게통지 상태변화에따른처리를기술할때효과적으로활용
추상클래스
public interface Observer { public abstract void update(numbergenerator generator); import java.util.vector; import java.util.iterator; public abstract class NumberGenerator { private Vector observers = new Vector(); // Observer들을보관 public void addobserver(observer observer) { // Observer를추가 observers.add(observer); public void deleteobserver(observer observer) { // Observer를삭제 observers.remove(observer); public void notifyobservers() { // Observer에통지 Iterator it = observers.iterator(); while (it.hasnext()) { Observer o = (Observer)it.next(); o.update(this); public abstract int getnumber(); // 수를취득한다. public abstract void execute(); // 수를생성한다. 자신을관찰하는 observer 자신의내용이갱신되었으니, 이를반영해달라고, observer 모두에게전달하는 method
import java.util.random; public class RandomNumberGenerator extends NumberGenerator { private Random random = new Random(); // 난수발생기 private int number; // 현재의수 public int getnumber() { // 수를취득한다. return number; public void execute() { for (int i = 0; i < 20; i++) { number = random.nextint(50); notifyobservers(); public class DigitObserver implements Observer { public void update(numbergenerator generator) { System.out.println("DigitObserver:" + generator.getnumber()); try { Thread.sleep(100); catch (InterruptedException e) { 0~49 범위의난수를발생 NumberGenerator 의 getnumber 메소드를이용하여변경된내용을출력
public class GraphObserver implements Observer { public void update(numbergenerator generator) { System.out.print("GraphObserver:"); int count = generator.getnumber(); for (int i = 0; i < count; i++) { System.out.print("*"); System.out.println(""); try { Thread.sleep(100); catch (InterruptedException e) { NumberGenerator 의 getnumber 메소드를이용하여변경된내용을출력 public class Main { public static void main(string[] args) { NumberGenerator generator = new RandomNumberGenerator(); Observer observer1 = new DigitObserver(); Observer observer2 = new GraphObserver(); generator.addobserver(observer1); generator.addobserver(observer2); generator.execute();
용도 실행하고싶은메소드의 history 관리 매크로명령을정의하고자할때 동일한명령을반복해서실행
package command; public interface Command { public abstract void execute(); 다수의 Command 를모아두기위한것 Command 필드에보관되어있는각인스터의 execute 메소드를호출 package command; import java.util.stack; import java.util.iterator; public class MacroCommand implements Command { // 명령의집합 private Stack commands = new Stack(); // 실행 public void execute() { Iterator it = commands.iterator(); while (it.hasnext()) { ((Command)it.next()).execute(); // 추가 public void append(command cmd) { if (cmd!= this) { commands.push(cmd); // 최후의명령을삭제 public void undo() { if (!commands.empty()) { commands.pop(); // 젂부삭제 public void clear() { commands.clear();
package drawer; public interface Drawable { public abstract void draw(int x, int y); package drawer; import command.command; import java.awt.point; public class DrawCommand implements Command { // 그림그리는대상 protected Drawable drawable; // 그림그리기를실행할대상 private Point position; // 생성자 public DrawCommand(Drawable drawable, Point position) { this.drawable = drawable; this.position = position; // 실행이위치에점을그려라 public void execute() { drawable.draw(position.x, position.y); package drawer; import command.*; import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class DrawCanvas extends Canvas implements Drawable { private Color color = Color.red; // 그림그리는색 private int radius = 6; // 그림그리기를할점의변경 private MacroCommand history; // 이력 // 생성자 public DrawCanvas (int width, int height, MacroCommand history) { setsize(width, height); setbackground(color.white); this.history = history; // 이력젂체를다시그리기 public void paint(graphics g) { history.execute(); // 그리기 public void draw (int x, int y) { Graphics g = getgraphics(); g.setcolor(color); g.filloval(x - radius, y - radius, radius * 2, radius * 2);
import command.*; import drawer.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Main extends JFrame implements ActionListener, MouseMotionListener, WindowListener { // 그림그리기이력 private MacroCommand history = new MacroCommand(); // 그림그리기영역 private DrawCanvas canvas = new DrawCanvas(400, 400, history); // 제거버튼 private JButton clearbutton = new JButton("clear"); // 생성자 public Main(String title) { super(title); this.addwindowlistener(this); canvas.addmousemotionlistener(this); // 마우스클리리스터의설정 clearbutton.addactionlistener(this); // Box buttonbox = new Box(BoxLayout.X_AXIS); buttonbox.add(clearbutton); Box mainbox = new Box(BoxLayout.Y_AXIS); mainbox.add(buttonbox); mainbox.add(canvas); getcontentpane().add(mainbox); // ActionListener 용 public void actionperformed(actionevent e) { if (e.getsource() == clearbutton) { history.clear(); canvas.repaint(); // MouseMotionListener 용 public void mousemoved(mouseevent e) { public void mousedragged(mouseevent e) { Command cmd = new DrawCommand(canvas, e.getpoint()); history.append(cmd); cmd.execute(); // WindowListener 용 public void windowclosing(windowevent e) { System.exit(0); public void windowactivated(windowevent e) { public void windowclosed(windowevent e) { public void windowdeactivated(windowevent e) { public void windowdeiconified(windowevent e) { public void windowiconified(windowevent e) { public void windowopened(windowevent e) { public static void main(string[] args) { new Main("Command Pattern Sample"); pack(); setvisible(true);
용도 어떤요구가발행하였을때, 그요구를처리할 object 를바로결정할수없는경우에는, 다수의 object 를 chain 으로연결하여, 그 object chain 을차례로방문하면서, 목적에맞는 object 를결정 요구하는측 과 처리하는측 의연결을약화 Coupling 을낮추는역할
다음책임자 (object) 를보관해두었다가스스로처리할수없는요구가발생하면다음책임자에게넘김
public class Trouble { private int number; public Trouble(int number) { this.number = number; // 트러블번호 // 트러블의생성 public int getnumber() { // 트러블번호를얻는다. return number; public String tostring() { return "[Trouble " + number + "]"; // 트러블의문자열표현 False 이면 다음 object 에 ( 책임을 ) 넘김 final method => overriding 불가 public abstract class Support { private String name; private Support next; public Support(String name) { this.name = name; // 트러블해결자의이름 // 떠넘기는곳 // 트러블해결자의생성 public Support setnext(support next) { // 떠넘길곳을설정 this.next = next; return next; public final void support(trouble trouble) { // 트러블해결순서 if (resolve(trouble)) { done(trouble); else if (next!= null) { next.support(trouble); else { fail(trouble); public String tostring() { return "[" + name + "]"; // 문자열표현 protected abstract boolean resolve(trouble trouble); // 해결용메소드 protected void done(trouble trouble) { // 해결 System.out.println(trouble + " is resolved by " + this + "."); protected void fail(trouble trouble) { // 미해결 System.out.println(trouble + " cannot be resolved.");
public class NoSupport extends Support { public NoSupport(String name) { super(name); public class OddSupport extends Support { public OddSupport(String name) { super(name); // 생성자 protected boolean resolve(trouble trouble) { // 해결용메소드 return false; // 자신은아무처리도하지않는다. public class LimitSupport extends Support { private int limit; // 이번호미만이면해결할수있다. public LimitSupport(String name, int limit) { // 생성자 super(name); this.limit = limit; protected boolean resolve(trouble trouble) { // 해결용메소드 if (trouble.getnumber() < limit) { return true; else { return false; protected boolean resolve(trouble trouble) { if (trouble.getnumber() % 2 == 1) { return true; else { return false; public class SpecialSupport extends Support { // 해결용메소드 private int number; // 이번호만해결할수있다. public SpecialSupport(String name, int number) { // 생성자 super(name); this.number = number; protected boolean resolve(trouble trouble) { if (trouble.getnumber() == number) { return true; else { return false; // 해결용메소드
public class Main { public static void main(string[] args) { Support alice = new NoSupport("Alice"); Support bob = new LimitSupport("Bob", 100); Support charlie = new SpecialSupport("Charlie", 429); Support diana = new LimitSupport("Diana", 200); Support elmo = new OddSupport("Elmo"); Support fred = new LimitSupport("Fred", 300); // chain 의형성 alice.setnext(bob).setnext(charlie).setnext(diana).setnext(elmo).setnext(fred); // 다양한트러블발생 for (int i = 0; i < 500; i += 33) { alice.support(new Trouble(i));
용도 mediator 역할을수행 모듞행동을수행하기이젂에 조정자 의결정이있어야하며, 조정자 의판단은각멤버에게내려짐
public interface Mediator { public abstract void createcolleagues(); public abstract void colleaguechanged(colleague colleague); public interface Colleague { public abstract void setmediator(mediator mediator); public abstract void setcolleagueenabled(boolean enabled); import java.awt.button; public class ColleagueButton extends Button implements Colleague { private Mediator mediator; public ColleagueButton(String caption) { super(caption); public void setmediator(mediator mediator) { // Mediator 를보관 this.mediator = mediator; public void setcolleagueenabled(boolean enabled) { // Mediator 가유효 / 무효를지시한다. setenabled(enabled); import java.awt.textfield; import java.awt.color; import java.awt.event.textlistener; import java.awt.event.textevent; public class ColleagueTextField extends TextField implements TextListener, Colleague { private Mediator mediator; public ColleagueTextField(String text, int columns) { // 생성자 super(text, columns); public void setmediator(mediator mediator) { // Mediator를보관 this.mediator = mediator; public void setcolleagueenabled(boolean enabled) { // Mediator가유효 / 무효를지시한다. setenabled(enabled); setbackground(enabled? Color.white : Color.lightGray); public void textvaluechanged(textevent e) { // 문자열이변하면 Mediator에게통지 mediator.colleaguechanged(this);
import java.awt.checkbox; import java.awt.checkboxgroup; import java.awt.event.itemlistener; import java.awt.event.itemevent; public class ColleagueCheckbox extends Checkbox implements ItemListener, Colleague { private Mediator mediator; public ColleagueCheckbox(String caption, CheckboxGroup group, boolean state) { // 생성자 super(caption, group, state); public void setmediator(mediator mediator) { // Mediator 를보관 this.mediator = mediator; public void setcolleagueenabled(boolean enabled) { // Mediator 가유효 / 무효를지시한다. setenabled(enabled); public void itemstatechanged(itemevent e) { // 상태가변하면 Mediator 에게통지 mediator.colleaguechanged(this); public class Main { static public void main(string args[]) { new LoginFrame("Mediator Sample");
import java.awt.frame; import java.awt.label; import java.awt.color; import java.awt.checkboxgroup; import java.awt.gridlayout; import java.awt.event.actionlistener; import java.awt.event.actionevent; public class LoginFrame extends Frame implements ActionListener, Mediator { private ColleagueCheckbox checkguest; private ColleagueCheckbox checklogin; private ColleagueTextField textuser; private ColleagueTextField textpass; private ColleagueButton buttonok; private ColleagueButton buttoncancel; // 생성자 // Colleague 들을생성해서배치한후에표시를실행한다. public LoginFrame(String title) { super(title); setbackground(color.lightgray); // 레이아웃매니저를사용해서 4*2 의그리드를만듞다. setlayout(new GridLayout(4, 2)); // Colleague 들의생성 createcolleagues(); // 배치 add(checkguest); add(checklogin); add(new Label("Username:")); add(textuser); add(new Label("Password:")); add(textpass); add(buttonok); add(buttoncancel); // 유효 / 무효의초기지정 colleaguechanged(checkguest); // 표시 pack(); show(); // Colleague 들을생성한다. public void createcolleagues() { // 생성 CheckboxGroup g = new CheckboxGroup(); checkguest = new ColleagueCheckbox("Guest", g, true); checklogin = new ColleagueCheckbox("Login", g, false); textuser = new ColleagueTextField("", 10); textpass = new ColleagueTextField("", 10); textpass.setechochar('*'); buttonok = new ColleagueButton("OK"); buttoncancel = new ColleagueButton("Cancel"); // Mediator 의세트 checkguest.setmediator(this); checklogin.setmediator(this); textuser.setmediator(this); textpass.setmediator(this); buttonok.setmediator(this); buttoncancel.setmediator(this); // Listener 의세트 checkguest.additemlistener(checkguest); checklogin.additemlistener(checklogin); textuser.addtextlistener(textuser); textpass.addtextlistener(textpass); buttonok.addactionlistener(this); buttoncancel.addactionlistener(this); // Colleague로부터의통지로각 Colleague의유효 / 무효를판정한다. public void colleaguechanged(colleague c) { if (c == checkguest c == checklogin) { if (checkguest.getstate()) { // Guest 모드 textuser.setcolleagueenabled(false); textpass.setcolleagueenabled(false); buttonok.setcolleagueenabled(true); else { // Login 모드 textuser.setcolleagueenabled(true); userpasschanged(); else if (c == textuser c == textpass) { userpasschanged(); else { System.out.println("colleagueChanged:unknown colleague = " + c); // textuser 또는 textpass의변경이있었다. // 각 Colleague의유효 / 무효를판정한다. private void userpasschanged() { if (textuser.gettext().length() > 0) { textpass.setcolleagueenabled(true); if (textpass.gettext().length() > 0) { buttonok.setcolleagueenabled(true); else { buttonok.setcolleagueenabled(false); else { textpass.setcolleagueenabled(false); buttonok.setcolleagueenabled(false); public void actionperformed(actionevent e) { System.out.println("" + e); System.exit(0);
용도 데이터구조안에많은요소가저장되어있고, 각요소들에대해서여러가지유형의 처리 를수행할경우에활용 데이터구조와처리를분리 데이터구조내부를 traversal 하는 visitor 를나타내는클래스를준비해서그클래스에게처리를일임함. 새로운처리를추가하고자할때에는새로운 visitor 를생성
public abstract class Visitor { public abstract void visit(file file); public abstract void visit(directory directory); public interface Acceptor { public abstract void accept(visitor v); import java.util.iterator; public abstract class Entry implements Acceptor { public abstract String getname(); // 이름을얻는다. public abstract int getsize(); // 사이즈를얻는다. public Entry add(entry entry) throws FileTreatmentException { // 엔트리를추가 throw new FileTreatmentException(); public Iterator iterator() throws FileTreatmentException { // Iterator의생성 throw new FileTreatmentException(); public String tostring() { // 문자열표현 return getname() + " (" + getsize() + ")"; public class File extends Entry { private String name; private int size; public File(String name, int size) { this.name = name; this.size = size; public String getname() { return name; public int getsize() { return size; public void accept(visitor v) { v.visit(this); import java.util.iterator; import java.util.vector; public class Directory extends Entry { private String name; // 디렉토리의이름 private Vector dir = new Vector(); // 디렉토리엔트리의집합 public Directory(String name) { // 생성자 this.name = name; public String getname() { // 이름을얻는다. return name; public int getsize() { // 사이즈를얻는다. int size = 0; Iterator it = dir.iterator(); while (it.hasnext()) { Entry entry = (Entry)it.next(); size += entry.getsize(); return size; public Entry add(entry entry) { // 엔트리의추가 dir.add(entry); return this; public Iterator iterator() { // Iterator의생성 return dir.iterator(); public void accept(visitor v) { // 방문자를받아들임 v.visit(this);
import java.util.iterator; public class ListVisitor extends Visitor { private String currentdir = ""; 는디렉토리명 public void visit(file file) { 다. System.out.println(currentdir + "/" + file); // 현재주목하고있 // 파일을방문했을때호출된 public void visit(directory directory) { // 디렉토리를방문했을때호출된다. System.out.println(currentdir + "/" + directory); String savedir = currentdir; currentdir = currentdir + "/" + directory.getname(); Iterator it = directory.iterator(); while (it.hasnext()) { Entry entry = (Entry)it.next(); entry.accept(this); currentdir = savedir; public class FileTreatmentException extends RuntimeException { public FileTreatmentException() { public FileTreatmentException(String msg) { super(msg); public class Main { public static void main(string[] args) { try { System.out.println("Making root entries..."); Directory rootdir = new Directory("root"); Directory bindir = new Directory("bin"); Directory tmpdir = new Directory("tmp"); Directory usrdir = new Directory("usr"); rootdir.add(bindir); rootdir.add(tmpdir); rootdir.add(usrdir); bindir.add(new File("vi", 10000)); bindir.add(new File("latex", 20000)); rootdir.accept(new ListVisitor()); System.out.println(""); System.out.println("Making user entries..."); Directory Kim = new Directory("Kim"); Directory Lee = new Directory("Lee"); Directory Kang = new Directory("Kang"); usrdir.add(kim); usrdir.add(lee); usrdir.add(kang); Kim.add(new File("diary.html", 100)); Kim.add(new File("Composite.java", 200)); Lee.add(new File("memo.tex", 300)); Kang.add(new File("game.doc", 400)); Kang.add(new File("junk.mail", 500)); rootdir.accept(new ListVisitor()); catch (FileTreatmentException e) { e.printstacktrace();
용도 Instance 작성을하위 class 에게위임 Instance 를만드는방법은상위 class 에서결정하지만, 구체적인 class 의이름까지는정하지않음 Instance 를생성을위한 framework 과실제로 instance 를생성하는 class 를분리함
package framework; public abstract class Product { public abstract void use(); package framework; public abstract class Factory { public final Product create(string owner) { Product p = createproduct(owner); registerproduct(p); return p; protected abstract Product createproduct(string owner); protected abstract void registerproduct(product product); package idcard; import framework.*; public class IDCard extends Product { private String owner; IDCard(String owner) { System.out.println(owner + " 의카드를만듭니다."); this.owner = owner; public void use() { System.out.println(owner + " 의카드를만듭니다."); public String getowner() { return owner; package idcard; import framework.*; import java.util.*; public class IDCardFactory extends Factory { private Vector owners = new Vector(); protected Product createproduct(string owner) { return new IDCard(owner); protected void registerproduct(product product) { owners.add(((idcard)product).getowner()); public Vector getowners() { return owners;
import framework.*; import idcard.*; public class Main { public static void main(string[] args) { Factory factory = new IDCardFactory(); Product card1 = factory.create(" 홍길동 "); Product card2 = factory.create(" 이순신 "); Product card3 = factory.create(" 강감찬 "); card1.use(); card2.use(); card3.use();