1. 종류
1.1. Singleton
- 인스턴스를 하나만 만들어 관리하는 패턴
- 주로 DB 커넥션을 관리할 때 싱글톤 패턴을 사용한다, 커넥션 인스턴스를 하나만 생성하고 관리하여 생성 비용을 아끼는 것
- Node.js에서 MongoDB, MySQL 연동할 때 쓰는 라이브러리들도 실제로 싱글톤 패턴으로 구현되어 있음
- TDD 기법에선 단점이 생기는데, 주로 단위 테스트는 독립적으로 실행을 해야 하는데 싱글톤 패턴은 미리 생성된 하나의 인스턴스로 구현되다 보니, 각 테스트마다 독립적으로 인스턴스를 만들기 어려운 점이 있다
- 사용하기 쉽고 실용적이지만, 모듈 간의 결합을 강하게 만드는 단점도 있는데, 이는 DI를 통해 의존성을 주입받는걸로 추상화를 시키면 결합을 조금 느슨하게 만들어줄 순 있다
class SingleTon {
static final SingleTon instance = new SingleTon();
private SingleTon() {
// 초기화
}
public SingleTon getInstance() {
return instance;
}
}
public class TestCode {
public static void main(String[] args) {
SingleTon a = SingleTon.getInstance();
SingleTon b = SingleTon.getInstance();
System.out.println(a == b); // true
}
}
- Spring에서의 싱글톤 패턴은 관리 면에서 다른 부분이 있다
- 일반 Java의 싱글톤은 JVM에서 제어를 할 수 있으나, Spring에선 Spring Context에 의해 제어가 된다
- 이는 인스턴스 생성이 Spring Context에 의해 만들어지기 때문에 관리를 할 수 있으며, Thread Safety를 보장한다고 한다, 반면 일반 Java 싱글톤은 보장하진 못한다
1.2. Builder
- 인스턴스를 직접 생성하지 않고, 내부 클래스를 통해 간접적으로 생성하게 하는 패턴
class Something {
private Something(String name, int size) {
// 초기화
}
public static class Builder {
String name = null;
int size = 0;
public Builder() {
// 초기화
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setSize(int size) {
this.size = size;
return this;
}
public Something build() {
return new Something(name, size);
}
}
}
// 예시 소스
public void createSomething() {
Something something = new Something.Builder().setName("hj").setSize(1).build();
}
- 빌더 패턴은 보통 클래스와 사용 대상 객체의 결합도를 낮추기 위해 많이 쓴다
- 예로 들어 Something 안에 name, size말고도 다른 value 인수를 추가해야 한다고 생각해보자
- 보통은 Something 안에 value 넣고 사용하는 모든 곳을 찾아 추가를 해야 하는데..
- 빌더를 쓰면 내부 Builder 안에서만 추가해주면 된다
1.3. Factory
- 객체를 만들어 제공해주는 공장 형태의 생성 패턴
- 상위 클래스가 뼈대를 결정하고, 하위 클래스가 구체화하는 패턴으로, 서로 분리가 되어 있어서 결합도 느슨하고 유연성도 크게 가질 수 있다
abstract class Unit {
public abstract String getVoice();
}
class UnitFatory {
public static Unit getUnit(String unitName) {
if ("marine".equalsIgnoreCase(unitName)) return new Marine();
else if ("zealot".equalsIgnoreCase(unitName)) return new Zealot();
else return new Zergling();
}
}
class Marine extends Unit {
public Marine() {}
public String getVoice() { return "You wanna piece of me, boy?" }
}
class Zealot extends Unit {
public Zealot() {}
public String getVoice() { return "My life for Aiur!" }
}
class Zergling extends Unit {
public Zergling() {}
public String getVoice() { return "!@#$$%^&*&^#$%*" }
}
public class TestCode {
public static void main(String[] args) {
Unit unit1 = UnitFatory.getUnit("Marine");
Unit unit2 = UnitFatory.getUnit("zzealot");
System.out.println(unit1.getVoice()); // You wanna piece of me, boy?
System.out.println(unit2.getVoice()); // !@#$$%^&*&^#$%*
}
}