참치김밥은 최고의 한식이다

[Effective C# 2판] Item 3 : 값 타입은 변경 불가능한 것이 낫다 본문

책/C#

[Effective C# 2판] Item 3 : 값 타입은 변경 불가능한 것이 낫다

l__j__h 2024. 2. 21. 11:29

원자적으로 상태를 변경하는 타입과 변경 불가능한 타입 두 가지가 있다고 하자.

‘원자적’이란, 단일 개체라서 구성 필드 중 하나를 변경하면, 전체 콘텐츠도 그에 맞춰 변경해야함을 뜻한다. 예를 들어, 주소 타입은 원자적이라고 할 수 있다.가령, 주소에는 시, 구, 우편번호가 포함된다. 이때, 이미 완성된 주소 A에서 우편번호만 변경할 경우, 나머지 ‘시’와 ‘구’는 유효하지 않게 된다. 즉, 우편번호를 변경할 경우, 그에 맞추어 ‘시’와 ‘구’도 변경해야 한다. 이를 원자적 타입이라고 한다.

원자적 개체의 필드를 변경 가능하도록 구현하면, 멀티스레드 시스템을 돌리거나 예외가 발생했을 때 해당 개체가 유효하지 않은 상태로 사용될 수 있다. 아래 코드를 보자.

public struct Address{
	private string state;
	private int zipCode;

	public string City {get; set;}
	public string State{ get; set; }
	public string ZipCode {get; set;}
}

//사용 예
Address a1 = new Address();
a1.City = "Seongnam";
a1.State = "Gyeonggi";
a1.ZipCode = "12345";

//변경 코드
a1.City = "Seoul"; //이제, ZipCode와 State가 유효하지 
a1.ZipCode = "12334"; //여전히, State가 유효하지 않다.
a1.State = "Seoul"; //이제서야 정상이다.

 

위의 예에서, 도시 이름 필드(City)를 변경하면 a1은 올바르지 않은 상태가 된다. 도시 이름이 바뀌면 이에 대응하는 우편번호(ZipCode)와 도 이름(State)가 적절하지 않을 것이기 때문이다.

위 코드는 언뜻 보면 문제가 없어 보이지만, 이 코드를 멀티스레드 프로그램에서 사용한다면 문제가 될 수 있다. City 값을 변경한 후, ZipCode나 State를 변경하기 전에, 다른 스레드로 문맥 전환이 일어나면…….. 다른 스레드는 유효하지 않은 값을 보게 될 것이다… (끔찍하네)

유니티의 경우 멀티 스레드를 사용하지 않지만, 만약 City만 변경한 후에 예외가 발생한다면 주소의 일부만 변경된 채로 a1이 불완전하게 존재할 수 있다.

그렇다고해서 setter에 모든 유효성 검사와 스레드 동기화를 넣으면 복잡해진다.

따라서, 아래 3가지 방법을 통해 ‘변경 불가능한 타입’으로 만들어야 한다. (이때 프로퍼티는 모두 get만 있다고 가정한다.)

  • 애초부터 값을 초기화하는 생성자를 정의한다. 즉, 생성자를 통해서만 새로운 주소를 생성할 수 있고, 주소 안의 필드는 변경할 수 없다. Address(string City, string State, string zipCode)
  • 구조체를 초기화하는 팩토리 메서드를 만든다.
  • 변경 가능한 동반 클래스를 만들어라. (예를 들어 StringBuilder 는 여러 연산을 거쳐 최종적으로 변경 불가능한 완성된 문자열을 만들어낸다.)
728x90