Search
📁

⭐ 객체의 불변성은 왜 유지해야할까?

전에 객체 주입에 관련해서
생성자의 주입
setter 주입
필드 주입
이렇게 있다고 했는데 생성자를 통한 주입으로 진행하면 객체가 불변성을 가지고
setter 주입은 이미 객체를 생성하고 넘겨주기 때문에 객체의 불변성이 없다는 말이 있었다.
그럼 객체의 불변성이 대체 뭘까? 그리고 객체의 불변성을 왜 유지해야 좋을까?

객체의 불변성이란,

객체가 최초 생성된 시점 이후 해당 객체의 상태를 변경할 수 없는, 값이 변하지 않는다는 뜻

객체의 불변성의 중요한 이유

근데 왜 불변성이 그렇게 중요할까?
1.
예측 가능성
객체가 불변하면, 코드의 다른 부분에서 그 객체를 사용할 때 그 내용이 변하지 않았음을 확신할 수 있다.
프로그램이 어떻게 동작할지 예측하기 쉽고, 버그를 줄이고 디버깅을 더 쉽게 할 수 있다.
2.
스레드 안전성
멀티스레딩 환경에서, 여러 스레드가 동시에 같은 객체를 참조하고 있을 때,
객체가 불변하면 내용이 변경될 위험이 없어서 데이터가 꼬이거나 하는 문제를 걱정하지 않아도 된다. (Tread safe)
즉, 불변 객체는 스레드에 안전해서 복잡한 동기화 처리 없이도 안전하게 사용할 수 있다.
3.
메모리 사용 최적화
불변 객체는 한 번 생성되면 그 상태가 변하지 않으므로, 같은 값을 가진 객체가 필요할 때마다 새로 생성하는 대신 이미 있는 객체를 재사용할 수 있음. 이는 메모리 사용량을 줄이는 데 도움이 된다.
4.
함수형 프로그래밍
함수형 프로그래밍에서는 데이터의 불변성을 중요!!!.
데이터가 변하지 않으므로 부작용(side effects) 없이 데이터를 처리할 수 있다.
프로그램을 더 이해하기 쉽고 관리하기 쉬운 형태로 만들어 준다.

불변 객체를 만드는 조건

불변 클래스

불변 클래스를 만들기 위한 몇 가지 규칙이 있다.
1.
클래스를 final로 선언해서 상속을 막는다.
2.
모든 필드를 privatefinal로 선언.
3.
외부에서 객체의 상태를 변경할 수 없도록, setter 메서드를 제공하지 않는다.
4.
객체의 상태를 반환하는 메서드가 있을 경우, 객체의 실제 내부 상태를 직접 노출하지 않도록 복사본을 반환.
public final class ImmutablePerson { private final String name; private final int age; public ImmutablePerson(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } }
Java
복사
이 예제에서 ImmutablePerson 클래스는 불변이다.
객체가 한 번 생성되면, 그 nameage는 변경될 수 없다.
getName()getAge() 메서드는 객체의 상태를 외부에 제공하지만, 이 객체의 상태를 변경할 수 있는 메서드는 제공되지 않음. 이렇게 불변 객체를 사용하면, 객체의 상태가 예기치 않게 변경되는 것을 방지할 수 있어서 프로그램의 안정성과 예측 가능성이 높아진다.

다른 예제

이 클래스는 색상을 나타내며, 한 번 생성되면 그 색상의 값(RGB 값)을 변경할 수 없다.
public final class ImmutableRGB { // 모든 필드는 final로 선언되어야 하며, 객체 생성 후 변경할 수 없다. private final int red; private final int green; private final int blue; private final String name; // 생성자를 통해 모든 값을 초기화. 한 번 설정된 후에는 변경할 수 없다. public ImmutableRGB(int red, int green, int blue, String name) { checkRange(red, green, blue); this.red = red; this.green = green; this.blue = blue; this.name = name; } // RGB 값이 유효한 범위 내에 있는지 확인. 유효하지 않은 경우 예외를 발생시킨다. private void checkRange(int red, int green, int blue) { if (red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) { throw new IllegalArgumentException("Color value out of range"); } } // 각 필드 값을 반환하는 메서드들. 객체의 상태를 변경하는 메서드는 제공하지 않음. public int getRed() { return red; } public int getGreen() { return green; } public int getBlue() { return blue; } public String getName() { return name; } // 새로운 색상을 만들어 반환하는 메서드. 이 클래스의 인스턴스는 불변이므로, 새 상태를 반영하기 위해 새로운 객체를 반환한다. public ImmutableRGB invertColor() { return new ImmutableRGB(255 - red, 255 - green, 255 - blue, "Inverted " + name); } }
Java
복사
상태를 변경하려는 시도가 필요한 경우, 원본 객체는 그대로 유지되고 변경된 새로운 상태를 가진 새 객체가 반환된다.
예를 들어, invertColor 메소드는 원본 색상의 반전된 색상을 가진 새 ImmutableRGB 객체를 반환.
이렇게 불변 클래스를 사용하면, 객체의 상태가 예상치 못한 시점에 변경되는 것을 방지할 수 있으며, 멀티스레드 환경에서의 안전성과 예측 가능성이 향상된다.

정리

1.
스레드 안전성
불변 객체는 여러 스레드에서 동시에 사용되어도 상태가 변경되지 않기 때문에, 스레드 안전성을 자연스럽게 보장.
따라서 복잡한 동기화 처리 없이도 안전하게 사용할 수 있다.
2.
실행 시간 버그 감소
객체가 변경되지 않기 때문에, 예상치 못한 상태 변화로 인한 버그 발생 가능성이 줄어든다.
프로그램의 예측 가능성과 안정성이 향상.
3.
코드 이해와 유지 보수성 향상
불변 객체를 사용하면 객체의 생명주기 동안 상태가 일정하게 유지되므로, 코드를 이해하고 디버깅하기가 더 쉬워짐.
4.
메모리 오버헤드 감소
불변 객체는 안전하게 공유될 수 있으므로, 동일한 데이터를 가진 여러 객체의 인스턴스를 생성할 필요가 없다.
이는 메모리 사용량을 최적화하는 데 도움이 됨.