Design Pattern

GoF์˜ ๋””์ž์ธ ํŒจํ„ด

  • Gang of Four

    • Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides ๋ผ๋Š” ๋„ค๋ช…์˜ ์ €์ž๋ฅผ ๋œปํ•จ

  • ์†Œํ”„ํŠธ์›จ์–ด ๋””์ž์ธ์—์„œ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์„ ์ œ๊ณตํ•˜๋Š” 23๊ฐ€์ง€ ๋””์ž์ธ ํŒจํ„ด์˜ ์กฐํ•ฉ

๊ฐ์ฒด์ง€ํ–ฅ ์›์น™

  • ๋ฐ”๋€Œ๋Š” ๋ถ€๋ถ„์€ ์บก์Šํ™”ํ•œ๋‹ค

  • ์ƒ์†๋ณด๋‹ค๋Š” ๊ตฌ์„ฑ์„ ํ™œ์šฉํ•œ๋‹ค

  • ๊ตฌํ˜„๋ณด๋‹ค๋Š” ์ธํ„ฐํŽ˜์ด์Šค์— ๋งž์ถฐ์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•œ๋‹ค

  • ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๊ฐ์ฒด ์‚ฌ์ด์—์„œ๋Š” ๊ฐ€๋Šฅํ•˜๋ฉด ๋Š์Šจํ•œ ๊ฒฐํ•ฉ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค

  • ํด๋ž˜์Šค๋Š” ํ™•์žฅ์—๋Š” ์—ด๋ ค ์žˆ์–ด์•ผ ํ•˜์ง€๋งŒ ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€์žˆ์–ด์•ผ ํ•œ๋‹ค(OCP)

  • ์ถ”์ƒํ™”๋œ ๊ฒƒ์— ์˜์กดํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ  ๊ตฌ์ƒ ํด๋ž˜์Šค์— ์˜์กดํ•˜์ง€ ์•Š๊ฒŒ ๋งŒ๋“ ๋‹ค.

์ƒ์„ฑ ํŒจํ„ด(Creational Patterns)

  • ๊ฐ์ฒด์˜ ์ƒ์„ฑ๊ณผ ์ดˆ๊ธฐํ™”์— ๊ด€๋ จ๋œ ํŒจํ„ด

  • ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์„ ์ถ”์ƒํ™”

  • ์ฃผ์š” ํŒจํ„ด: Singleton, Factory Method, Abstract Factory, Builder, Prototype

๊ตฌ์กฐ ํŒจํ„ด(Structural Patterns)

  • ํด๋ž˜์Šค๋‚˜ ๊ฐ์ฒด๋ฅผ ํ•ฉ์„ฑํ•˜์—ฌ ๋” ํฐ ๊ตฌ์กฐ๋กœ ๋งŒ๋“œ๋Š” ํŒจํ„ด

  • ํด๋ž˜์Šค์™€ ๊ฐ์ฒด๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ๋”ํฐ ๊ตฌ์กฐ๋กœ ๋งŒ๋“ฆ

  • ์ฃผ์š” ํŒจํ„ด: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy

ํ–‰๋™ ํŒจํ„ด(Behavioral Paterrns)

  • ๊ฐ์ฒด ๊ฐ„์˜ ํ†ต์‹ ๊ณผ ์ฑ…์ž„์„ ๋ถ„์‚ฐํ•˜๋Š” ํŒจํ„ด

  • ์•Œ๊ณ ๋ฆฌ์ฆ˜๊ณผ ๊ฐ์ฒด ๊ฐ„์˜ ์ฑ…์ž„์„ ๋ถ„๋ฆฌ

  • ์ฃผ์š” ํŒจํ„ด: Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor

์ƒ์„ฑ ํŒจํ„ด

ํŒฉํ† ๋ฆฌ ํŒจํ„ด

  • ๊ฐ์ฒด ์ƒ์„ฑ์„ ์บก์Šํ™”ํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด

  • ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๋กœ๋ถ€ํ„ฐ ๋ถ„๋ฆฌํ•˜์—ฌ ์œ ์—ฐ์„ฑ์„ ์ œ๊ณต

  • ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์„ ์„œ๋ธŒํด๋ž˜์Šค์—์„œ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ํŠน์ง•

  • ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ณ„๋„ ๋„๊ตฌ๋ฅผ ์ œ๊ณต

// ๋„ํ˜• ์ธํ„ฐํŽ˜์ด์Šค
interface Shape {
  void draw()
}

// ๊ตฌ์ฒด์ ์ธ ๋„ํ˜• ํด๋ž˜์Šค - ์›
class Circle implements Shpae {
  @Override
   public void draw() {
  }
}

// ๊ตฌ์ฒด์ ์ธ ๋„ํ˜• ํด๋ž˜์Šค - ์ •์‚ฌ๊ฐํ˜•
class Square implements Shpae {
  @Override
   public void draw() {
  }
}

// ํŒฉํ† ๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค
interface ShapeFactory {
  Shape createShape();
}

// ์›์„ ์ƒ์„ฑํ•˜๋Š” ํŒฉํ† ๋ฆฌ
class CircleFactory implements ShapeFactory {
  @Override
  public Shape createShape() {
    return new Circle();
  }
}

// ์ •์‚ฌ๊ฐํ˜•์„ ์ƒ์„ฑํ•˜๋Š” ํŒฉํ† ๋ฆฌ
class SquareFactory implements ShapeFactory {
  @Override
  public Shape createShape() {
    return new Square();
  }
}

// ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ
public class FactoryPatternExample {
  public state void main(String[] args) {
    // ์›์„ ์ƒ์„ฑํ•˜๋Š” ํŒฉํ† ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ๋„ํ˜• ์ƒ์„ฑ
    ShapeFactory circleFactory = new CircleFactory();
    Shape circle = circleFactory.createShape();
    circle.draw();
    
    // ์ •์‚ฌ๊ฐํ˜•์„ ์ƒ์„ฑํ•˜๋Š” ํŒฉํ† ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ๋„ํ˜• ์ƒ์„ฑ
    ShapeFactory squareFactory = new SquareFactory();
    Shape square = squareFactory.createShape();
    square.draw();
  }
}

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด

  • ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋งŒ์„ ๊ฐ–๋„๋ก ๋ณด์žฅ

public class Singleton {
  private static Singleton intstance;
  // ์™ธ๋ถ€์—์„œ ์ƒ์„ฑํ•  ์ˆ˜ ์—†๋„๋ก ์ƒ์„ฑ์ž private
  private Singleton() {}
  
  // ์˜ค์ง ์ƒ์„ฑ์ž๋Š” getInstance()๋ฅผ ํ†ตํ•ด ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์„œ๋“œ ์ œ๊ณต
  public static Singleton getInstance() {
    if (instace == null) {
      instance = new Singleton();
    }
    return instacne;
  }
}

ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด

  • ๊ฐ์ฒด ์ƒ์„ฑ์„ ์บก์Šํ™”

  • ๊ฐ์ฒด ์ƒ์„ฑ์„ ์„œ๋ธŒ ํด๋ž˜์Šค์—์„œ ๊ฒฐ์ •ํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ

interface Product {
  void create();
}

class ConcreteProduct implements Product {
  @Override
  public void create() {
  }
}
  
abstract class Creator {
  public abstract Product factoryMethod();
}
  
class ConcreteCreator extends Creator {
  @Override 
  public Product factoryMethod() {
    return new ConcreteProduct();
  }
} 

๋นŒ๋” ํŒจํ„ด(Builder Pattern)

  • ๋ณต์žกํ•œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ„ด

  • ๊ฐ์ฒด ์ƒ์„ฑ์˜ ๋‹จ๊ณ„๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ณต์žก์„ฑ์„ ํ•ด๊ฒฐ

  • ๊ฐ์ฒด์˜ ์ƒ์„ฑ ๊ณผ์ •์„ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ์กฐํ•ฉ

class Product {
  private String part1;
  private String part2;
  
  public void setPart1(String part1) {
    this.part1 = part1;
  }
  
  public void setPart2(String part1) {
    this.part2 = part2;
  }
  
  @Override
  public String toString() {
    return "Product [part1=" + part1 + ", part2=" +part2 + "]";
  }
}

// ๋นŒ๋” ์ธํ„ฐํŽ˜์ด์Šค
interface Builder {
  void buildPart1(String part1);
  void buildPart2(String part2);
  Product getResult();
}

// ๊ตฌ์ฒด์ ์ธ ๋นŒ๋” ํด๋ž˜์Šค
class ConcreteBuilder implements Builder {
  private Product product = new Product();
  
  @Override
  public void buildPart1(String part1) {
    product.setPart1(part1);
  }
  
  @Override
  public void buildPart2(String part2) {
    product.setPart2(part2);
  }  
  
  @Override
  public Product getResult() {
    return project
  }
}

// ์ˆœ์„œ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ์ „๋žต ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ ex

// ๋””๋ ‰ํ„ฐ ํด๋ž˜์Šค (์ƒ์„ฑ ์ˆœ์„œ๋ฅผ ์ •์˜)
class Director {
  public Product construct(Builder builder) {
    builder.buildPart1("Part1");
    builder.buildPart2("Part2");
    return builder.getResult();
  }
}

// ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ'
public class BuilderPatternExample {
  public static void main(String[] args) {
    Builder builder = new ConcreteBuilder();
    Director director = new Director();
    
    Product product = director.construct(builder);
  }
}

๊ตฌ์กฐ ํŒจํ„ด

Facade ํŒจํ„ด

  • ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์„œ๋ธŒ์‹œ์Šคํ…œ์ด๋‚˜ ๋ณต์žกํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋‹จ์ˆœํ™”

  • ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋” ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ํŒจํ„ด

  • ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์„ ๋‹จ์ˆœํ™” ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋ž˜ํ•‘

  • ์—ฌ๋Ÿฌ ์„œ๋ธŒ์‹œ์Šคํ…œ์„ ์กฐํ•ฉํ•ด์„œ ๋ญ”๊ฐ€ ๋™์ž‘์„ ๋งŒ๋“ค๊ธฐ์—” ๋ณต์žกํ•ด์ง„๋‹ค. ์ด๋Ÿด ๋•Œ Facade ํŒจํ„ด์„ ์‚ฌ์šฉ

  • ๋ณต์žกํ•œ ์—ฌ๋Ÿฌ ์„œ๋ธŒ์‹œ์Šคํ…œ์„ ์ด์šฉํ•ด์„œ ์˜คํผ๋ ˆ์ด์…˜์„ ๋งŒ๋“ค ๋•Œ ํŒŒ์‚ฌ๋“œ ํŒจํ„ด์„ ์“ด๋‹ค.

// Subsystem classes
class Subsystem1 {
  public void operation1() {}
  
  public void operation2() {}
}

class Subsystem2 {
  public void operation1() {}
  
  public void operation2() {}
}

// Facade class
class Facade {
  private Subsystem1 subsystem1;
  private Subsystem2 subsystem2;
  private Subsystem3 subsystem3;
  
  public Facade() {
    this.subsystem1 = new SubSystem1();
    this.subsystem2 = new SubSystem2();
    this.subsystem3 = new SubSystem3();
  }
  
  // Methods that simplify complex operations
  public void operationA() {
    System.out.println('Facde Operation A");
    subsystem1.operation1();
    subsystem2.operation1();
    subsystem3.operation1();
  }
}

// Exmplae
public class FacadePatternExample {
  public static void main(String[] args) {
    Facade facade = new Facade();
    
    // Use the Facade to simplify operations
    facade.operationA();
    facade.operationB();
  }
}

Proxy ํŒจํ„ด

  • ๊ฐ์ฒด์˜ ์ ‘๊ทผ์ด๋‚˜ ์กฐ์ž‘์„ ์ค‘๊ฐ„์—์„œ ์ œ์–ดํ•˜๊ฑฐ๋‚˜ ๋ณด์กฐํ•  ๋•Œ ์‚ฌ์šฉ

  • ๊ฐ์ฒด์˜ ๋Œ€๋ฆฌ์ž๋‚˜ ๋Œ€๋ณ€์ž ์—ญํ• ์„ ํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉ

  • ์‹ค์ œ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ์ค‘๊ฐ„์—์„œ ์ œ์–ดํ•˜๊ฑฐ๋‚˜ ์ค‘๊ฐ„์—์„œ ๋ถ€๊ฐ€์ ์ธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํŒจํ„ด

  • ์ค‘๊ฐ„์— ๋Œ€๋ฆฌ์ž๊ฐ€ ๋‚€ ํ˜•ํƒœ

  • ์ค‘๊ฐ„์—์„œ ํ•˜๋Š” ์ผ์€ ์—†์ง€๋งŒ ์–ด๋–ค ๋™์ž‘์— ๋Œ€ํ•ด์„œ ์œ„์ž„ ๋ฐ›์€ ๊ฐ์ฒด์— ์ „๋‹ฌ

  • ๋กœ๊น… ๋ฐ ์†๋„ ์ธก์ •, ์ ‘๊ทผ ์ œ์–ด ๋“ฑ ๋ถ€๊ฐ€์ ์ธ ๋ณ„๋„ ๋™์ž‘์„ ํ• ๋•Œ ์‚ฌ์šฉ

interface Image {
  void display();
}

class RealImage implements Image {
  private String filename;
  
  public RealImage(String filename) {
    this.filename = filename;
    loadImageFromDisk();
  }
  
  private void loadImageFromDisk() {}
  
  @Override
  public void display() {}
}

// ์ด๋ฏธ์ง€์˜ ํ”„๋ก์‹œ ํด๋ž˜์Šค
// ์‹ค์งˆ์ ์ธ ๋™์ž‘์€ ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ
class ProxyImage implements Image {
  private RealImage realImage;
  private String filename;
  
  public ProxyImage(String filename) {
    this.filename = filename;
  }
  
  @Override
  public void display() {
    if (realImage == null) {
      realImage = new RealImage(filename);
    }
    realImage.display();
  }
}

// ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ
public class ProxyPatternExample {
  public static void main(String[] args) {
    // ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ๋ฐ ํ‘œ์‹œ
    Image image = new ProxyImage("example.jpg");
    
    // ์ด๋ฏธ์ง€๊ฐ€ ์ฒ˜์Œ์œผ๋กœ ํ‘œ์‹œ๋  ๋•Œ ์‹ค์ œ ์ด๋ฏธ์ง€๊ฐ€ ๋กœ๋”ฉ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ
    image.display();
    
    // ์ด๋ฏธ์ง€๊ฐ€ ์ด๋ฏธ ๋กœ๋”ฉ๋œ ๊ฒฝ์šฐ์—๋Š” ์‹ค์ œ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์‹œ ๋กœ๋”ฉํ•˜์ง€ ์•Š์Œ
    image.dispaly();
  }
}

ํ–‰๋™ ํŒจํ„ด

์ „๋žต ํŒจํ„ด(Strategy Pattern)

  • ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‚˜ ํ–‰์œ„๋ฅผ ์ •์˜ํ•˜๊ณ , ๊ฐ๊ฐ์„ ์บก์Šํ™”

  • ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๋ณ€๊ฒฝ์— ๋”ฐ๋ฅธ ์˜ํ–ฅ์„ ์ตœ์†Œํ™”

  • ๋™์ ์œผ๋กœ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์„ ํƒ

  • ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‚˜ ํ–‰์œ„(์ „๋žต)์„ ์ฃผ์ž…ํ•˜์—ฌ ๊ต์ฒดํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ

// ์ „๋žต ์ธํ„ฐํŽ˜์ด์Šค
interface SortingStrategy {
  void sort(int[] array);
}

// ๊ตฌ์ฒด์ ์ธ ์ „๋žต ํด๋ž˜์Šค - ๋ฒ„๋ธ” ์ •๋ ฌ
class BubbleSortStrategy implements SortingStrategy {
  @Override
  public void sort(int[] array) {
  //์‹ค์ œ ๋ฒ„๋ธ” ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌํ˜„
  }
}

// ๊ตฌ์ฒด์ ์ธ ์ „๋žต ํด๋ž˜์Šค - ํ€ต ์ •๋ ฌ
class QuickSortStrategy implements SortingStrategy {
  @Override
  public void sort(int[] array) {
  //์‹ค์ œ ํ€ต ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌํ˜„
  }
}

// ์ปจํ…์ŠคํŠธ ํด๋ž˜์Šค
class SortContext {
  private SortingStrategy sortingStrategy;
  
  public void setSortingStrategy(SortingStrategy sortingStrategy) {
    this.sortingStrategy = sortingStrategy;
  }
  
  public void performSort(int[] array) {
    sortingStrategy.sort(array);
  }
}

// ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ
public class StrategyPatternExample {
  public static void main(String[] args) {
    int[] arrayToSort = {5, 2, 8, 1, 7};
    
    SortContext sortContext = new SortContext();
    
    // ์ฒซ ๋ฒˆ์งธ ์ •๋ ฌ: ๋ฒ„๋ธ”
    SortContext.setSortingStrategy(new BubbleSortStrategy());
    sortContext.performSort(arrayToSort);
    
    
    // ๋‘ ๋ฒˆ์งธ ์ •๋ ฌ: ํ€ต
    SortContext.setSortingStrategy(new QuickSortStrategy());
    sortContext.performSort(arrayToSort);
  }
}

์˜ต์ €๋ฒ„ ํŒจํ„ด

  • ์–ด๋–ค ๊ฐ์ฒด์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์ข…์† ๊ฐ์ฒด๋“ค์ด ์ž๋™์œผ๋กœ ์•Œ๋ฆผ์„ ๋ฐ›๋Š”๋‹ค.

  • ๊ฐ์ฒด๋“ค์—๊ฒŒ ์•Œ๋ฆฌ๊ธฐ

// Observer interface
interface Observer {
  void update(String message);
}

// ConcreteObserver class
class ConcreteObserver implements Observer {
  private String name;
  
  public ConcreteObserver(String name) {
    this.name = name;
  }
  
  @Override
  public void update(String message) {
    System.out.println(name + " received message: " + message);
  }
}

// Subject class
class Subject {
  private List<Observer> observers = new ArrayList<>();
  
  // Add an observer to the list
  public void addObserver(Observer observer) {
    observers.add(observer);
  }
  
  // Remove an observer from the list
  public void removeObserver(Observer observer) {
    observers.remove(observer);
  }
  
  // Notify all observers with a message
  public void notifyObservers(String message) {
    for (Observer observer : observers) {
      observer.update(message);
    }
  }
}

// Example usage
public class ObserverPatternExample {
  public static void main(String[] args){
    // Create subject
    Subject subject = new Subject();
    
    // Create observers
    Observer observer1 = new ConcreteObserver("Observer 1");
    Observer observer2 = new ConcreteObserver("Observer 2");
    
    // Register observers with the subject
    subject.notifyObservers("Hello Observers!");
    
    // Unregister observer1
    subject.removeObserver(observer1);
    
    // Notify remaining observer with another message
    subject.notifiyObservers("Observer 1 has been removed.");
  }
}

Last updated