Design Pattern에는 여러 방법들이 존재한다. 먼저 Singleton Pattern에 대해 알아보자.
실제로 생성되는 객체는 하나
이다.공통된 객체를 여러개 생성해 사용
하는 DBCP(Database Connection Pool)과 같은 상황에서 많이 사용
된다.즉, 하나의 객체만을 생성
해 이후에 호출된 곳에서는 생성된 객체를 반환하여 프로그램 전반에서 하나의 인스턴스만을 사용
하게 하는 패턴이다.
👉 PC 자원은 한정되어 있기 때문에 인스턴스가 남용되는 건 바람직하지 않다. 하나의 자원으로 모두가 공유해 사용해야 하는 경우 이 패턴은 유용한 방법이 될 수 있다.
public class Printer {
private static Printer printer = null;
private Printer(){}
public static Printer getInstance() {
if(printer == null) {
printer = new Printer();
}
return printer;
}
public void print(String input) {
System.out.println(input);
}
}
하나의 인스턴스를 유지
한다.👉 static 공유 자원 !!
싱글톤 패턴을 멀티 쓰레드 환경에서 사용하는 경우 문제점이 존재한다.
public static Printer getInstance() {
if(printer == null) {
printer = new Printer();
}
return printer;
}
하나의 인스턴스가 아닌 여러개의 인스턴스가 생성
되는 위험이 발생할 수 있다.public class Printer {
private static Printer printer = null;
private int count = 0;
private Printer(){}
public static Printer getInstance() {
if(printer == null) {
printer = new Printer();
}
return printer;
}
public void print(String input) {
count++;
System.out.println(input + "count : "+ count);
}
}
멀티 쓰레드 환경에서 문제점을 해결할 수 있는 방법은 다음과 같다.
public class Printer {
private static Printer printer = new Printer();
private static int count = 0;
private Printer(){}
public static Printer getInstance() {
return printer;
}
public synchronized static void print(String input) {
count++;
System.out.println(input + "count : "+ count);
}
}
static
키워드로 인해 기존에 조건문에서 체크하던 부분이 제거된다.synchronized
키워드로 여러 쓰레드에서 동시 접근하는 것을 막아 이를 해결할 수 있다.public interface Printer {
public void print(String input);
}
--------------------------------------------
public class RealPrinter implements Printer {
private static Printer printer = null;
private RealPrinter() {
}
public synchronized static Printer getInstance() {
if (printer == null)
printer = new RealPrinter();
return printer;
}
@Override
public void print(String input) {
System.out.println(input);
}
}
--------------------------------------------
public class UsePrinter {
public void doSomething(Printer printer) {
printer.print("fakeGet");
}
}
--------------------------------------------
public class FakePrinter implements Printer {
private String str;
public void print(String str) {
this.str = str;
}
public String get() {
return str;
}
}
--------------------------------------------
class UsePrinterTest {
@Test
void testdoSomething() {
FakePrinter fake = new FakePrinter();
UsePrinter use = new UsePrinter();
use.doSomething(fake);
assertThat("fakeGet").isEqualTo(fake.get());
}
}
interface
를 사용하는 것이 좋으나, static
타입의 method를 만든다면 위와 같은 테스트는 할 수 없다
.👉 interface는 static method를 가질 수 없기 때문이다.
싱글톤 패턴을 적절하게 활용하면 좋지만, 남용될 여지가 많다.
static
키워드를 통해 관리해야 한다.static
키워드를 사용하면 클래스 로딩 단계에서 바로 초기화 된다.synchronized
키워드를 통해 관리해야 한다.interface
형태로 관리하면 좋다.