개발/객체지향개발

[디자인패턴] 싱글톤 패턴, 팩토리 패턴

대왕판다 2023. 4. 12. 23:09

의존성 주입과 싱글톤 패턴

  • 의존성 주입은 객체 간의 결합도를 낮추기 위한 설계 패턴
  • 객체 생성 및 관리를 담당하는 제 3자인 컨테이너가 객체 간의 의존성을 연결해주는 방식
  • 객체는 자신이 필요로 하는 의존 객체가 어떤 것인지 알지 못하더라도, 외부(컨테이너, 의존성 주입자)에서 주입받은 객체를 사용하여 동작할 수 있다.
  • 싱글톤 패턴은 객체를 하나의 인스턴스만 생성하여 공유하는 방식의 패턴이다.
  • 의존성 주입을 통해 싱글톤 객체를 사용하 객체 간의 결합도를 낮출 수 있다.()
  • 그렇기에 모듈들을 쉽게 교체할 수 있다→테스팅과 마이그레이션이 수월하다.

의존성 주입을 하지 않은 경우

// 싱글톤 클래스
public class Singleton {
    private static Singleton instance;

    private Singleton() {
    }

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

    public void doSomething() {
        // some code
    }
}

public class Client {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        singleton.doSomething();

        // Singleton 객체에 의존하는 다른 객체 생성
        OtherClass otherClass = new OtherClass();
        otherClass.doSomethingElse();
    }
}

// 싱글톤 클래스에 의존하는 클래스
public class OtherClass {
    private Singleton singleton;

    public OtherClass() {
// 인스턴스를 직접 생성
        singleton = Singleton.getInstance();
    }

    public void doSomethingElse() {
        singleton.doSomething();
    }
}
  1. 위 코드에서는 OtherClass 클래스에서 Singleton 객체를 직접 생성하는 코드가 중복되어 나타나고
  2. Singleton 객체의 생성 및 관리에 대한 책임OtherClass 클래스로 넘어가게 되므로, 유지보수 및 확장성이 저하된다.

의존성 주입을 사용하는 경우

public class Singleton {
    // same code
}

public class Client {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        singleton.doSomething();

        // 생성자를 통해 의존성 주입
        OtherClass otherClass = new OtherClass(singleton);
        otherClass.doSomethingElse();
    }
}

public class OtherClass {
    private Singleton singleton;

    // 생성자를 통해 의존성 주입
    public OtherClass(Singleton singleton) {
        this.singleton = singleton;
    }

    public void doSomethingElse() {
        singleton.doSomething();
    }
}
  1. **OtherClass**클래스에서 **Singleton**객체를 생성하지 않고, 생성자를 통해 의존성 주입을 받도록 변경
  2. 객체의 생성/관리 책임이 Client 클래스로 다시 돌아가므로 유지보수 및 확장성이 개선

팩토리 패턴

팩토리패턴이란?

  • 객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화
  • 상위 클래스가 중요한 뼈대 결정 ↔ 하위 클래스에서 객체 생성에 관한 구체적 내용 결정

팩토리 패턴의 효용성

  • 객체 생성 로직의 캡슐화 (+유연화)
    • 객체 생성 과정을 외부로부터 감춘다.
    • 사용하는 코드에서는 객체 생성 과정을 알 필요가 없어지며
    • 객체 생성 과정이 변경되더라도 클라이언트 코드에 영향을 미치지 않음
  • 객체 생성의 표준화
    • 팩토리 패턴을 사용하면 객체를 생성하는 방식을 표준화할 수 있음
    • 이를 통해 객체 생성을 일관성 있게 처리할 수 있으며 유지보수 및 확장성이 개선
  • 코드 중복 제거
    • 팩토리 패턴을 사용하면 객체 생성 로직을 중복으로 작성하는 것을 방지
    public interface Animal {
        void makeSound();
    }
    
    public class Client {
        public static void main(String[] args) {
    // dog 객체를 직접 생성
            Animal dog = new Dog();
            dog.makeSound();
        }
    }
    
    public class Dog implements Animal {
        @Override
        public void makeSound() {
            System.out.println("Bark!");
        }
    }
    
    public interface Animal {
        void makeSound();
    }
    
    public class Dog implements Animal {
        @Override
        public void makeSound() {
            System.out.println("Bark!");
        }
    }
    
    public class AnimalFactory {
        public static Animal createAnimal(String type) {
            switch (type) {
                case "dog":
                    return new Dog();
                default:
                    // some code
            }
        }
    }
    
    public class Client {
        public static void main(String[] args) {
    // 애니멀팩토리를 이용해 객체 생성
            Animal dog = AnimalFactory.createAnimal("dog");
            dog.makeSound();
        }
    }