코딩 공부
디자인 패턴 - ① 싱글톤 패턴 본문
싱글톤 패턴(singleton pattern)은 하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴이다.
보통, 하나의 클래스를 기반으로 여러 개의 개별적인 인스턴스를 만들지만, 그렇게 하지 않고 하나의 클래스를 기반으로 오직 하나의 인스턴스를 만들어 이를 기반으로 로직을 만드는 데 쓰이며, 보통 데이터베이스 연결 모듈에 많이 사용한다.
메인 모듈에서 데이터베이스 연결에 관한 인스턴스를 정의하고 다른 모듈에서 해당 인스턴스를 기반으로 쿼리를 보내는 형식으로 주로 쓰인다.
하나의 인스턴스를 만들어놓고 해당 인스턴스를 다른 모듈들이 공유하며 사용하기 때문에, 인스턴스를 생성할 때 드는 비용이 줄어드는 장점이 있지만, 의존성이 높아진다는 단점도 존재한다.
* 인스턴스란?
객체 지향 프로그래밍에서 클래스의 구체적인 실체를 뜻한다. 클래스는 일종의 설계도이고, 이 설계도를 기반으로 실제 메모리 상에 할당된 것이 인스턴스이다. 즉, 인스턴스는 클래스의 실체화된 객체를 의미한다.
여러 객체가 동일한 클래스를 기반으로 생성될 수 있으며, 각 객체는 독립적인 메모리 공간을 가지고 클래스에서 정의한 멤버 변수와 메서드를 공유한다. 즉, 클래스는 객체를 생성하기 위한 템플릿 역할을 하고, 인스턴스는 클래스를 기반으로 실제 프로그램에서 사용되는 데이터를 나타낸다.
// 클래스 정의
public class Car{
String color;
int speed;
// 생성자
public Car(String color, int speed){
this.color = color;
this.speed = speed;
}
// 메서드
public void accelerate(){
speed += 10;
System.out.println("Accelerating. Current speed: " + speed);
}
}
// 인스턴스 생성 및 사용
Car myCar = new Car("Blue", 0);
myCar.accelerate();
위의 코드에서 'Car' 클래스는 자동차를 나타내는 설계도이고, 'myCar'은 이 클래스를 기반으로 생성된 실제 자동차 객체(인스턴스)이다. 인스턴스는 'accelerate'메서드를 호출해 동작을 수행할 수 있다.
이는 애플리케이션 전체에서 해당 클래스의 단일 객체를 사용하고자 할 때 유용하다.
싱글톤 패턴을 구현하면, 여러 부분에서 동일한 객체에 접근할 수 있으므로 자원의 공유와 효율성을 증가시킬 수 있다.
<싱글톤 패턴의 특징>
1. 유일한 인스턴스 : 클래스의 인스턴스는 오직 하나만 존재한다.
2. 전역 접근점 : 어디서든지 인스턴스에 접근할 수 있는 전역적인 접근점을 제공한다.
<싱글톤 패턴 접근 방식>
1. 정적 멤버 변수를 이용한 구현
public class Singleton{
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if (instance==null){
instance = new Singleton();
}
return instance;
}
}
2. 미리 생성된 인스턴스를 반환하는 방식
public class Singleton{
private static final Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}
위의 두가지 예제에서도 주의할 점은 생성자가 'private'으로 선언되어 외부에서 객체를 직접 생성하는 것을 막고, 유일한 인스턴스를 반환하는 'getInstance' 메서드를 통해 인스턴스에 접근한다는 것이다.
싱글톤 패턴은 필요한 경우에만 사용해야 하며, 과도하게 사용하면 전역 상태를 만들어 유지보수에 어려움을 줄 수 있다.
<싱글톤 패턴의 단점>
싱글톤 패턴은 TDD(Test Driven Development)를 할 때 어려움을 주곤 한다.
TDD를 할 때 단위 테스트를 많이 시행하는데, 단위 테스트는 테스트가 서로 독립적이어야 하며 테스트를 어떤 순서로든 실행할 수 있어야 하는데,
싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이어서 각 테스트마다 독립적인 인스턴스를 만들기가 어렵다.
* TDD란?
TDD는 "Test-Driven Development"의 약자로, 테스트 주도 개발을 의미한다. 이는 소프트웨어를 개발할 때, 테스트 케이스를 먼저 작성하고, 그다음에 해당 테스트 케이스를 통과시키는 코드를 작성하는 개발 방법론이다.
TDD는 주로 다음과 같은 3단계로 진행된다.
1. 테스트 작성(Red)
: 신규 기능이나 변경된 기능에 대한 테스트 케이스를 먼저 작성한다. 이때 해당 기능은 아직 구현되지 않았으므로 테스트는 실패한다.
2. 코드 작성(Green)
: 테스트를 통과시키는 최소한의 코드를 작성한다. 목표는 테스트를 통과시키는 것뿐이며, 이 과정에서 과도한 설계나 기능 추가는 지양한다.
3. 리팩토링(Refactor)
: 테스트를 통과시킨 코드를 리팩토링하여 가독성을 높이고, 중복된 코드를 제거하며, 구조를 개선한다. 리팩토링 과정에서도 테스트 케이스가 계속 통과되는지 확인한다.
<의존성 주입>
싱글톤 패턴은 사용하기 쉽고 실용적이지만, 모듈 간의 결합을 강하게 만들 수 있다는 단점이 있다.
그래서, 의존성 주입(DI, Dependency Injection)을 통해 모듈 간의 결합을 좀 느슨하게 만들 수 있다.
의존성을 종속성이라고도 하며, A가 B에 의존성이 있다는 말은 B가 변하게 되면 A 또한 변해야 된다는 것을 의미한다.
메인 모듈이 직접 다른 하위 모듈에 대한 의존성을 주기보다는 중간에 의존성 주입자(dependency injector)가 이 부분을 가로채 메인 모듈이 간접적으로 의존성을 주입하는 방식이다.
따라서, 메인 모듈은 하위 모듈에 대한 의존성이 떨어지게 되는 결과를 가져오고, 이를 '디커플링'된다고도 말한다.
의존성 주입의 장점
1. 모듈 교체 및 확장 용이성
: 의존성 주입을 통해 모듈 간 결합도가 낮아져, 특정 모듈을 다른 구현으로 쉽게 교체가 가능하다. 이를 이용해, 테스트를 위한 가짜(mock) 객체 또는 실제 구현을 주입하는데 특히 유용하며, 애플리케이션의 특정 부분을 확장하거나 수정할 때도 유연성을 제공한다.
2. 테스트 용이성
: 의존성 주입을 통해 모의(mock) 객체나 다른 테스트 대상을 주입할 수 있다. 즉, 테스트에서 특정 모듈에 의존하는 부분을 쉽게 격리시킬 수 있다. 의존성 주입을 사용하면 테스트 환경에서만 사용되는 객체를 주입해 특정 부분을 격리하고 테스트가 가능하다.
3. 추상화 레이어 도입
: 의존성 주입은 종종 인터페이스 또는 추상 클래스를 이용해 추상화 레이어를 도입하는데 활용한다. 이를 통해 실제 구현이 아니라 인터페이스나 추상 클래스에 의존하게 되므로, 애플리케이션의 의존성 방향이 일관되고, 구현체를 쉽게 교체할 수 있다.
4. 애플리케이션 의존성 방향의 명확성
: 의존성 주입을 사용하면 클래스나 모듈 간의 의존성이 명시적으로 표현되어 코드를 읽고 이해하기 쉬워진다. 클래스가 직접 객체를 생성하는 대신, 외부에서 주입받으면서 의존성이 어디서 왔는지 명확하게 파악할 수 있다.
5. 모듈 간의 관계 명확성 : 의존성 주입은 모듈 간의 관계를 명확하게 정의하고 구조화하는데 도움이 된다. 의존성이 주입되는 방식에 따라 각 모듈이 어떤 역할을 수행하고, 어떤 인터페이스를 통해 소통하는지 파악하기 쉬워진다.
의존성 주입 원칙
의존성 역전 원칙(Dependency Inversion Principle, DIP)
: 상위 모듈은 하위 모듈에 의존해서는 안되며, 양쪽 모두 추상화에 의존해야 한다.
즉, 추상화 레이어를 도입해 고수준 모듈이 하위 수준 모듈에 직접 의존하지 않고, 추상화를 통해 상호작용 해야 한다.
이때, 추상화는 세부 사항에 의존하지 말아야 한다.
잘못된 설명이 있을 경우, 지적해주시면 감사하겠습니다.
'CS 공부' 카테고리의 다른 글
정보처리기사 실기 C 공부 ② (0) | 2024.04.22 |
---|---|
정보처리기사 실기 C 공부 ① (2) | 2024.04.19 |
정보처리기사 실기 공부 ① (0) | 2024.04.17 |
디자인 패턴 (0) | 2024.01.17 |
라이브러리(Library) vs. 프레임워크(Framework) (0) | 2024.01.16 |