자바에서 싱글톤(Singleton)은 디자인 패턴 중 하나로, 해당 클래스의 인스턴스가 오직 하나만 생성되도록 하는 패턴이다. (객체의 인스턴스가 오직 1개만 생성되는 패턴을 의미) 이를 통해 어플리케이션 전체에서 하나의 객체만을 공유하여 사용할 수 있다.
- 싱글톤 패턴을 구현하기
가. 생성자의 접근 제어: 클래스의 생성자를 private으로 선언하여 외부에서 인스턴스를 직접적으로 생성할 수 없도록 한다.
나. 정적 메서드로 인스턴스 제공: 클래스 내부에서 유일한 인스턴스를 얻을 수 있는 정적(static) 메서드를 제공. 이 메서드를 통해 항상 동일한 인스턴스를 반환.
다. 인스턴스 유지: 내부적으로 유일한 인스턴스를 유지하고, 필요할 때 이를 반환하는 방식으로 구현한ㄴ다.
- 간단한 예제를 통해 싱글톤 패턴 보기
public class Singleton {
// 정적 변수로 유일한 인스턴스를 저장합니다.
private static Singleton instance;
// private 생성자로 외부에서 인스턴스를 생성하는 것을 방지합니다.
private Singleton() {
}
// 정적 메서드로 유일한 인스턴스를 반환합니다.
public static Singleton getInstance() {
// 인스턴스가 없는 경우에만 생성합니다.
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
이렇게 하면 Singleton 클래스의 인스턴스는 getInstance() 메서드를 통해서만 얻을 수 있다. 이 메서드는 항상 동일한 인스턴스를 반환하므로, 여러 곳에서 호출되더라도 항상 동일한 인스턴스가 반환.
- 실전 예시
public class Logger {
// 유일한 인스턴스를 저장할 정적 변수
private static Logger instance;
// 외부에서 생성자에 접근하지 못하도록 private으로 선언
private Logger() {
}
// 인스턴스를 제공하는 정적 메서드
public static Logger getInstance() {
// 인스턴스가 생성되어 있지 않다면 생성
if (instance == null) {
instance = new Logger();
}
return instance;
}
// 로그 메시지 출력 메서드
public void log(String message) {
System.out.println("[LOG] " + message);
}
}
public class Main {
public static void main(String[] args) {
// Logger의 인스턴스를 가져옵니다.
Logger logger = Logger.getInstance();
// 로그 메시지를 출력합니다.
logger.log("Application started.");
logger.log("Processing data...");
logger.log("Data processed successfully.");
}
}
이 예제에서는 Logger 클래스가 싱글톤으로 구현되어 있으며, getInstance() 메서드를 통해 항상 동일한 인스턴스를 반환할 수 있다. Main 클래스에서는 이 인스턴스를 사용하여 로그 메시지를 출력.
이러한 방식으로 싱글톤 패턴을 사용하면 어플리케이션 전체에서 로그를 관리하는데 유용한 로깅 시스템을 구현할 수 있다.
- 싱글톤 패턴의 장점
가. 리소스 절약: 싱글톤 패턴을 사용하면 한 번 생성된 인스턴스를 계속해서 재사용할 수 있으므로, 메모리와 시스템 리소스를 절약할 수 있다.
나. 전역적인 접근성: 어플리케이션 전역에서 해당 객체에 접근할 수 있기 때문에, 어디서든지 손쉽게 해당 객체를 사용할 수 있다. 이는 객체의 공유성을 활용하여 데이터나 상태를 유지하거나 전역적인 설정을 처리할 때 유용.
다. 인스턴스 제어: 싱글톤 패턴을 사용하면 해당 클래스의 인스턴스가 항상 하나만 존재하도록 보장할 수 있다. 이는 객체의 중복 생성을 방지하고, 의도치 않은 인스턴스의 생성을 막아 프로그램의 안정성을 높일 수 있다.
라. 상태 유지: 싱글톤 패턴을 사용하면 객체의 상태를 유지할 수 있다. 이는 애플리케이션의 런타임 동안 일관된 상태를 유지하거나, 설정 정보나 캐시 등을 유지하는 데에 유용.
마. 확장성: 싱글톤 패턴을 구현하면 필요에 따라 해당 클래스의 동작을 수정하고 확장하기가 쉬워진다. 다른 클래스에서 싱글톤 인스턴스에 대한 참조를 쉽게 얻을 수 있으므로, 확장성이 높은 구조를 유지할 수 있다.
- 싱글톤 패턴의 단점
가. 전역 상태의 공유: 싱글톤 패턴은 전역적인 상태를 공유하므로, 객체 간의 결합도가 높아질 수 있다. 이는 코드를 이해하기 어렵게 만들고, 유지 보수를 어렵게 할 수 있다.
나. 테스트 어려움: 싱글톤 객체는 전역적으로 접근 가능하기 때문에, 테스트를 어렵게 만들 수 있다. 특히 다른 클래스에서 의존하는 경우, 이를 모의(mock) 객체로 대체하여 테스트하는 것이 어려울 수 있다.
다. 생명주기 관리: 싱글톤 객체의 생명주기를 관리하기가 어려울 수 있다. 특히 복잡한 의존성이 있는 경우, 객체의 생성, 초기화, 파괴 등을 올바르게 처리하기 위해 추가적인 작업이 필요할 수 있다.
라. 멀티스레드 문제: 기본적인 싱글톤 구현은 멀티스레드 환경에서 안전하지 않을 수 있다. 동시에 여러 스레드에서 getInstance() 메서드를 호출할 경우, 여러 개의 인스턴스가 생성될 수 있거나 예기치 않은 동작이 발생할 수 있다.
마. 테스트용 확장성 제한: 싱글톤 패턴은 특정 클래스에 대한 하나의 인스턴스만을 허용하므로, 테스트 목적으로 해당 클래스를 확장하여 다른 동작을 수행하는 것이 어려울 수 있다.
클래스는 객체(또는 인스턴스)를 만들기 위한 설계도 혹은 틀. 클래스는 데이터(속성, 멤버 변수)와 해당 데이터를 다루는 메서드(함수)를 포함.
예를 들어, '사람'이라는 클래스에는 이름, 나이, 성별 등의 속성이 있을 수 있으며, 이에 대한 행동을 나타내는 메서드도 포함될 수 있습니다.
클래스의 실제 객체를 인스턴스라고 한다. 클래스를 기반으로 생성된 개별 객체를 인스턴스라고 하며, 각각의 인스턴스는 클래스에서 정의된 속성과 메서드를 가집니다.
예를 들어, '사람' 클래스에서 생성된 '철수', '영희' 등의 개별 사람 객체가 인스턴스입니다. '철수' 인스턴스는 사람 클래스의 속성과 메서드를 사용할 수 있다.
간단한 예제로 설명하면:
// Person 클래스 정의
public class Person {
// 속성(멤버 변수)
String name;
int age;
// 메서드
public void greet() {
System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
}
}
// Main 클래스
public class Main {
public static void main(String[] args) {
// Person 클래스의 인스턴스 생성
Person person1 = new Person();
Person person2 = new Person();
// 속성 값 설정
person1.name = "John";
person1.age = 30;
person2.name = "Emily";
person2.age = 25;
// 메서드 호출
person1.greet(); // 출력: Hello, my name is John and I am 30 years old.
person2.greet(); // 출력: Hello, my name is Emily and I am 25 years old.
}
}
위 예제에서 Person 클래스는 속성(name, age)과 메서드(greet())를 정의하고 있다. 이 클래스로부터 생성된 person1과 person2는 각각의 인스턴스이며, 클래스에서 정의된 속성과 메서드를 사용할 수 있다.