불변 클래스는 어떤 시점에서 부담이 되는가?


16

데이터 모델을 보유 할 클래스를 설계 할 때 읽은 것은 불변의 객체를 만드는 것이 유용 할 수 있지만 생성자 매개 변수 목록과 딥 카피의 부담이 너무 커져 불변의 제한을 포기해야합니까?

예를 들어, 여기에 명명 된 것을 나타내는 불변 클래스가 있습니다 (C # 구문을 사용하고 있지만 원칙은 모든 OO 언어에 적용됩니다)

class NamedThing
{
    private string _name;    
    public NamedThing(string name)
    {
        _name = name;
    }    
    public NamedThing(NamedThing other)
    {
         this._name = other._name;
    }
    public string Name
    {
        get { return _name; }
    }
}

명명 된 사물은 새로운 명명 된 사물로 구성, 쿼리 및 복사 할 수 있지만 이름은 변경할 수 없습니다.

이것은 모두 좋지만 다른 속성을 추가하려고하면 어떻게됩니까? 생성자에 매개 변수를 추가하고 복사 생성자를 업데이트해야합니다. 복잡한 작업을 불변 으로 만들고 싶을 때 볼 수있는 한 문제가 너무 많지 만 문제가 시작됩니다 .

클래스에 다른 복잡한 클래스를 포함하는 may 속성과 컬렉션이 포함되어 있으면 생성자 매개 변수 목록이 악몽이 될 것 같습니다.

그렇다면 어떤 시점에서 클래스가 불변 하기에는 너무 복잡해 집니까?


저는 항상 모델의 클래스를 변경할 수 없도록 노력합니다. 거대하고 긴 구성자 매개 변수 목록이 있다면 클래스가 너무 커서 분할 될 수 있습니까? 하위 수준 개체도 변경할 수없고 동일한 패턴을 따르는 경우 상위 수준 개체는 너무 많이 고통받지 않아야합니다. 처음부터 시작할 때 데이터 모델을 변경 불가능하게 만드는 것보다 기존 클래스를 변경 불가능하게 변경하는 것이 훨씬 어렵다는 것을 알았습니다.
아무도

1
이 질문에서 제안 된 빌더 패턴을 볼 수 있습니다 : stackoverflow.com/questions/1304154/…
Ant

MemberwiseClone을 보셨습니까? 각각의 새 멤버에 대해 복사 생성자를 업데이트하지 않아도됩니다.
케빈 클라인

3
@Tony 컬렉션과 컬렉션에 포함 된 모든 내용을 변경할 수없는 경우 딥 카피가 필요하지 않으며 얕은 카피로 충분합니다.
mjcopple

2
따로, 나는 클래스가 "정당하게"불변이어야하지만 완전히 그렇지는 않은 클래스에서 "한 번만"필드를 사용합니다. 이것이 거대한 생성자의 문제를 해결하지만 불변 클래스의 이점을 대부분 제공한다는 것을 알았습니다. (즉, 내부 클래스 코드는 값의 변화에 대해 걱정할 필요하지 않음)
Earlz

답변:


23

그들이 부담이 될 때? 매우 빠르게 (특히 선택한 언어가 불변성에 대해 충분한 구문 지원을 제공하지 않는 경우)

불변성은 멀티 코어 딜레마와 그 모든 것의 은총으로 판매되고 있습니다. 그러나 대부분의 OO 언어에서 불변성은 모델과 프로세스에 인공 인공물과 실습을 추가하도록합니다. 각 복합 불변 클래스에는 동등하게 복잡한 (적어도 내부적으로) 빌더가 있어야합니다. 어떻게 디자인하든 여전히 강력한 커플 링을 소개합니다 (따라서 소개해야 할 이유가 더 낫습니다).

작은 비 복잡 클래스에서 모든 것을 모델링 할 수있는 것은 아닙니다. 때문이 아니라 - 그래서 많은 클래스와 구조를 위해, 우리는 인위적으로 그들을 분할 우리의 도메인 모델에서 의미가 있지만, 우리가 가지고 있기 때문에 코드의 복잡한 인스턴스와 빌더를 처리 할 수 있습니다.

사람들이 Java 또는 C #과 같은 범용 언어에서 불변성에 대한 아이디어를 너무 멀리 가져 와서 모든 것을 불변으로 만들면 여전히 나빠집니다. 결과적으로 사람들은 s-expression 구문을 쉽게 지원하지 않는 언어로 강요합니다.

엔지니어링은 타협과 절충을 통해 모델링하는 행위입니다. 누군가가 모든 것이 X 또는 Y 기능 언어 (완전히 다른 프로그래밍 모델)에서 불변하다는 것을 읽었 기 때문에 받아 들일 수없는 모든 것을 칙령에 의해 불변으로 만듭니다. 좋은 엔지니어링이 아닙니다.

작을 수도 있고 일관된 것을 불변으로 만들 수도 있습니다. 더 복잡한 것들이 말이 되면 불변이 될 수 있습니다 . 그러나 불변성은 은총 알이 아닙니다. 버그를 줄이고 확장 성과 성능을 향상시키는 능력은 불변성의 유일한 기능은 아닙니다. 적절한 엔지니어링 관행 의 기능입니다 . 결국 사람들은 불변성없이 훌륭하고 확장 가능한 소프트웨어를 작성했습니다.

불변성은 도메인 모델의 맥락에서 의미가없는 것 이외의 이유로 수행 될 경우 이유없이 수행되면 실제로는 빠른 부담이됩니다 (실수로 복잡 해짐).

나는 그것을 피하려고 노력한다.


6
luis, 간단하면서도 건전한 엔지니어링 원칙에 대한 설명으로 작성된 잘 쓰여지고 실용적으로 올바른 답변이 최신 코딩 페이드를 사용하는 것보다 많은 표를 얻지 못하는 경향이 있음을 알고 있습니까? 이것은 대단한 답변입니다.
Huperniketes

3
고마워 :) 나는 스스로 트렌드 트렌드를 알아 차 렸지만 괜찮습니다. Fad fanboys 우리가 나중에 더 나은 시간당 요금으로 수리받을 코드를 휘젓다, hahah :) jk ...
luis.espinal

4
총알? 아니. C # / Java의 어색함이 가치가 있습니까 (실제 그렇게 나쁘지는 않습니다)? 물론. 또한 불변성에서 멀티 코어의 역할은 매우 미미합니다. 진정한 이점은 추론의 용이성입니다.
Mauricio Scheffer

@Mauricio-당신이 그렇게 말하면 (Java의 불변성은 그렇게 나쁘지 않습니다). 1998 년부터 2011 년까지 Java에서 작업 한 후에는달라고 간절히 원하지만 단순한 코드 기반에서는 사소한 예외가 아닙니다. 그러나 사람들마다 경험이 다르기 때문에 본인의 POV에 주관성이 없다는 것을 인정합니다. 죄송합니다, 거기에 동의 할 수 없습니다. 그러나 나는 불변성과 관련하여 추론의 용이성이 가장 중요한 것에 동의합니다.
luis.espinal

13

나는 가능한 곳에서는 클래스가 불변 인 것을 주장하는 단계를 겪었다. 거의 모든 것에 대한 빌더, 불변 배열 등을 가졌습니다. 나는 당신의 질문에 대한 대답이 간단하다는 것을 알았습니다. 불변 클래스는 어떤 시점에서 부담이됩니까? 매우 빠르게. 직렬화하려는 경우 직렬화를 해제 할 수 있어야합니다. 즉, 변경 가능해야합니다. ORM을 사용하자마자 대부분은 속성을 변경할 수 있다고 주장합니다. 등등.

결국 그 정책을 변경 가능한 객체에 대한 변경 불가능한 인터페이스로 대체했습니다.

class NamedThing : INamedThing
{
    private string _name;    
    public NamedThing(string name)
    {
        _name = name;
    }    

    public NamedThing(NamedThing other)
    {
        this._name = other._name;
    }

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

interface INamedThing
{
    string Name { get; }
}

이제 객체에는 유연성이 있지만 호출 코드에 이러한 속성을 편집해서는 안된다고 알릴 수 있습니다.


8
사소한 퀴즈는 제쳐두고, 나는 불변의 객체가 명령형 언어로 프로그래밍 할 때 엉덩이에 매우 빨리 고통을 줄 수 있다는 데 동의하지만 불변의 인터페이스가 실제로 같은 문제 또는 문제를 해결하는지 확실하지 않습니다. 불변의 객체를 사용하는 주된 이유는 언제 어디서나 객체를 던질 수 있고 다른 사람이 당신의 상태를 손상시킬 것에 대해 걱정할 필요가 없기 때문입니다. 기본 객체가 변경 가능한 경우, 특히 변경 한 이유가 다양한 것들을 변경해야하기 때문에 보장 할 수 없습니다.
Aaronaught

1
@Aaronaught-재미있는 점. 실제 보호보다 심리적 인 것 같아요. 그러나 마지막 줄에는 잘못된 전제가 있습니다. 그것을 변경 가능하게 유지하는 이유는 인스턴스화 한 번 변경하지 않고 다양한 것들이 그것을 인스턴스화하고 반사를 통해 채워야하기 때문입니다.
pdr

1
@Aaronaught : 같은 방법으로 IComparable<T>그 경우 보장 X.CompareTo(Y)>0하고 Y.CompareTo(Z)>0, 다음 X.CompareTo(Z)>0. 인터페이스에는 계약이 있습니다. 에 대한 계약에서 IImmutableList<T>인스턴스가 외부 세계에 노출되기 전에 모든 항목 및 속성의 값을 "돌로 설정"해야한다고 지정하면 모든 합법적 인 구현이 그렇게합니다. IComparable구현이 이행 성을 위반 하는 것을 막을 수있는 것은 없지만 그렇게하는 구현은 불법입니다. 경우 SortedDictionary오작동은 불법 주어 졌을 때 IComparable, ...
supercat

1
@Aaronaught : 왜의 구현 것을 신뢰해야 IReadOnlyList<T>불변의 것, (1) 이러한 요구 사항은 인터페이스 설명서에 언급되지 않으며, (2) 가장 일반적인 구현 주어진 List<T>, 심지어 읽기 전용이다 ? 내 용어에 대해 모호한 것이 무엇인지는 확실하지 않습니다. 내부의 데이터를 읽을 수 있으면 컬렉션을 읽을 수 있습니다. 코드를 통해 외부 참조를 변경하지 않으면 포함 된 데이터를 변경할 수 없다고 약속 할 수있는 경우 읽기 전용 입니다. 그것이 변경 될 수 없음을 보장 할 수 있다면 불변입니다.
supercat

1
@supercat : 우연히도 Microsoft는 저에게 동의합니다. 그들은 불변의 컬렉션 패키지를 출시했으며 추상 클래스 또는 인터페이스가 실제로 불변이라는 것을 보장 할 수 없기 때문에 모든 구체적인 유형임을 알 수 있습니다.
Aaronaught

8

나는 이것에 대한 일반적인 대답이 없다고 생각합니다. 클래스가 복잡할수록 상태 변경에 대한 추론이 어려워지고 새 사본을 만드는 데 비용이 많이 듭니다. 따라서 (개인적) 수준의 복잡성보다 클래스를 불변으로 만들거나 유지하기에는 너무 고통 스럽습니다.

참고 것을 너무 복잡 클래스 또는 긴 메소드 매개 변수 목록이 디자인 냄새입니다 관계없이 불변의, 그 자체.

따라서 일반적으로 선호되는 솔루션은 이러한 클래스를 여러 개의 개별 클래스로 나누는 것입니다. 각 클래스는 자체적으로 변경 가능하거나 변경할 수 없습니다. 이것이 가능하지 않은 경우, 변경할 수 있습니다.


5

불변의 모든 필드를 inner에 저장하면 복사 문제를 피할 수 있습니다 struct. 이것은 기본적으로 메멘토 패턴의 변형입니다. 그런 다음 사본을 만들려면 기념품을 복사하십시오.

class MyClass
{
    struct Memento
    {
        public int field1;
        public string field2;
    }

    private readonly Memento memento;

    public MyClass(int field1, string field2)
    {
        this.memento = new Memento()
            {
                field1 = field1,
                field2 = field2
            };
    }

    private MyClass(Memento memento) // for copying
    {
        this.memento = memento;
    }

    public int Field1 { get { return this.memento.field1; } }
    public string Field2 { get { return this.memento.field2; } }

    public MyClass WithNewField1(int newField1)
    {
        Memento newMemento = this.memento;
        newMemento.field1 = newField1;
        return new MyClass(newMemento);
    }
}

내면의 구조체가 필요하지 않다고 생각합니다. MemberwiseClone을 수행하는 또 다른 방법입니다.
Codism

@Codism-예, 아니오 복제하지 않으려는 다른 구성원이 필요할 수 있습니다. 게터 중 하나에서 게으른 평가를 사용하고 결과를 멤버에 캐싱하는 경우 어떻게됩니까? MemberwiseClone을 수행하면 캐시 된 값이 복제되고 캐시 된 값이 의존하는 멤버 중 하나가 변경됩니다. 캐시에서 상태를 분리하는 것이 더 깨끗합니다.
Scott Whitlock

내부 구조체의 또 다른 장점을 언급 할 가치가 있습니다. 객체 가 다른 참조가 존재 하는 객체 상태를 쉽게 복사 할 수있게 합니다 . OOP의 일반적인 애매 모호한 원인은 객체 참조를 반환하는 메서드가받는 사람의 컨트롤 외부에서 변경 될 수있는 객체의 뷰를 반환하는지 여부입니다. 객체 참조를 반환하는 대신 메서드가 호출자로부터 객체 참조를 받아 상태를 복사하면 객체의 소유권이 훨씬 명확 해집니다. 이러한 접근 방식은 상속 할 수없는 유형에는 적합하지 않지만 ...
supercat

가변 데이터 홀더에 매우 유용합니다. 이 접근 방식은 또한 "병렬"변경 가능하고 변경 불가능한 클래스 (추상적 인 "판독 가능"기반에서 파생 됨)를 갖고 생성자가 서로 데이터를 복사 할 수 있도록하는 것이 매우 쉽습니다.
supercat

3

여기 몇 가지 일이 있습니다. 변경 불가능한 데이터 세트는 멀티 스레드 확장성에 좋습니다. 기본적으로 하나의 매개 변수 집합이 클래스의 한 인스턴스가 될 수 있도록 메모리를 상당히 최적화 할 수 있습니다. 객체는 절대 변경되지 않기 때문에 멤버 액세스에 대한 동기화에 대해 걱정할 필요가 없습니다. 좋은 일입니다. 그러나 지적했듯이 객체가 복잡할수록 약간의 가변성이 필요합니다. 나는이 라인을 따라 추론으로 시작할 것이다.

  • 객체 상태를 변경할 있는 비즈니스 이유가 있습니까? 예를 들어, 데이터베이스에 저장된 사용자 개체는 ID에 따라 고유하지만 시간이 지남에 따라 상태를 변경할 수 있어야합니다. 반면에 그리드에서 좌표를 변경하면 원래 좌표가되지 않으므로 좌표를 변경할 수 없게됩니다. 문자열과 동일합니다.
  • 일부 속성을 계산할 수 있습니까? 즉, 객체의 새 복사본에있는 다른 값이 전달하는 일부 핵심 값의 함수 인 경우 생성자 또는 요청시 값을 계산할 수 있습니다. 이렇게하면 복사 또는 생성시 동일한 방식으로 해당 값을 초기화 할 수 있으므로 유지 관리 비용이 줄어 듭니다.
  • 새로운 불변 ​​객체를 구성하는 값은 몇 개입니까? 어떤 시점에서 객체 생성의 복잡성은 사소하지 않게되고 그 시점에서 더 많은 객체 인스턴스를 갖는 것이 문제가 될 수 있습니다. 예를 들어 불변 트리 구조, 매개 변수가 세 개 이상 전달 된 객체 등이 있습니다.

변경 불가능한 오브젝트 (예 : Erlang) 만 지원하는 언어에서 변경 불가능한 오브젝트의 상태를 수정하는 조작이있는 경우 최종 결과는 갱신 된 값을 가진 오브젝트의 새 사본입니다. 예를 들어 벡터 / 목록에 항목을 추가 할 때 :

myList = lists:append([[1,2,3], [4,5,6]])
% myList is now [1,2,3,4,5,6]

더 복잡한 객체를 사용하는 건전한 방법 일 수 있습니다. 예를 들어 트리 노드를 추가하면 결과에 노드가 추가 된 새 트리가 생성됩니다. 위 예제의 메소드는 새 목록을 리턴합니다. 이 단락의 예에서 tree.add(newNode)노드가 추가 된 새 트리를 반환합니다. 사용자에게는 작업하기가 쉬워집니다. 라이브러리 작성자의 경우 언어가 암시 적 복사를 지원하지 않으면 지루해집니다. 그 임계 값은 자신의 인내심에 달려 있습니다. 라이브러리 사용자의 경우 내가 찾은 가장 제 한한 한계는 약 3 ~ 4 개의 매개 변수입니다.


변경 가능한 객체 참조 으로 사용하려는 경향이 있는 경우 [소유자 내에 참조가 포함되지 않고 노출되지 않음을 의미] 원하는 "변경된"내용을 보유하는 새로운 불변 ​​객체를 구성하는 것은 객체를 직접 수정하는 것과 같습니다. 비록 느릴 것입니다. 그러나 가변 객체도 엔티티 로 사용할 수 있습니다 . 변경 가능한 객체가없는 엔티티처럼 동작하는 것을 어떻게 만들 수 있습니까?
supercat

0

최종 클래스 멤버가 여러 개인 경우이를 생성해야하는 모든 오브젝트에 노출시키지 않으려면 빌더 패턴을 사용할 수 있습니다.

class NamedThing
{
    private string _name;    
    private string _value;
    private NamedThing(string name, string value)
    {
        _name = name;
        _value = value;
    }    
    public NamedThing(NamedThing other)
    {
        this._name = other._name;
        this._value = other._value;
    }
    public string Name
    {
        get { return _name; }
    }

    public static class Builder {
        string _name;
        string _value;

        public void setValue(string value) {
            _value = value;
        }
        public void setName(string name) {
            _name = name;
        }
        public NamedThing newObject() {
            return new NamedThing(_name, _value);
        }
    }
}

다른 이름의 다른 이름으로 만 새 객체를 쉽게 만들 수 있다는 장점이 있습니다.


정적 인 빌더가 올바르지 않다고 생각합니다. 다른 스레드는 정적 이름 또는 정적 값을 설정 한 후 호출하기 전에 변경할 수 있습니다 newObject.
ErikE

빌더 클래스 만 정적이지만 멤버는 그렇지 않습니다. 즉, 작성하는 모든 빌더에 대해 해당 값을 가진 고유 한 멤버 세트가 있습니다. 클래스는 포함 클래스 외부에서 사용하고 인스턴스화 할 수 있도록 정적이어야합니다 ( NamedThing이 경우)
Salandur

나는 당신이 말하는 것을 보았습니다. 개발자가 "성공의 구덩이에 빠지지"않기 때문에 이것에 문제가 있다고 생각합니다. 정적 변수를 사용한다는 사실은 a Builder가 재사용되면 내가 언급 한 일이 실제로 발생할 위험이 있음을 의미합니다 . 누군가가 많은 객체를 만들고 아마도 대부분의 속성이 동일하기 때문에 단순히 재사용하기 Builder위해 의존성을 주입하는 전역 단일 톤으로 만들자! 으악. 주요 버그가 소개되었습니다. 따라서 혼합 인스턴스화와 정적 의이 패턴이 잘못되었다고 생각합니다.
ErikE

1
C #의 @Salandur 내부 클래스는 Java 내부 클래스 의미에서 항상 "정적"입니다.
Sebastian Redl

0

그렇다면 어떤 시점에서 클래스가 불변하기에는 너무 복잡합니까?

제 생각에는 작은 클래스를 당신이 보여주는 언어와 같은 언어로 변경할 수 없게 만드는 것은 가치가 없습니다 . 나는 여기에 작고 복잡 하지 않습니다. 해당 클래스에 10 개의 필드를 추가하고 실제로 멋진 작업을 수행하더라도 메가 바이트는 물론 기가 바이트는 물론 킬로바이트를 취할 것이므로 의심 할 여지가 없습니다. 클래스는 단순히 외부 부작용을 피하기 위해 원본을 수정하지 않도록 전체 객체의 저렴한 사본을 만들 수 있습니다.

지속적인 데이터 구조

불변성을 개인적으로 사용하는 곳은 백만을 저장하는 클래스와 같이 보여주고있는 클래스의 인스턴스와 같은 많은 십대 데이터를 집계하는 큰 중앙 데이터 구조입니다 NamedThings. 변경 불가능한 영구 데이터 구조에 속하고 읽기 전용 액세스 만 허용하는 인터페이스 뒤에 있으면 컨테이너에 속하는 요소는 요소 클래스 ( NamedThing)를 처리 하지 않고도 변경할 수 없습니다 .

저렴한 사본

지속적인 데이터 구조는 데이터 구조를 전체적으로 복사하지 않고도 원본의 수정을 피하면서 영역을 변형하고 고유하게 만들 수 있습니다. 그것이 진정한 아름다움입니다. 기가 바이트의 메모리를 차지하고 메가 바이트의 메모리 만 수정하는 데이터 구조를 입력하는 부작용을 피하는 함수를 순진하게 작성하려면 입력을 건드리지 않고 새로운 기능을 반환하기 위해 전체 괴물을 복사해야합니다. 산출. 부작용을 피하기 위해 기가 바이트를 복사하거나 해당 시나리오에서 부작용을 일으키므로 두 가지 불쾌한 선택 중에서 선택해야합니다.

영구적 인 데이터 구조를 사용하면 이러한 함수를 작성하고 전체 데이터 구조를 복사하지 않아도되며, 함수가 메가 바이트의 메모리 만 변환 한 경우 출력에 약 메가 바이트의 추가 메모리 만 있으면됩니다.

부담

부담은 적어도 내 경우에는 즉각적인 문제가 있습니다. 사람들이 거대한 데이터 구조에 대한 변형을 건드리지 않고 효과적으로 표현할 수 있으려면 사람들이 말하고있는 "일시적인"빌더가 필요합니다. 다음과 같은 코드 :

void transform_stuff(MutList<Stuff>& stuff, int first, int last)
{
     // Transform stuff in the range, [first, last).
     for (; first != last; ++first)
          transform(stuff[first]);
}

... 다음과 같이 작성해야합니다.

ImmList<Stuff> transform_stuff(ImmList<Stuff> stuff, int first, int last)
{
     // Grab a "transient" (builder) list we can modify:
     TransientList<Stuff> transient(stuff);

     // Transform stuff in the range, [first, last)
     // for the transient list.
     for (; first != last; ++first)
          transform(transient[first]);

     // Commit the modifications to get and return a new
     // immutable list.
     return stuff.commit(transient);
}

그러나이 두 줄의 코드를 교체하면 동일한 원래 목록을 가진 스레드를 호출해도 안전하고 부작용 등이 발생하지 않습니다. 또한이 작업을 실행 취소 할 수없는 사용자 작업으로 만드는 것이 매우 쉽습니다. 실행 취소는 이전 목록의 저렴한 얕은 사본을 저장할 수 있습니다.

예외 안전 또는 오류 복구

모든 사람들이 이와 같은 맥락에서 영구적 인 데이터 구조에서 얻은 것만 큼 많은 이점을 얻을 수는 없습니다 (VFX 도메인의 중심 개념 인 실행 취소 시스템 및 비파괴 편집에서 많은 용도로 사용됨). 고려해야 할 모든 사람은 예외 안전 또는 오류 복구 입니다.

원래의 변경 기능을 예외로부터 안전하게 만들려면 롤백 로직이 필요합니다. 롤백 로직이 가장 간단한 구현에는 전체 목록을 복사해야 합니다.

void transform_stuff(MutList<Stuff>& stuff, int first, int last)
{
    // Make a copy of the whole massive gigabyte-sized list 
    // in case we encounter an exception and need to rollback
    // changes.
    MutList<Stuff> old_stuff = stuff;

    try
    {
         // Transform stuff in the range, [first, last).
         for (; first != last; ++first)
             transform(stuff[first]);
    }
    catch (...)
    {
         // If the operation failed and ran into an exception,
         // swap the original list with the one we modified
         // to "undo" our changes.
         stuff.swap(old_stuff);
         throw;
    }
}

이 시점에서 예외 안전 변경 가능 버전은 "빌더"를 사용하여 변경 불가능한 버전보다 계산 비용이 훨씬 비싸고 정확하게 작성하기가 훨씬 더 어렵습니다. 그리고 많은 C ++ 개발자는 예외 안전을 무시하고 도메인에 적합 할 수도 있지만 예외가 발생하는 경우에도 코드가 올바르게 작동하는지 확인하고 싶습니다 (심지어 예외를 테스트하기 위해 예외를 던지는 테스트 작성) 안전), 그래서 그것은 무언가가 던져지면 함수가 함수에 반쯤 발생하는 부작용을 롤백 할 수 있어야합니다.

예외 안전하고 응용 프로그램 충돌 및 굽기없이 오류를 정상적으로 복구하려면 오류 / 예외가 발생했을 때 기능으로 인해 발생할 수있는 부작용을 되돌 리거나 취소해야합니다. 그리고 빌더는 실제로 계산 시간과 함께 비용보다 더 많은 프로그래머 시간을 절약 할 수 있습니다.

아무런 영향을 미치지 않는 함수에서 부작용을 롤백하는 것에 대해 걱정할 필요가 없습니다!

근본적인 질문으로 돌아가십시오.

불변 클래스는 어떤 시점에서 부담이 되는가?

불변성보다 변이성을 중심으로하는 언어에서는 항상 부담이되므로 이점이 비용보다 훨씬 큰 곳에서 사용해야합니다. 그러나 충분히 큰 데이터 구조를 수용 할 수있을만큼 넓은 수준에서 가치있는 트레이드 오프가되는 경우가 많다고 생각합니다.

또한 필자는 몇 가지 불변의 데이터 유형 만 가지고 있으며 막대한 수의 요소 (이미지 / 텍스처의 픽셀, ECS의 엔티티 및 구성 요소, 정점 / 가장자리 / 다각형)를 저장하려는 거대한 데이터 구조입니다. 메쉬).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.