CS/개발론

[개발론] 생성 패턴 (Creational Pattern) - 싱글톤, 팩토리, 추상 팩토리, 빌더, 프로토타입

pythaac 2022. 6. 4. 20:43

생성 패턴

  • 정의
    - 인스턴스를 만드는 절차를 추상화하는 패턴
  • 역할
    - 객체 생성/합성 방법 또는 객체 표현 방법을 시스템과 분리
  • 중요한 이유
    - 시스템이 상속(inheritance)보다 복합(composite) 방법을 사용하는 방향으로 진화하면서 더 중요해짐
  • 중요한 이슈
    • 생성 패턴은 시스템 사용하는 Concrete Class 정보를 캡슐화
    • 생성 패턴은 class의 인스턴스 생성/결합 부분을 완전히 가림
    • [정리] 생성 패턴으로 누가, 어떻게, 언제 생성하고 무엇이 생성되는지에 대한 결정이 유연해짐
  • 종류
    • 싱글톤 패턴 (Singleton)
    • 팩토리 패턴 (Factory)
    • 추상 팩토리 패턴 (Abstract Factory)
    • 빌더 패턴 (Builder)
    • 프로토타입 패턴 (Prototype)

 

싱글톤 패턴 (Singleton Pattern)

  • 정의
    - 어떤 클래스의 인스턴스가 오직 하나임을 보장하며, 이 인스턴스의 접근을 제공하는 패턴
  • 특징
    • 프로그램의 런타임동안 인스턴스가 메모리에 단 하나 존재
    • 이 인스턴스를 어디에서나 접근 가능
  • 장점
    • 단 하나의 인스턴스만 갖는 것이 좋은 경우 사용
      - ex) Logging / 쓰레드 풀 / 윈도우 관리자
  • 접근 방법
    - class내에서 해당 인스턴스로 접근하는 방법을 자체 관리
  • 구현방법
    • Eager Initialization
    • Static Block Initialization
    • Lazy Initialization
    • Thread Safe Singleton
    • Bill Pugh Singleton Implementation
    • Enum SIngleton

 

팩토리 패턴 (Factory Pattern)

  • 정의
    - 여러 sub class를 가진 super class에서, input에 따라 sub class를 결정해 인스턴스를 생성해주는 방식
  • 특징
    - 인스턴스화에 대한 책임이 객체를 사용하는 클라이언트에서 팩토리 클래스로 옮겨짐
  • 사용시기
    • 어떤 클래스에서 생성해야하는 객체의 클래스 예측이 어려울 때
    • 생성할 객체를 기술하는 책임을 sub class로 넘길 때
  • 예제 코드
// https://readystory.tistory.com/117
public class ComputerFactory {
 
    public static Computer getComputer(String type, String ram, String hdd, String cpu){
        if("PC".equalsIgnoreCase(type))
            return new PC(ram, hdd, cpu);
        else if("Server".equalsIgnoreCase(type))
            return new Server(ram, hdd, cpu);
		
        return null;
    }
}


public class TestFactory {
 
    public static void main(String[] args) {
        Computer pc = ComputerFactory.getComputer("pc","2 GB","500 GB","2.4 GHz");
        Computer server = ComputerFactory.getComputer("server","16 GB","1 TB","2.9 GHz");
        System.out.println("Factory PC Config::"+pc);
        System.out.println("Factory Server Config::"+server);
    }
 
}
  • 장점
    • 클라이언트 코드에서 sub class의 인스턴스화 제거
      - 종속성을 낮춤 (Loose coupling)
      - 확장성
      - ex) 위 PC class에 대해 수정/삭제가 일어나도 클라이언트 코드 변경 불필요
    • 클라이언트와 구현 객체들 사이의 추상화 제공
  • 사용 예시
    • java.util의 Calendar, ResourceBundle, NumberFormat 등에서 getInstance() 메소드가 팩토리 패턴
    • Boolean, Integer, Long 등 Wrapper class에서 valueOf() 메소드가 팩토리 패턴

 

추상 팩토리 패턴 (Abstract Factory Pattern)

  • 정의
    - 팩토리 패턴처럼 input으로 sub class를 식별하지 않고, 또 하나의 팩토리 클래스를 입력받음
  • 특징
    - 팩토리 패턴에서 if-else 제거
  • 예제 코드
// https://readystory.tistory.com/119

// 추상 팩토리의 역할을 하는 인터페이스
public interface ComputerAbstractFactory {
 
	public Computer createComputer();
 
}

// sub class에서 추상 팩토리를 상속받아 Overriding
public class PCFactory implements ComputerAbstractFactory {
 
	private String ram;
	private String hdd;
	private String cpu;
	
	public PCFactory(String ram, String hdd, String cpu){
		this.ram=ram;
		this.hdd=hdd;
		this.cpu=cpu;
	}
	@Override
	public Computer createComputer() {
		return new PC(ram,hdd,cpu);
	}
 
}

// 클라이언트 코드의 sub class 생성을 제공하는 Consumer class
public class ComputerFactory {
 
	public static Computer getComputer(ComputerAbstractFactory factory){
		return factory.createComputer();
	}
}

// 클라이언트 코드
public class AbstractFactoryTest {
 
	public static void main(String[] args) {
		Computer pc = ComputerFactory.getComputer(new PCFactory("2 GB","500 GB","2.4 GHz"));
		Computer server = ComputerFactory.getComputer(new ServerFactory("16 GB","1 TB","2.9 GHz"));
		System.out.println("AbstractFactory PC Config::"+pc);
		System.out.println("AbstractFactory Server Config::"+server);
	}
    
}
  • 장점
    • interface를 위한 코드 접근법 제공
      - getComputer() 메소드는 인터페이스를 파라미터로 처리하므로 구현이 복잡하지 않음
    • sub class 확장이 쉬움
      - 위에서 Laptop 클래스를 추가한다고 하면 getComputer() 수정 없이 LaptopFactory만 작성
    • if-else, switch 등을 제거

 

빌더 패턴 (Builder Pattern)

  • 정의
    - 객체의 "생성"을 정의하는 클래스와 "표현"을 정의하는 클래스를 분리하여, 서로 표현이 달라도 생성이 가능
  • 특징
    - Optional한 속성이 많을수록 좋음
  • 비교
    • 팩토리 패턴과 추상 팩토리 패턴에서 생성에 필요한 파라미터가 많을 때
      - 클라이언트에서 타입, 순서 등 관리가 어려워 에러 확률이 높음
      - 필요없는 파라미터에 대해 null을 입력해야함
      - sub class가 무거워지고 복잡해지면 팩토리 클래스도 복잡해짐
    • 빌더 패턴는 이를 해결
  • 방법
    • 별도의 Builder 클래스를 생성
    • 필수값은 생성자로 입력
    • 선택적인 값은 메소드로 입력
    • 입력이 끝나면 build() 메소드로 최종 하나의 인스턴스를 반환
  • 구현 방법
    • Static Nested Class로 Builder 클래스 생성
      - Builder를 끝에 붙이는 것이 convention
    • 생성자는 public으로, 필수값을 파라미터로 받음
    • Optional값은 메소드로 제공하며, 반환값은 객체 자신
    • build() 메소드로 인스턴스를 반환하며, 생성 대상 클래스의 생성자는 private이어야함
  • 예제 코드
// https://readystory.tistory.com/121

public class Computer {
	
    //required parameters
    private String HDD;
    private String RAM;
	
    //optional parameters
    private boolean isGraphicsCardEnabled;
    private boolean isBluetoothEnabled;
	
 
    public String getHDD() {
        return HDD;
    }
 
    public String getRAM() {
        return RAM;
    }
 
    public boolean isGraphicsCardEnabled() {
        return isGraphicsCardEnabled;
    }
 
    public boolean isBluetoothEnabled() {
        return isBluetoothEnabled;
    }
	
    private Computer(ComputerBuilder builder) {
        this.HDD=builder.HDD;
        this.RAM=builder.RAM;
        this.isGraphicsCardEnabled=builder.isGraphicsCardEnabled;
        this.isBluetoothEnabled=builder.isBluetoothEnabled;
    }
	
    //Builder Class
    public static class ComputerBuilder{
 
        // required parameters
        private String HDD;
        private String RAM;
 
        // optional parameters
        private boolean isGraphicsCardEnabled;
        private boolean isBluetoothEnabled;
		
        public ComputerBuilder(String hdd, String ram){
            this.HDD=hdd;
            this.RAM=ram;
        }
 
        public ComputerBuilder setGraphicsCardEnabled(boolean isGraphicsCardEnabled) {
            this.isGraphicsCardEnabled = isGraphicsCardEnabled;
            return this;
        }
 
        public ComputerBuilder setBluetoothEnabled(boolean isBluetoothEnabled) {
            this.isBluetoothEnabled = isBluetoothEnabled;
            return this;
        }
		
        public Computer build(){
            return new Computer(this);
        }
 
    }
 
}


// 클라이언트 코드
public class TestBuilderPattern {
 
    public static void main(String[] args) {
        Computer comp = new Computer.ComputerBuilder("500 GB", "2 GB")
                .setBluetoothEnabled(true)
                .setGraphicsCardEnabled(true)
                .build();
    }
 
}

 

프로토타입 패턴 (Prototype Pattern)

  • 정의
    - Original 객체를 새로운 객체에 복사하여 필요에 따라 수정하는 메커니즘을 제공하는 패턴
  • 용도
    - 객체 생성 비용이 많이 들 때, 비슷한 객체가 있는 경우 clone으로 사용
  • 예시
    • DB에서 데이터를 가져오는 객체가 존재
    • DB에서 가져온 데이터를 여러 번 수정할 경우:
      - 매번 new 키워드를 통해 객체를 생성하고 (근데 clone도 new를 사용하지 않나?)
      - DB에서 데이터를 가져오는 것은 좋은 방법이 아님
    • 따라서, 한 번 DB에서 가져온 객체를 복사하여 데이터 수정 작업을 하는 것이 좋은 방법
    public class Employees implements Cloneable {
 
    private List<String> empList;
	
    public Employees(){
        empList = new ArrayList<String>();
    }
    
    @Override
    public Object clone() throws CloneNotSupportedException{
        List<String> temp = new ArrayList<String>();
        for(String s : this.empList){
            temp.add(s);
        }
        return new Employees(temp);
    }

 

 

 

https://readystory.tistory.com/116

 

[생성 패턴] 싱글톤(Singleton) 패턴을 구현하는 6가지 방법

첫 번째로 소개할 디자인 패턴은 싱글톤(Singleton) 패턴입니다. 종종 싱글톤 패턴을 '단일체' 패턴으로 번역하고 있는 책도 있지만, 일반적으로 싱글톤 패턴이라고 부릅니다. 싱글톤 패턴은 객체

readystory.tistory.com

https://readystory.tistory.com/117

 

[생성 패턴] 팩토리 패턴(Factory Pattern) 이해 및 예제

이번에 살펴볼 디자인 패턴은 가장 유명한 디자인 패턴 중 하나인 팩토리 패턴(Factory Pattern)입니다. 이 팩토리 패턴은 조금 더 구체적인 용어인 팩토리 메소드 패턴(Factory Method Pattern)으로도 널리

readystory.tistory.com

https://readystory.tistory.com/119

 

[생성 패턴] 추상 팩토리 패턴(Abstract Factory Pattern) 이해 및 예제

이번에 살펴볼 디자인 패턴은 생성 패턴 중 하나인 추상 팩토리 패턴(Abstract Factory Pattern)입니다. 추상 팩토리 패턴은 이전 포스팅에서 살펴본 팩토리 패턴(Factory Pattern)과 유사한 패턴으로, 팩토

readystory.tistory.com

https://readystory.tistory.com/121

 

[생성 패턴] 빌더 패턴(Builder pattern) 이해 및 예제

빌더 패턴은 복잡한 객체를 생성하는 방법을 정의하는 클래스와 표현하는 방법을 정의하는 클래스를 별도로 분리하여, 서로 다른 표현이라도 이를 생성할 수 있는 동일한 절차를 제공하는 패턴

readystory.tistory.com

https://readystory.tistory.com/122

 

[생성 패턴] 프로토타입 패턴(Prototype Pattern) 이해 및 예제

프로토타입은 주로 실제 제품을 만들기에 앞서 대략적인 샘플 정도의 의미로 사용되는 단어입니다. 프로토타입 패턴은 객체를 생성하는 데 비용(시간과 자원)이 많이 들고, 비슷한 객체가 이미

readystory.tistory.com