생성 패턴
- 정의
- 인스턴스를 만드는 절차를 추상화하는 패턴 - 역할
- 객체 생성/합성 방법 또는 객체 표현 방법을 시스템과 분리 - 중요한 이유
- 시스템이 상속(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에 대해 수정/삭제가 일어나도 클라이언트 코드 변경 불필요 - 클라이언트와 구현 객체들 사이의 추상화 제공
- 클라이언트 코드에서 sub 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 등을 제거
- interface를 위한 코드 접근법 제공
빌더 패턴 (Builder Pattern)
- 정의
- 객체의 "생성"을 정의하는 클래스와 "표현"을 정의하는 클래스를 분리하여, 서로 표현이 달라도 생성이 가능 - 특징
- Optional한 속성이 많을수록 좋음 - 비교
- 팩토리 패턴과 추상 팩토리 패턴에서 생성에 필요한 파라미터가 많을 때
- 클라이언트에서 타입, 순서 등 관리가 어려워 에러 확률이 높음
- 필요없는 파라미터에 대해 null을 입력해야함
- sub class가 무거워지고 복잡해지면 팩토리 클래스도 복잡해짐 - 빌더 패턴는 이를 해결
- 팩토리 패턴과 추상 팩토리 패턴에서 생성에 필요한 파라미터가 많을 때
- 방법
- 별도의 Builder 클래스를 생성
- 필수값은 생성자로 입력
- 선택적인 값은 메소드로 입력
- 입력이 끝나면 build() 메소드로 최종 하나의 인스턴스를 반환
- 구현 방법
- Static Nested Class로 Builder 클래스 생성
- Builder를 끝에 붙이는 것이 convention - 생성자는 public으로, 필수값을 파라미터로 받음
- Optional값은 메소드로 제공하며, 반환값은 객체 자신
- build() 메소드로 인스턴스를 반환하며, 생성 대상 클래스의 생성자는 private이어야함
- Static Nested Class로 Builder 클래스 생성
- 예제 코드
// 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
https://readystory.tistory.com/117
https://readystory.tistory.com/119
https://readystory.tistory.com/121
https://readystory.tistory.com/122
'CS > 개발론' 카테고리의 다른 글
[개발론] CRC 카드 (Class-Responsibility-Collaboration card) (0) | 2022.06.26 |
---|---|
[개발론] Responsibility-Driven Design (0) | 2022.06.26 |
[개발론][테스트] Mock (0) | 2022.05.10 |