💡 객체의 생성을 캡슐화하는 패턴
객체를 사용하는 코드에서 객체 생성 부분을 떼어네 추상화한 패턴이자 상속 관계에 있는 두 클래스에서 상위 클래스가 중요한 뼈대를 결정하고, 하위 클래스에서 객체 생성에 대한 구체적인 내용을 결정하는 패턴이다.
상위 클래스와 하위 클래스가 분리되기 때문에 느슨한 결합을 가지며 상위 클래스에서는 인스턴스 생성 방식에 대해 전혀 알 필요가 없기 때문에 더 많은 유연성을 가지게 된다.
객체 생성 로직이 따로 떨어져 있기 때문에 코드를 리팩토링 하는 경우에도 한 곳만 고칠 수 있기 떄문에 유지 보수성이 증가한다.
팩토리 패턴은 객체 생성에 대한 로직은 상위 클래스에서 작성하고, 하위 클래스에서 객체 생성에 대한 구체적인 내용을 결정한다.
프록시는 대리인이라는 뜻으로, 무엇인가를 대신 처리하는 의미이다. 실제 객체 앞단에 위치하여 해당 객체의 행위(Method)를 대리하는 역할을 한다. 프록시 객체를 통해 대상 객체에 접근하기 때문에 실제 객체가 메모리에 존재하지 않아도 기본적인 정보를 참조하거나 설정할 수 있고, 또한 실제 객체의 기능이 반드시 필요한 시점까지 객체의 생성을 미룰 수 있다.
💡 대표적인 프록시 3가지
💡 프록시 패턴 장점
💡 프록시 패턴 단점
💡 프록시 패턴 예제
public interface Image {
void displayImage();
}
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
}
public class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void displayImage() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.displayImage();
}
}
public class ProxyMain {
public static void main(String[] args) {
Image image1 = new ProxyImage("test1.jpg");
Image image2 = new ProxyImage("test2.jpg");
image1.displayImage();
image2.displayImage();
}
}
전역변수를 사용하지 않고 객체를 하나만 생성한다.
생성된 객체를 어디에서든지 참조할 수 있다.
생성자가 여러 차례 호출되어도 하나의 객체만 존재한다.
최초 생성 이후에 호출된 생성자는 최초 생성자가 생성한 객체를 리턴한다.
환경설정 관리 클래스나 커넥션 풀과 같이 pool 형태로 관리되는 공통 클래스에서 사용되는 것이 일반적이다.
💡 싱글톤 패턴의 특징
고정된 메모리 영역을 얻으면서 동시에 한번만 new
를 사용하여 메모리 낭비를 방지할 수 있다.
싱글톤으로 만들어진 인스턴스는 전역으로 사용되며, 다른 클래스의 인스턴스들이 데이터를 공유하고 변경할 수 있다.
많은 일을 위임하거나 공유하는 경우 Coupling이 많아지고 결합도가 높아진다.
Java에서의 싱글톤 패턴
public class Singleton {
private static class singleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static synchronized Singleton getInstance() {
return singleInstanceHolder.INSTANCE;
}
}
public class HelloWorld {
public static void main(String[] args) {
Singleton a = Singleton.getInstance();
Singleton b = Singleton.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
if (a == b) {
System.out.println(true);
}
}
}
💡 싱글톤 패턴의 단점
💡 TDD를 할 때 단위 테스트를 주로 하는데, 단위 테스트는 테스트가 서로 독립적이어야 하며 테스트를 어떤 순서로든 실행할 수 있어야 한다. 하지만 싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이므로 각 테스트마다 독립적인 인스턴스를 만들기가 어렵다.
💡 의존성 주입 (DI, Dendency Injection)을 통해 모듈 간 결합을 조금 더 느슨하게 만들어 해결할 수 있다.
💡 싱글톤 패턴에 주의할 점
상태를 가진 객체를 Singleton으로 만들면 안된다. 앱 내의 단 한개의 인스턴스가 존재하고, 이를 전역에서 접근할 수 있다면 각기 다른 스레드에서 객체의 상태를 마구잡이로 변경시킬 여지가 있다. 상태가 공유되는 것은 위험하기 때문에 무상태 객체 혹은 설계상 유일해야하는 시스템 컴포넌트를 Singleton으로 만들어야 한다.
잘 읽었습니다. 좋은 정보 감사드립니다.