2025. 1. 22. 17:08ㆍ개발/소프트웨어 디자인 패턴
🌟 들어가기 전
요즘 메모리 최적화 이슈에 대해 관심이 많다.
그렇게 메모리에 대해서 보다가 유독 눈에 띄는 게 있었다.
바로 Kotlin에서 Object 객체 !!
늘 그냥 쓰라니까 써왔던.
익숙하지만 낯선.
그 존재에 대해 파헤쳐봤다.
🔍 세부 내용
1. 싱글톤 패턴(Singleton Pattern)
싱글톤 패턴은 객체의 전역적 유일성을 보장하는 디자인 패턴이다.
이를 통해 하나의 인스턴스만 사용하며, 여러 곳에서 동일한 객체를 공유하거나 관리할 수 있다.
- 전역상태 관리: 애플리케이션에서 하나의 상태를 유지하고 여러 곳에서 공유할 때 사용 (ex. 로그인 세션, 앱 설정, DB 연결)
- 자원 절약: 객체가 여러번 생성되지 않도록 보장하여 메모리 사용 절감
- 일관된 상태 유지: 하나의 인스턴스를 통해 상태를 일관되게 관리
object DatabaseManager {
private val connection = "Database Connection"
fun connect() {
println("Connecting to the database: $connection")
}
fun disconnect() {
println("Disconnecting from the database.")
}
}
fun main() {
// DatabaseManager 객체는 애플리케이션 전역에서 하나만 존재
DatabaseManager.connect() // Connecting to the database: Database Connection
DatabaseManager.disconnect() // Disconnecting from the database.
}
2. object와 companion object의 차이점
Kotlin에서는 싱글톤 패턴을 구현할 때 object 객체를 사용한다.
- 단일 인스턴스 보장: object로 선언된 객체는 전역적으로 유일한 인스턴스를 생성
- 자동 초기화: object는 첫 번째 사용 시점에 초기화 (lazy initialization)
- 상속 불가: object는 상속할 수 없고, 이를 구현하려면 class와 interface를 활용해야 함
- 동반 객체(Companion Object): 클래스 내에 object를 선언하여 해당 클래스의 인스턴스 없이 접근할 수 있는 정적 멤버
그렇다면 object와 companion object는 어떤 차이가 있을까?
class MyClass {
/* 클래스 안에 속한 단 하나의 instance */
companion object {
const val CONSTANT = "This is a constant"
fun staticMethod() {
println("This is a static method")
}
}
}
fun main() {
println(MyClass.CONSTANT) // 접근: MyClass.CONSTANT
MyClass.staticMethod() // 접근: MyClass.staticMethod()
}
companion object는 클래스 내부에 선언되며, 해당 클래스의 정적 멤버를 구현할 때 사용된다.
반면 object는 클래스 밖에서 독립적인 싱글톤 객체를 정의할 때 사용한다.
따라서 object와 companion은 사용성에 따라 나뉘게 된다.
2-1) companion object를 사용하는 이유
- 클래스와 관련된 정적 멤버를 정의하기 위해
2-2) object를 사용하는 이유
- 독립적인 싱글톤 객체를 만들고 싶을 때 (특정 클래스와 관련 X)
- 상속/구현이 필요 없는 전역 유틸리티를 만들 때
2-3) 정리
특징 | companion object | object |
클래스와의 관계 | 특정 클래스와 강하게 연결됨 | 독립적이며 특정 클래스와 관계없음 |
사용 목적 | 클래스와 관련된 정적 멤버 정의 | 싱글톤 객체 생성, 전역 유틸리티 또는 독립적인 데이터 관리 |
접근 방식 | ClassName.member처럼 클래스 이름으로 접근 | ObjectName.member처럼 객체 이름으로 접근 |
인스턴스 멤버 접근 | 클래스의 멤버와 정적 멤버를 함께 사용할 수 있음 | 독립적이며 클래스 인스턴스와는 별개로 동작 |
결국 companion object를 선택할지는 “이 정적 멤버가 해당 클래스와 강하게 연관된 기능인가?”에 따라 결정하면 된다.
3. Java-static과 Kotlin-companion object의 차이점
Java에서는 static으로 정적 변수와 메서드를 구현할 수 있다.
그럼 companion object는 static과 같은 기능인걸까?
두 경우 모두 클래스에 속하는 메서드와 속성을 인스턴스 생성 없이 사용할 수 있다.
하지만 완전히 동일하지는 않다.
class Parent {
static void greet() {
System.out.println("Hello from Parent");
}
}
class Child extends Parent {
// greet()는 오버라이드 불가
static void greet() {
System.out.println("Hello from Child");
}
}
public class Main {
public static void main(String[] args) {
Parent.greet(); // "Hello from Parent"
Child.greet(); // "Hello from Child"
}
}
- 정적 메서드 및 변수는 클래스 자체에 속하며, 클래스 로드 시 메모리에 올라간다.
- static은 상속은 가능하지만 override는 불가능하다.
open class Parent {
companion object {
open fun greet() {
println("Hello from Parent")
}
}
}
class Child : Parent() {
companion object {
override fun greet() {
println("Hello from Child")
}
}
}
fun main() {
Parent.greet() // "Hello from Parent"
Child.greet() // "Hello from Child"
}
- companion object는 객체이며, 클래스와 연결된 싱글톤 객체이다.
- 클래스 이름으로 접근 가능하지만, 실제로는 클래스와 연결된 객체(companion object)의 메서드/속성을 호출하는 것이다.
- 상속이 가능하며, override도 가능하다.
여기서 잠깐!
위에서 잠깐 언급했지만 object 객체는 상속과 override 모두 안됨
object Parent {
open fun display() { // 오류! 'open' 키워드 사용 불가
println("Parent object")
}
}
// 불가능: object는 상속할 수 없음
object Child : Parent() // 오류!
🤔 배운 점 & 느낀 점
언어의 세계는 무한히 넓고도 깊구나.
그동안 얼마나 대충 사용해왔는지 느끼는 시간이었다.
요즘 메모리 최적화에 관심이 많은데 그러면서 싱글톤 객체까지 넘어오게 됐다.
대충 들어만 봤고 그냥 그런가보다 하고 사용해왔던건데 이렇게 자세히 들여다본적은 없다는 걸 느꼈다.
하나를 쓰더라도 알고 쓰자.
'개발 > 소프트웨어 디자인 패턴' 카테고리의 다른 글
UseCase 사용의 이유, ViewModel에서 Repository를 직접 연결하면 안될까? (0) | 2025.02.21 |
---|---|
비즈니스 로직이란? 개발자가 꼭 알아야 할 핵심 개념 정리 (0) | 2025.02.20 |
설계 패턴에서 Interface와 implementation을 쓰는 이유 (0) | 2025.02.18 |
보일러 플레이트 코드 (Boilerplate Code) (0) | 2025.01.14 |
MVVM 패턴 (0) | 2023.02.24 |