불변성이란 무엇이며 왜 걱정해야합니까?
불변성에 대한 기사를 몇 개 읽었지만 여전히 개념을 잘 따르지 않습니다.
최근에 여기에 불변성을 언급 한 스레드를 만들었지 만 이것이 그 자체로 주제이므로 지금은 전용 스레드를 만들고 있습니다.
나는 불변성이 객체를 읽기 전용으로 만들고 낮은 가시성을 부여하는 과정이라고 생각했다. 또 다른 멤버는 그것과는 아무런 관련이 없다고 말했다. 이 페이지 ( 시리즈의 일부 )는 변경 불가능한 클래스 / 구조체의 예를 사용하며 읽기 전용 및 기타 개념을 사용하여 잠급니다.
이 예제의 경우 상태의 정의는 정확히 무엇입니까? 상태는 제가 실제로 이해하지 못한 개념입니다.
디자인 지침 관점에서 볼 때 불변 클래스는 사용자 입력을 받아들이지 않고 실제로 값을 반환하는 클래스 여야합니까?
내 이해는 정보를 반환하는 모든 객체는 불변하고 "잠겨 있어야"한다는 것입니다. 맞습니까? 따라서 해당 메서드를 사용하여 전용 클래스에서 현재 시간을 반환하려면 참조 유형을 사용해야합니다. 참조 유형이 유형의 참조를 작동하므로 불변성의 이점을 얻습니다.
불변성이란 무엇입니까?
- 불변성은 주로 객체 (문자열, 배열, 사용자 지정 Animal 클래스)에 적용됩니다.
- 일반적으로 변경 불가능한 클래스 버전이있는 경우 변경 가능한 버전도 사용할 수 있습니다. 예를 들어 Objective-C와 Cocoa는 NSString 클래스 (불변)와 NSMutableString 클래스를 모두 정의합니다.
- 객체가 변경 불가능한 경우 생성 된 후에 변경할 수 없습니다 (기본적으로 읽기 전용). "생성자 만이 객체를 변경할 수 있습니다"라고 생각할 수 있습니다.
이것은 사용자 입력과 직접적인 관련이 없습니다. 코드조차도 불변 객체의 값을 변경할 수 없습니다. 그러나 언제든지 변경할 수없는 새 개체를 만들어이를 대체 할 수 있습니다. 다음은 의사 코드 예입니다. 많은 언어에서 myString = "hello";
아래와 같이 생성자를 사용하는 대신 간단하게 할 수 있지만 명확성을 위해 포함했습니다.
String myString = new ImmutableString("hello");
myString.appendString(" world"); // Can't do this
myString.setValue("hello world"); // Can't do this
myString = new ImmutableString("hello world"); // OK
"정보를 반환하는 객체"를 언급합니다. 이것은 자동으로 불변성의 좋은 후보가되지는 않습니다. 불변 객체는 항상 생성 된 것과 동일한 값을 반환하는 경향이 있으므로 자주 변경되기 때문에 현재 시간이 이상적이지 않다고 말하는 경향이 있습니다. 그러나 특정 타임 스탬프를 사용하여 생성 된 MomentOfTime 클래스가있을 수 있으며 나중에 해당 타임 스탬프를 항상 반환합니다.
불변성의 이점
객체를 다른 함수 / 메서드에 전달하는 경우 해당 객체가 함수가 반환 된 후 동일한 값을 가질 지 여부에 대해 걱정할 필요가 없습니다. 예를 들면 :
String myString = "HeLLo WoRLd"; String lowercasedString = lowercase(myString); print myString + " was converted to " + lowercasedString;
lowercase()
소문자 버전을 만들 때 myString 구현이 변경 되면 어떻게 됩니까? 세 번째 줄은 원하는 결과를 제공하지 않습니다. 물론 좋은lowercase()
함수는이를 수행하지 않지만 myString이 불변이면이 사실을 보장합니다. 따라서 불변 객체는 좋은 객체 지향 프로그래밍 관행을 시행하는 데 도움이 될 수 있습니다.변경 불가능한 객체를 스레드로부터 안전하게 만드는 것이 더 쉽습니다.
- 잠재적으로 클래스의 구현을 단순화합니다 (클래스를 작성하는 경우 유용함).
상태
개체의 모든 인스턴스 변수를 가져 와서 그 값을 종이에 적어두면 주어진 순간에 해당 개체의 상태가됩니다. 프로그램의 상태는 주어진 순간에 모든 객체의 상태입니다. 상태는 시간이 지남에 따라 빠르게 변합니다. 프로그램을 계속 실행하려면 상태를 변경해야합니다.
그러나 불변 객체는 시간이 지남에 따라 고정 된 상태를 갖습니다. 일단 생성되면, 프로그램 전체의 상태는 변경 될 수 있지만 변경 불가능한 객체의 상태는 변경되지 않습니다. 이렇게하면 어떤 일이 일어나고 있는지 더 쉽게 추적 할 수 있습니다 (위의 다른 이점 참조).
불변성
간단히 말해서, 메모리는 초기화 후 수정되지 않으면 불변입니다.
C, Java 및 C #과 같은 명령형 언어로 작성된 프로그램은 메모리 내 데이터를 마음대로 조작 할 수 있습니다. 일단 따로 설정 한 실제 메모리 영역은 프로그램 실행 중 언제든지 실행 스레드에 의해 전체 또는 일부를 수정할 수 있습니다. 실제로 명령형 언어는 이러한 프로그래밍 방식을 권장합니다.
이러한 방식으로 프로그램을 작성하는 것은 단일 스레드 응용 프로그램에서 매우 성공적이었습니다. 그러나 최신 애플리케이션 개발이 단일 프로세스 내에서 여러 개의 동시 작업 스레드로 이동함에 따라 잠재적 인 문제와 복잡성의 세계가 도입되었습니다.
실행 스레드가 하나 뿐인 경우이 단일 스레드가 메모리의 모든 데이터를 '소유'하므로 마음대로 조작 할 수 있다고 상상할 수 있습니다. 그러나 여러 실행 스레드가 관련되어있는 경우 소유권에 대한 암시 적 개념이 없습니다.
대신, 이러한 부담은 모든 독자가 인 메모리 구조를 일관된 상태로 유지하기 위해 많은 노력을 기울여야하는 프로그래머에게 있습니다. 잠금 구문은 한 스레드가 다른 스레드에 의해 업데이트되는 동안 데이터를 볼 수 없도록 신중하게 사용해야합니다. 이 조정이 없으면 스레드는 필연적으로 업데이트 중간에 있던 데이터를 소비하게됩니다. 그러한 상황의 결과는 예측할 수 없으며 종종 비극적입니다. 또한 코드에서 잠금이 올바르게 작동하도록하는 것은 매우 어렵고 잘못 수행하면 성능이 저하되거나 최악의 경우 실행을 복구 불가능하게 중단하는 교착 상태가 발생할 수 있습니다.
불변 데이터 구조를 사용하면 코드에 복잡한 잠금을 도입 할 필요성이 줄어 듭니다. 프로그램의 수명 동안 메모리 섹션이 변경되지 않도록 보장되면 여러 판독기가 동시에 메모리에 액세스 할 수 있습니다. 불일치 상태의 특정 데이터를 관찰하는 것은 불가능합니다.
Lisp, Haskell, Erlang, F # 및 Clojure와 같은 많은 함수형 프로그래밍 언어는 본질적으로 불변 데이터 구조를 권장합니다. 우리가 점점 더 복잡 해지는 다중 스레드 응용 프로그램 개발과 많은 컴퓨터 컴퓨터 아키텍처로 이동함에 따라 그들이 관심의 부활을 즐기고있는 것은 바로 이러한 이유 때문입니다.
상태
애플리케이션의 상태는 주어진 시점에서 모든 메모리 및 CPU 레지스터의 내용으로 간단히 생각할 수 있습니다.
논리적으로 프로그램의 상태는 두 가지로 나눌 수 있습니다.
- 힙 상태
- 각 실행 스레드의 스택 상태
C # 및 Java와 같은 관리 환경에서는 한 스레드가 다른 스레드의 메모리에 액세스 할 수 없습니다. 따라서 각 스레드는 스택 상태를 '소유'합니다. 스택은 값 유형 ( struct
) 의 지역 변수와 매개 변수 및 객체에 대한 참조를 보유하는 것으로 생각할 수 있습니다 . 이러한 값은 외부 스레드에서 격리됩니다.
그러나 힙의 데이터는 모든 스레드에서 공유 할 수 있으므로 동시 액세스를 제어하려면주의해야합니다. 모든 참조 유형 ( class
) 개체 인스턴스는 힙에 저장됩니다.
OOP에서 클래스 인스턴스의 상태는 필드에 의해 결정됩니다. 이러한 필드는 힙에 저장되므로 모든 스레드에서 액세스 할 수 있습니다. 클래스가 생성자가 완료된 후 필드를 수정할 수 있도록하는 메서드를 정의하는 경우 클래스는 변경 가능합니다 (불변이 아님). 필드를 어떤 식 으로든 변경할 수없는 경우 유형은 변경 불가능합니다. 모든 C # readonly
/ Java final
필드가 있는 클래스가 반드시 변경 불가능한 것은 아닙니다. 이러한 구성은 참조 를 변경할 수 없지만 참조 된 개체는 변경할 수 없도록합니다. 예를 들어 필드에는 개체 목록에 대한 변경 불가능한 참조가있을 수 있지만 목록의 실제 내용은 언제든지 수정할 수 있습니다.
유형을 변경 불가능한 것으로 정의하면 해당 상태가 고정 된 것으로 간주 될 수 있으므로 유형은 여러 스레드에서 액세스하는 데 안전합니다.
실제로 모든 유형을 불변으로 정의하는 것은 불편할 수 있습니다. 변경 불가능한 유형의 a 값을 수정하려면 상당한 메모리 복사가 필요할 수 있습니다. 일부 언어는이 프로세스를 다른 언어보다 쉽게 만들지 만 어느 쪽이든 CPU가 추가 작업을 수행하게됩니다. 메모리 복사에 소요 된 시간이 잠금 경합의 영향을 초과하는지 여부를 결정하는 데 많은 요인이 기여합니다.
목록 및 트리와 같은 불변 데이터 구조의 개발에 대한 많은 연구가 진행되었습니다. 목록과 같은 구조를 사용할 때 '추가'작업은 새 항목이 추가 된 새 목록에 대한 참조를 반환합니다. 이전 목록에 대한 참조는 변경 사항이 없으며 데이터에 대한 일관된보기를 유지합니다.
간단히 말해서 불변 객체를 생성하면 해당 객체의 내용을 변경할 방법이 없습니다. .Net 불변 개체의 예로는 String 및 Uri가 있습니다.
문자열을 수정할 때 단순히 새 문자열을 얻습니다. 원래 문자열은 변경되지 않습니다. Uri에는 읽기 전용 속성 만 있으며 Uri의 내용을 변경하는 데 사용할 수있는 메서드가 없습니다.
불변 개체가 중요한 경우는 다양하며 대부분의 경우 보안과 관련이 있습니다. Uri가 여기에 좋은 예입니다. (예 : 신뢰할 수없는 코드에 의해 Uri가 변경되는 것을 원하지 않습니다.) 이것은 내용이 변경 될 염려없이 주변의 불변 객체에 대한 참조를 전달할 수 있음을 의미합니다.
도움이 되었기를 바랍니다.
불변하는 것은 결코 변하지 않습니다. 변덕스러운 것은 바뀔 수 있습니다. 변이 할 수있는 것들은 변합니다. 변경 불가능한 것은 변경되는 것처럼 보이지만 실제로는 새로운 변경 가능한 것을 생성합니다.
예를 들어 여기 Clojure의지도가 있습니다.
(def imap {1 "1" 2 "2"})
(conj imap [3 "3"])
(println imap)
첫 번째 줄은 새로운 불변의 Clojure 맵을 만듭니다. 두 번째 선은 3과 "3"을지도에 연결합니다. 이것은 이전지도를 수정하는 것처럼 보일 수 있지만 실제로는 3 "3"이 추가 된 새 지도를 반환합니다 . 이것은 불변성의 대표적인 예입니다. 이 변경 가능한지도 있었다면 그것은 단순히 직접 "3"3를 추가 한 것 으로 같은 옛지도. 세 번째 줄은지도를 인쇄합니다.
{3 "3", 1 "1", 2 "2"}
불변성은 코드를 깨끗하고 안전하게 유지하는 데 도움이됩니다. 이것과 다른 이유는 함수형 프로그래밍 언어가 불변성과 덜 상태 저장성에 의존하는 경향이있는 이유입니다.
좋은 질문.
멀티 스레딩. 모든 유형이 불변이면 경쟁 조건이 존재하지 않으며 원하는만큼 코드에 스레드를 던져도 안전합니다.
분명히 가변성 없이는 복잡한 계산을 저장하지 않고 그렇게 많은 것을 달성 할 수 없으므로 일반적으로 기능적인 비즈니스 소프트웨어를 만들려면 약간의 가변성이 필요합니다. 그러나 트랜잭션과 같이 불변성이 어디에 있어야 하는지를 인식하는 것은 가치가 있습니다.
철학에 대한 자세한 내용은 함수형 프로그래밍과 순수성의 개념을 찾아보십시오. 컬렉션이나 정적으로 사용 가능한 객체와 같은 참조를 통해 사용할 수 있도록하는 것과는 반대로 호출 스택 (메서드에 전달하는 매개 변수)에 더 많이 저장할수록 프로그램이 더 순수 해지고 경쟁 조건이 덜 발생합니다. 요즘에는 더 많은 멀티 코어로이 주제가 더 중요합니다.
또한 불변성은 프로그램의 가능성을 줄여 잠재적 인 복잡성과 버그 가능성을 줄입니다.
불변 객체는 변경되지 않을 것이라고 안전하게 가정 할 수있는 것입니다. 그것은 그것을 사용하는 모든 사람이 동일한 가치를보고 있다고 가정 할 수있는 중요한 속성을 가지고 있습니다.
불변성은 일반적으로 개체를 "값"으로 생각할 수 있으며 개체의 동일한 복사본과 개체 자체간에 효과적인 차이가 없음을 의미합니다.
한 가지 더 추가하겠습니다. 위에서 언급 한 모든 것 외에도 다음과 같은 불변성을 원합니다.
- 값 개체 (데이터 개체 또는 pojo라고도 함)
- 구조체 (C # /. NET)- 복싱에 대한 stackoverflow 질문 참조
불변으로 만드는 것은 많은 일반적인 실수를 방지합니다.
예를 들어, 학생은 자신의 학생을 # 변경해서는 안됩니다. 변수를 설정하는 방법을 제공하지 않으면 (그리고 그것을 const, final 또는 언어가 지원하는 모든 것으로 만들면) 컴파일 타임에 적용 할 수 있습니다.
사물이 변경 가능하고 주변을 통과 할 때 변경되는 것을 원하지 않는 경우 통과 할 수있는 방어 복사본을 만들어야합니다. 그런 다음 호출하는 메서드 / 함수가 항목의 사본을 변경하면 원본은 그대로 유지됩니다.
불변으로 만드는 것은 방어용 복사본을 만들기 위해 기억 (또는 시간 / 기억을 할 필요가 없음)을 의미하지 않습니다.
실제로 작업하고 만드는 각 변수에 대해 생각하면 변수의 대다수 (일반적으로 90-95 %)가 값이 주어지면 절대 변경되지 않는다는 것을 알게 될 것입니다. 이렇게하면 프로그램을 더 쉽게 따라갈 수 있고 버그 수를 줄일 수 있습니다.
상태에 대한 질문에 답하기 위해 상태는 "객체"(클래스 또는 구조체)의 변수가 갖는 값입니다. 사람의 "대상"상태는 눈 색깔, 머리 색깔, 머리 길이 등과 같은 것입니다. 그 중 일부 (예 : 눈 색깔)는 변하지 않는 반면 다른 것 (예 : 머리 길이)은 변합니다.
"... 왜 걱정해야하지?"
실용적인 예는 문자열의 반복적 인 연결입니다. 예를 들어 .NET에서 :
string SlowStringAppend(string [] files)
{
// Declare an string
string result="";
for (int i=0;i<files.length;i++)
{
// result is a completely new string equal to itself plus the content of the new
// file
result = result + File.ReadAllText(files[i]);
}
return result;
}
string EfficientStringAppend(string [] files)
{
// Stringbuilder manages a internal data buffer that will only be expanded when absolutely necessary
StringBuilder result=new SringBuilder();
for (int i=0;i<files.length;i++)
{
// The pre-allocated buffer (result) is appended to with the new string
// and only expands when necessary. It doubles in size each expansion
// so need for allocations become less common as it grows in size.
result.Append(File.ReadAllText(files[i]));
}
return result.ToString();
}
불행히도 첫 번째 (느린) 함수 접근 방식을 사용하는 것이 여전히 일반적으로 사용됩니다. 불변성에 대한 이해는 StringBuilder를 사용하는 것이 왜 그렇게 중요한지 명확하게합니다.
You cannot change an immutable object, therefore you must replace it.... "to change it". i.e. replace then discard. "Replacing" in this sense means changing the pointer from one memory location (of the old value) to another (for the new value).
Note that in doing so we now using additional memory. Some for the old value, some for the new value. Also note, some people get confused because they look at code, such as:
string mystring = "inital value";
mystring = "new value";
System.Console.WriteLine(mystring); // Outputs "new value";
and think to themselves, "but I am changing it, look right there, in black and white! mystring outputs 'new value'...... I thought you said i couldn't change it?!!"
But actually under the hood, whats happening is this allocation of new memory i.e. mystring is now pointing at a different memory address and space. "Immutable" in this sense, is not referring to the value of mystring but rather the memory used by the variable mystring to store its value.
In certain languages the memory storing the old value must be manually cleaned up i.e. the programmer must explicitly release it..... and remember to do so. In other languages this is an automatic feature of the language i.e. garbage collection in .Net.
One of the places this really blows out re:memory usage is in highly iterative loops, specially with strings as in Ashs' post. Say you were building an HTML page in an iterative loop, where you constantly appended the next HTML block to the last and, just for kicks, you were doing this on a high volume server. This constant allocation of "new value memory" can quickly get expensive, and ultimately fatal, if the "old value memory", is not properly cleaned up.
Another problem is that some people assume things like garbage collection (GC) happens immediately. But it doesn't. There are various optimizations that occur such that garbage collection is set to occur during the more idle periods. So there can be a significant delay between when the memory is marked as discarded and when it is actually freed by the garbage collector.... so you can suffer large memory usage spikes if you simply defer the problem to the GC.
If the GC doesn't get a chance to operate before you run out of memory, then things wont necessarily fall over like in other languages that don't have automatic garbage collection. Instead the GC will kick in as the highest priority process to free up the discarded memory, no matter how bad the timing, and become a blocking process while it cleans things up. Obviously, this isn't cool.
So basically, you need to code with these things in mind and look into the documentation for the languages you are using for the best practices/patterns that allow you to avoid/mitigate this risk.
As in Ashs' post, in .Net and with strings, the recommended practice is to use the mutable StringBuilder class, rather than the immutable string classes when it comes to the need to constantly change a strings value.
Other languages/types will similarly have their own workarounds.
Why Immutability?
They are less prone to error and are more secure.
Immutable classes are easier to design, implement, and use than mutable classes.
Immutable objects are thread-safe so there is no synchronization issues.
Immutable objects are good Map keys and Set elements, since these typically do not change once created.
Immutability makes it easier to write, use and reason about the code (class invariant is established once and then unchanged).
Immutability makes it easier to parallelize program as there are no conflicts among objects.
The internal state of program will be consistent even if you have exceptions.
References to immutable objects can be cached as they are not going to change.(i.e in Hashing it provide fast operations).
See my blog for a more detailed answer:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html
Look, I haven't read the links that you have posted.
However, here is my understanding.
Every program holds some knowledge of it's data (State), which can change either by user-input/external changes etc.
Variables (values which change) are kept to maintain state. Immutable means some data which doesn't change. You can say, it is same as readonly or constant in some way (it can be seen it that way).
AFAIK, functional programming has things immutable (i.e. you cannot use assignment to a variable holding the value. What you can do is create another variable which can hold the original value + changes).
.net has string class which is an example.
i.e. You can't modify string in its place
string s = "hello"; I can write s.Replace("el", "a"); But this won't modify the contents of variable s.
What I can do is s = s.Replace("el","a");
This will create a new variable & assign its value to s (overwriting content of s).
Experts can correct mistakes if I have, in my understanding.
EDIT: Immutable = Unassignable once it is holding some value & can't be replaced in place (maybe?)
An example of the potential performance benefits offered by immutable objects is available in the WPF API. A common base class of many WPF types is Freezable
.
Several WPF examples suggest that freezing objects (making them immutable at runtime) can improve application performance significantly as locking and copying is not required.
Personally I wish the concept of immutability was easier to express in the language I use most often, C#. There is a readonly
modifier available for fields. I would like to see a readonly
modifier on types as well that would only be allowed for types that only have readonly fields that are of readonly types. Essentially this means that all state would need to be injected at construction time, and that and entire object graph would be frozen. I imagine that were this metadata intrinsic to the CLR then it could be easily used to optimise garbage analysis for GC.
Sorry, why does immutability prevent race conditions (in this example, write after read hazards)?
shared v = Integer(3)
v = Integer(v.value() + 1) # in parallel
Immutability is about values, and values are about facts. Something has value if is unchangeable, because if something can be changed, then it means that no specific value can be connected to it. Object was initialized with state A and during program execution was mutated to state B and state C. It means that object doesn't represent single specific value but is only a container, abstraction on a place in the memory, nothing more. You can't have trust to such container, you can not believe that this container has the value you suppose should have.
Let's go to example - lets imagine that in code is created instance of Book class.
Book bookPotter = new Book();
bookPotter.setAuthor('J.K Rowling');
bookPotter.setTitle('Harry Potter');
This instance has some fields set like author and title. Everything is ok, but in some part of the code again setters are used.
Book bookLor = bookPotter; // only reference pass
bookLor.setAuthor('J.R.R Tolkien');
bookLor.setTitle('Lords of The Rings');
Don't be cheated by different variable name, really it is the same instance. The code is using setters on the same instance again. It means that bookPotter was never really the Harry Potter book, bookPotter is only pointer to the place where unknown book is located. That said, it looks like it is more a shelf then the book. What trust you can have to such object? It is Harry Potter book or LoR book or neither?
Mutable instance of a class is only a pointer to an unknown state with the class characteristics.
How then avoid mutation? It is quite easy in rules:
- construct object with wanted state via constructor or builder
- don't create setters for encapsulated state of the object
- don't change any encapsulated state of the object in any of its methods
These few rules will allow to have more predictable and more reliable objects. Back to our example and book following above rules:
Book bookPotter = new Book('J.K Rowling', 'Harry Potter');
Book bookLor = new Book('J.R.R Tolkien', 'Lord of The Rings');
Everything is set during constructing phase, in this case constructor, but for bigger structures it can be a builder. No setters exist in objects, book can not mutate to different one. In such case bookPotter represents value of Harry Potter book and you can be sure that this is unchangeable fact.
If you are interested in wider scope of immutability, in this medium article is more about the subject in relation to JavaScript - https://medium.com/@macsikora/the-state-of-immutability-169d2cd11310.
ReferenceURL : https://stackoverflow.com/questions/622664/what-is-immutability-and-why-should-i-worry-about-it
'program story' 카테고리의 다른 글
PHP 세션 데이터가 저장되지 않음 (0) | 2020.12.30 |
---|---|
패턴 매칭 대 스위치 설명 (0) | 2020.12.30 |
GitHub 저장소 백업 (0) | 2020.12.30 |
python3.3에서 docx를 가져올 때 ImportError : No module named 'exceptions'오류가 있습니다. (0) | 2020.12.29 |
Spring MVC @RestController 및 리디렉션 (0) | 2020.12.29 |