디자인 패턴 정리
디자인 패턴
싱글톤 패턴
- 전역 변수를 사용하지 않고 객체를 하나만 생성하도록 하며, 생성된 객체를 어디에서든지 참조할 수 있도록 하는 패턴이다.
- 하나의 인스턴스만을 생성하며, 모든 클라이언트에게 동일한 인스턴스를 반환한다.
private
생성자를 가지는 특징을 가지며, 생성된 싱글톤 오브젝트는 저장할 수 있는 자신과 같은 타입의 스태틱 필드를 정의한다.
문제점
- 의존 관계상 클라이언트가 구체 클래스에 의존한다.
- private 생성자 때문에 테스트가 어렵다.
- 객체 인스턴스를 하나만 생성해서 공유하는 방식 때문에, 싱글톤 객체를 stateful하게 설계 했을 경우 장애 발생요인이 된다.
- 싱글톤의 단점을 해결하기 위해 무상태(stateless)로 설계해야 한다.
- 특정 클라이언트에 의존적인 필드가 있으면 안된다.
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
- 가급적 읽기 전용으로 만들고, 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터,
ThreadLocal
등을 사용해야 한다.
public class Singleton {
private static Singleton instance;
private Singleton() { }
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
가교 패턴 (= Bridge pattern)
- 추상부와 구현부를 분리하는 디자인 패턴
- 기능은 인터페이스를 통해 정의 & 이용되고, 해당 인터페이스를 따르는 클래스를 통해 구현된다.
- 클라이언트에서 추상부와 구현부를 독립적으로 수정 및 확장할 수 있다.
- 객체지향 설계의 SOLID 원칙 중 단일 책임 원칙(SRP)과 개방 폐쇄 원칙(OCP)에 부합한 패턴이다.
// Abstraction. 추상부
public abstract class Shape {
protected Color color;
public Shape(Color color) {
this.color = color;
}
public abstract void draw();
}
// Implementor. 구현 인터페이스
public interface Color {
void applyColor();
}
// Concrete Implementors 구현부
public class RedColor implements Color {
@Override
public void applyColor() {
System.out.println("Applying red color");
}
}
public class BlueColor implements Color {
@Override
public void applyColor() {
System.out.println("Applying blue color");
}
}
// Refined Abstractions
public class Circle extends Shape {
public Circle(Color color) {
super(color);
}
@Override
public void draw() {
System.out.print("Drawing a Circle with ");
color.applyColor();
}
}
public class Square extends Shape {
public Square(Color color) {
super(color);
}
@Override
public void draw() {
System.out.print("Drawing a Square with ");
color.applyColor();
}
}
전략 패턴 (Strategy Pattern)
- 알고리즘을 객체 단위로 캡슐화하는 디자인 패턴
- 알고리즘은 인터페이스를 통해 정의 및 이용되고 , 해당 인터페이스를 따르는 클래스를 통해 구현된다.
- 해당 패턴을 통해서 사용자는 알고리즘을 필요에 따라 바꿔서 사용할 수 있다.
-
객체지향 설계의 SOLID 원칙 중 개방 폐쇄 원칙(OCP)에 부합한 패턴
- 브릿지 패턴과 구조가 비슷하지만 목적에 차이가 있다.
- 브릿지 패턴이 추상과 구현의 분리를 통한 독립적 개발의 용이성에 중점을 둔다면, 전략 패턴은 알고리즘의 캡슐화를 통한 알고리즘 변경의 유연성에 중점을 둔다.
// Strategy Interface
public interface PaymentStrategy {
void pay(int amount);
}
// Concrete Strategies : 객체에 따라 알고리즘을 달리 구현
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
private String name;
public CreditCardPayment(String cardNumber, String name) {
this.cardNumber = cardNumber;
this.name = name;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card.");
}
}
public class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal.");
}
}
// Context
public class ShoppingCart {
private List<Item> items;
public ShoppingCart() {
this.items = new ArrayList<>();
}
public void addItem(Item item) {
items.add(item);
}
public void checkout(PaymentStrategy paymentMethod) {
int totalAmount = calculateTotal();
paymentMethod.pay(totalAmount);
}
private int calculateTotal() {
int total = 0;
for (Item item : items) {
total += item.getPrice();
}
return total;
}
}
빌더 패턴
- 빌더 패턴은 복잡한 객체를 생성하는 과정을 캡슐화하고, 객체를 생성하기 위한 단계별 빌드를 제공하는 패턴
- 객체 생성을 단순화하고, 객체의 속성을 설정하는 과정을 명확하게 만들어 준다.
public class Person {
private String firstName;
private String lastName;
private int age;
private Person(Builder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
}
public static class Builder {
private String firstName;
private String lastName;
private int age;
public Builder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Person build() {
return new Person(this);
}
}
}
팩토리 메서드 패턴
- 객체 생성시 어떤 클래스의 인스턴스로 생성할 지 서브 클래스로 미루는 디자인 패턴이다.
- 슈퍼 클래스에서는 객체 생성에 관한 인터페이스를 정의하고, 서브 클래스에서 실제 객체 생성을 담당한다.
- 생성자 대신에 팩토리 메소드 사용을 고려할 수 있다.
// 객체 생성에 관한 인터페이스를 정의
public abstract class Animal {
public abstract String speak();
}
public class Dog extends Animal {
@Override
public String speak() {
return "Woof!";
}
}
public class Cat extends Animal {
@Override
public String speak() {
return "Meow!";
}
}
public interface AnimalFactory {
Animal createAnimal();
}
public class DogFactory implements AnimalFactory {
@Override
public Animal createAnimal() {
return new Dog();
}
}
public class CatFactory implements AnimalFactory {
@Override
public Animal createAnimal() {
return new Cat();
}
}
퍼사드 패턴
- 서브시스템의 복잡성을 감추고, 간단한 인터페이스를 제공하는 패턴이다.
- 복잡한 시스템을 단순하게 다룰 수 있다.
class CPU {
public void processData() {
System.out.println("Processing data...");
}
}
class Memory {
public void load() {
System.out.println("Loading data...");
}
}
class HardDrive {
public void readData() {
System.out.println("Reading data...");
}
}
class ComputerFacade {
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;
public ComputerFacade() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
public void start() {
System.out.println("Starting the computer...");
cpu.processData();
memory.load();
hardDrive.readData();
System.out.println("Computer started successfully.");
}
}
- 바운디드 컨텍스트로 구분된 각각의 독립적인 애플리케이션을 UI 서버를 통해 파사드 역할을 담당하도록 두고, 각 바운디드 컨텍스트에서 UI 서버와 통신하기 위해 HTTP, Protobuf, Thrift와 같은 방식을 이용할 수 있다.
Leave a comment