[Java 상속] 디자인 패턴 - 싱글톤, 체인, 메서드 체이닝 완벽 가이드

객체 생성과 처리 흐름을 효율적으로 관리하는 세 가지 주요 디자인 패턴의 구현 방법과 활용 사례를 설명하는 문서입니다.



싱글톤 패턴

전체 시스템에서 하나의 인스턴스만 생성하여 공유하는 디자인 패턴

  • stateless 객체(수정 가능한 멤버 변수가 없는 경우)
  • 객체 생성/삭제 비용이 높아 재사용이 필요한 경우
public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
//사용 예시
Singleton single = SingleTon.getInstance();
single.log("test log"); //원하는 기능 사용



체인 패턴 (책임 연쇄)

요청을 처리하는 객체들이 체인 형태로 연결되어, 각 객체가 요청을 처리하거나 다음 처리자에게 전달하는 패턴

코드는 이분 참고 했습니다! https://hirlawldo.tistory.com/178

// Number.java
public class Number {
    private final int number;
    
    public Number(int number) {
        this.number = number;
    }
    
    public int getNumber() {
        return number;
    }
}

// Processor.java 추상 클래스
public abstract class Processor {
    private Processor next;
    protected String name;
    
    protected Processor(String name) {
        this.name = name;
    }
    
    public void process(Number request) {
        if (next != null) {
            next.process(request);
        }
    }
    
    public void setNext(Processor next) {
        this.next = next;
    }
}

// PositiveProcessor.java
public class PositiveProcessor extends Processor {
    public PositiveProcessor(String name) {
        super(name);
    }
    
    @Override
    public void process(Number request) {
        if (request.getNumber() > 0) {
            System.out.println(name + ": " + request.getNumber());
        } else {
            super.process(request);
        }
    }
}

// ZeroProcessor.java
public class ZeroProcessor extends Processor {
    public ZeroProcessor(String name) {
        super(name);
    }
    
    @Override
    public void process(Number request) {
        if (request.getNumber() == 0) {
            System.out.println(name + ": " + request.getNumber());
        } else {
            super.process(request);
        }
    }
}

// NegativeProcessor.java
public class NegativeProcessor extends Processor {
    public NegativeProcessor(String name) {
        super(name);
    }
    
    @Override
    public void process(Number request) {
        if (request.getNumber() < 0) {
            System.out.println(name + ": " + request.getNumber());
        } else {
            super.process(request);
        }
    }
}

// Chain.java
public class Chain {
    private final Processor chain;
    
    public Chain() {
        this.chain = buildProcessorChain();
    }
    
    private Processor buildProcessorChain() {
        Processor firstProcessor = new NegativeProcessor("음수 프로세서");
        Processor secondProcessor = new PositiveProcessor("양수 프로세서");
        Processor thirdProcessor = new ZeroProcessor("제로 프로세서");
        
        firstProcessor.setNext(secondProcessor);
        secondProcessor.setNext(thirdProcessor);
        return firstProcessor;
    }
    
    public void process(Number request) {
        chain.process(request);
    }
}

// ChainTest.java
class ChainTest {
    @Test
    @DisplayName("체인 테스트")
    void chainTest() {
        Chain chain = new Chain();
        chain.process(new Number(90));
        chain.process(new Number(-50));
        chain.process(new Number(0));
        chain.process(new Number(91));
    }
}
// 출력결과
양수 프로세서: 90
음수 프로세서: -50
제로 프로세서: 0
양수 프로세서: 91


핵심 체인: buildProcessorChain 메서드는 프로세서들을 순서대로 연결

  • 첫 번째 프로세서(NegativeProcessor)는 next로 두 번째 프로세서(PositiveProcessor)를 갖는다.
  • 두 번째 프로세서(PositiveProcessor)는 next로 세 번째 프로세서(ZeroProcessor)를 갖는다.
  • 첫 번째 프로세서를 반환하여 체인의 시작점을 제공한다.


자세한 동작 과정:

  • Number(90) 요청 처리 과정:

    1. NegativeProcessor에서 request.getNumber() > 0이 아니므로 super.process(request) 호출.

    2. 부모 클래스의 process에서 next(즉, PositiveProcessor)의 process 호출.

    3. PositiveProcessor에서 request.getNumber() > 0이므로 System.out.println 실행.

  • Number(-50) 요청 처리 과정:

    1. NegativeProcessor에서 request.getNumber() < 0이므로 System.out.println 실행.
  • Number(0) 요청 처리 과정:

    1. NegativeProcessor에서 request.getNumber() < 0이 아니므로 super.process(request) 호출.

    2. 부모 클래스의 process에서 next(PositiveProcessor) 호출.

    3. PositiveProcessor에서 request.getNumber() > 0이 아니므로 super.process(request) 호출.

    4. 부모 클래스의 process에서 next(ZeroProcessor) 호출.

    5. ZeroProcessor에서 request.getNumber() == 0이므로 System.out.println 실행.



메서드 체이닝 패턴

메서드가 자기 자신의 인스턴스를 반환하여 연속적인 메서드 호출이 가능하게 하는 패턴

반환 타입을 Calculator(본인)으로 선언하면 쉽게 만들 수 있다.

public class Calculator {
    private int value;
    
    public Calculator add(int num) {
        this.value += num;
        return this;
    }
    
    public Calculator multiply(int num) {
        this.value *= num;
        return this;
    }
}

// 사용 예시
Calculator calc = new Calculator()
    .add(5)
    .multiply(3);


각 패턴은 특정 문제를 해결하기 위한 검증된 솔루션을 제공하며, 상황에 맞게 적절히 선택하여 사용해야 합니다.

댓글남기기