Singleton Pattern(싱글톤 패턴) 이란?

: 특정 객체 하나만 존재 해야할때 쓰는 전략

  • 싱글톤 패턴을 사용하는 이유
    • new 를 통해 객체 를 한번만 생성하므로 메모리 낭비 방지
    • 다른 클래스의 인스턴스들이 데이터 공유하기 쉽다.
    • DBCP처럼 공통된 객체를 여러개 생성해서 사용하는 경우 사용
  • 주로 쓰는 곳
    • 커넥션풀, 쓰레드풀, 캐시, 로그 객체 등
  • 문제점
    • 멀티쓰레드 환경에서 동기화처리해야함
    • 너무 싱글톤에 의존하여 많이 사용하면 코드가 복잡해질수록 유지보수가 힘들어짐
public class Singleton implements Serializable {

  private static Singleton instance;

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (instance == null) {
      instance = new Singleton();
    }
    return instance;
  }

}

Strategy Pattern(전략 패턴) 이란?

: 여러 알고리즘을 하나의 추상적인 접근점(인터페이스)을 만들어 접근점에서 알고리즘이 서로 교환 가능하도록 하는 패턴

  • 전략패턴은 언제 사용해야 되는가?
    • 행동들이 조금씩 다르고 개념적으로 관련된 많은 클래스들이 존재할 때
    • 하나의 클래스가 많은 행동을 정의하고 이런 행동들이 클래스 안에서 다중 조건문의 모습을 취할 때
  • 장점
    • 코드 중복 방지
    • 쉽게 확장 가능 (OCP 원칙 준수)
    • 독립적이므로 결합도가 느슨해진다.
  • 단점
    • 객체가 무한정 늘어나 복잡
    • 오히려 코드가 늘어나 익숙하지 않으면 필요성 못느낌

전략 패턴이 적용되지 않은 코드

  • 할인정책이 추가될경우 if-else 분기를 계속 추가해줘야함
  • 이는 하나의 메소드에서 할인정책 확인, 물건 가격 확인 등 여러가지 일을 하게 된다.
public class Calculator {

  public double calculate(boolean isFirstGuest, boolean isLastGuest, List<Item> items) {
    double sum = 0;
    for (Item item : items) {
      if (isFirstGuest) {
        sum += item.getPrice() * 0.9;
      } else if (!item.isFresh()) {
        sum += item.getPrice() * 0.8;
      } else if (isFirstGuest) {
        sum += item.getPrice() * 0.8;
      } else {
        sum += item.getPrice();
      }
    }
    return sum;
  }
}

public class Item {
  private final String name;
  private final int price;

  public Item(String name, int price) {
    this.name = name;
    this.price = price;
  }

  public int getPrice() {
    return price;
  }

  public int getName() {
    return name;
  }
  
  public boolean isFresh() {
    return true;
  }
}

전략패턴이 적용된 코드

  • 할인전략을 DiscountPolicy 인터페이스로 분리
    public interface DiscountPolicy {
  double calculateWithDisCountRate(Item item);
}

public class FirstCustomerDiscount implements DiscountPolicy {
  @Override
  public double calculateWithDisCountRate(Item item) {
    return item.getPrice() * 0.9;
  }
}

public class LastCustomerDiscount implements DiscountPolicy {
  @Override
  public double calculateWithDisCountRate(Item item) {
    return item.getPrice() * 0.8;
  }
}

public class UnFreshFruitDiscount implements DiscountPolicy {
  @Override
  public double calculateWithDisCountRate(Item item) {
    return item.getPrice() * 0.8;
  }
}
  • 그리고 Calculator 클래스에서 필요한 할인정책을 주입받아 사용
public class Calculator {

  private final DiscountPolicy discountPolicy;

  public Calculator(DiscountPolicy discountPolicy) {
    this.discountPolicy = discountPolicy;
  }

  public double calculate(List<Item> items) {
    double sum = 0;
    for (Item item : items) {
      sum += discountPolicy.calculateWithDisCountRate(item);
    }
    return sum;
  }
}
  • 위의 Calculator 객체에 해당하는 할인전략 객체를 주입하여 처리
    public class FruitController {
  public static void main(String[] args) {
    Calculator calculator = new Calculator(new FirstCustomerDiscount());
    calculator.calculate(Arrays.asList(
            new Item("Apple", 3000),
            new Item("Banana", 3000),
            new Item("Orange", 2000),
            new Item("Pitch", 4000)
    ));
  }
}

Factory Method(팩토리 메소드 패턴) 이란?

: 객체를 만드는 부분을 Sub class에 맡기는 패턴.

  • 장점
    • 객체 간의 결합도를 낮춰 유지보수가 용이
    • 객체들을 한곳에서 관리
  • 단점
    • 불필요하게 많은 클래스를 정의
    • 더 복잡해질 수 있음
  • UI컴포넌트 라는 객체 를 상속받은 Button, Switch, Dropdown 객체가 있음
abstract class Component {
  protected abstract String getCompName ();
  public Component () {
    System.out.println(this.getCompName() + " 생성");
  }
}

class Button extends Component {
  @Override
  protected String getCompName() { return "버튼"; }
}
class Switch extends Component {
  @Override
  protected String getCompName() { return "스위치"; }
}
class Dropdown extends Component {
  @Override
  protected String getCompName() { return "드랍다운"; }
}
  • 사용 용도에 따라 객체 생성하는 팩토리객체
class CompFactory {
  public Component getComp (Usage usage) {
    if (usage == Usage.PRESS) {
      return new Button();
    } else if (usage == Usage.TOGGLE) {
      return new Switch();
    } else {
      return new Dropdown();
    }
  }
}
  • 미 적용시 component객체가 변경시 변경된곳을 일일히 찾아 서 수정해줘야함 하지만 팩토리패턴 적용시 CompFactory객체만 수정하여 적용 가능
class Console {

  private CompFactory compFactory = new CompFactory();

  Component comp1;
  Component comp2;
  Component comp3;

  // 팩토리패턴 미적용
  void withoutFactory () {
    comp1 = new Button();
    comp2 = new Switch();
    comp3 = new Dropdown();
  }

  // 팩토리패턴 적용
  void withFactory () {
    comp1 = compFactory.getComp(Usage.PRESS);
    comp2 = compFactory.getComp(Usage.TOGGLE);
    comp3 = compFactory.getComp(Usage.EXPAND);
  }
}

enum Usage {
  PRESS, TOGGLE, EXPAND
}
참고
chanhee.kim's profile image

chanhee.kim

2021-05-19 13:06

Read more posts by this author