가변 대 불변 개체
변경 가능한 객체와 불변의 객체 주위에 머리를 갖으려고합니다. 가변 객체를 사용하면 프레스에서 많은 나쁜 프레스 (예 : 메소드에서 문자열 배열 반환)가 발생하지만 부정적인 영향이 무엇인지 이해하는 데 어려움을 겪고 있습니다. 가변 객체 사용에 대한 모범 사례는 무엇입니까? 가능할 때마다 피해야합니까?
글쎄, 이것에는 몇 가지 측면이 있습니다. 첫째, 참조 ID가없는 변경 가능한 객체는 이상한 시간에 버그를 일으킬 수 있습니다. 예를 들어, Person
값 기반 equals
메소드가 있는 Bean을 고려하십시오 .
Map<Person, String> map = ...
Person p = new Person();
map.put(p, "Hey, there!");
p.setName("Daniel");
map.get(p); // => null
Person
인스턴스는 키로 사용될 때 맵에서 "잃어버린"상태를 유지합니다 . 인스턴스가 hashCode
동일하고 변경 가능한 값을 기반으로 했기 때문 입니다. 이러한 값은 맵 외부에서 변경되었으며 모든 해싱은 더 이상 사용되지 않습니다. 이론가들은이 시점에서 하프를 좋아하지만 실제로는 그다지 큰 문제가 아니라고 생각했습니다.
또 다른 측면은 코드의 논리적 "합리성"입니다. 가독성부터 흐름까지 모든 것을 포괄하는 정의하기 어려운 용어입니다. 일반적으로 코드 조각을보고 코드의 기능을 쉽게 이해할 수 있어야합니다. 그러나 그보다 더 중요한 것은 자신이 올바르게 작동한다는 것을 스스로에게 확신시킬 수 있어야 합니다 . 서로 다른 코드 "도메인"에서 개체가 독립적으로 변경 될 수있는 경우, 위치와 이유 ( "원격에서 어리석은 동작")를 추적하기가 어려운 경우가 있습니다. 이것은 설명하기가 더 어려운 개념이지만 더 크고 복잡한 아키텍처에서 종종 직면하는 것입니다.
마지막으로, 변경 가능한 객체는 동시 상황에서 킬러 입니다. 별도의 스레드에서 변경 가능한 객체에 액세스 할 때마다 잠금을 처리해야합니다. 이 처리량 감소하고 코드를 만드는 극적으로 더 어려워 유지 할 수 있습니다. 충분히 복잡한 시스템은이 문제를 일정 비율로 날려 유지하기가 거의 불가능 해집니다 (동시성 전문가 인 경우에도).
불변 개체 (특히 불변 컬렉션)는 이러한 모든 문제를 피합니다. 코드의 작동 방식에 대해 염두에두면 코드는 읽기 쉽고 유지 관리가 쉽고 이상한 방식으로 예측할 수없는 방식으로 개발됩니다. 불변의 객체는 쉽게 조롱 할 수있을뿐만 아니라 실행하려는 코드 패턴으로 인해 테스트하기도 더 쉽습니다. 요컨대, 그들은 모든 주위에 좋은 연습입니다!
그 말로, 나는이 문제에 열심이 아닙니다. 모든 것이 불변 인 경우 일부 문제는 잘 모델링되지 않습니다. 그러나 나는 당신이 이것을 가능한 견해로 만드는 언어를 사용한다고 가정 할 때 가능한 한 많은 방향으로 코드를 푸시하려고 노력해야한다고 생각합니다 (C / C ++은 Java와 마찬가지로 이것을 어렵게 만듭니다) . 요컨대, 장점은 문제에 다소 달려 있지만 불변성을 선호하는 경향이 있습니다.
불변 객체와 불변 콜렉션
불변성 객체와 불변성 객체에 대한 토론에서 더 좋은 점 중 하나는 불변성의 개념을 컬렉션으로 확장 할 수 있다는 것입니다. 불변 객체는 종종 데이터의 단일 논리 구조 (예 : 불변 문자열)를 나타내는 객체입니다. 불변 개체에 대한 참조가 있으면 개체의 내용이 변경되지 않습니다.
불변 컬렉션은 절대 변경되지 않는 컬렉션입니다.
변경 가능한 컬렉션에서 작업을 수행하면 컬렉션을 적절히 변경하고 컬렉션을 참조하는 모든 엔터티가 변경 사항을 볼 수 있습니다.
변경 불가능한 콜렉션에서 조작을 수행하면 변경 사항을 반영하는 참조가 새 콜렉션으로 리턴됩니다. 이전 버전의 컬렉션에 대한 참조가있는 모든 엔터티에는 변경 내용이 표시되지 않습니다.
영리한 구현은 불변성을 제공하기 위해 전체 컬렉션을 복사 (복제) 할 필요는 없습니다. 가장 간단한 예는 단일 연결 목록 및 푸시 / 팝 작업으로 구현 된 스택입니다. 새 컬렉션의 이전 컬렉션에서 모든 노드를 재사용하여 푸시에 단일 노드 만 추가하고 팝에 대한 노드는 복제하지 않을 수 있습니다. 반면에, 단독으로 연결된 목록에 대한 push_tail 작업은 그렇게 간단하거나 효율적이지 않습니다.
불변 변수와 가변 변수 / 참조
일부 기능적 언어는 객체 참조 자체에 대한 불변성의 개념을 취하여 단일 참조 할당 만 허용합니다.
- Erlang에서는 모든 "변수"에 해당됩니다. 객체를 참조에 한 번만 할당 할 수 있습니다. 컬렉션에서 작업 할 경우 새 컬렉션을 이전 참조 (변수 이름)에 다시 할당 할 수 없습니다.
- 스칼라는 또한 모든 참조가 var 또는 val 으로 선언되고 , val 은 단일 지정이고 기능적 스타일을 홍보하지만보다 C 형 또는 Java 형 프로그램 구조를 허용하는 vars 로 언어에이를 빌드합니다 .
- var / val 선언이 필요하지만 많은 전통적인 언어는 final in java 및 const const in C 와 같은 선택적 수정자를 사용합니다 .
손쉬운 개발 및 성능
거의 항상 불변의 객체를 사용하는 이유는 부작용이없는 프로그래밍과 코드에 대한 간단한 추론을 촉진하기위한 것입니다 (특히 동시 / 병렬 환경에서). 객체가 불변 인 경우 다른 엔터티가 기본 데이터를 변경하는 것에 대해 걱정할 필요가 없습니다.
주요 단점은 성능입니다. 다음은 장난감 문제에서 변경 불가능한 객체와 변경 불가능한 객체를 비교하여 Java 에서 수행 한 간단한 테스트 에 대한 글입니다.
성능 문제는 많은 응용 프로그램에서 문제가 될 수 있지만 전부는 아닙니다. 따라서 Python의 Numpy Array 클래스와 같은 많은 큰 숫자 패키지가 큰 배열의 내부 업데이트를 허용합니다. 이는 대규모 행렬 및 벡터 연산을 사용하는 응용 분야에 중요합니다. 이처럼 큰 데이터 병렬 및 계산 집약적 인 문제는 제자리에서 작동하여 속도를 크게 향상시킵니다.
이 블로그 게시물을 확인하십시오 : http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html . 불변의 객체가 변경 가능한 것보다 나은 이유를 설명합니다. 한마디로 :
- 불변 개체는 구성, 테스트 및 사용이 더 간단합니다.
- 진정한 불변의 객체는 항상 스레드 안전
- 그들은 시간적 결합을 피하는 데 도움이됩니다
- 그들의 사용법은 부작용이 없습니다 (방어 사본 없음)
- 신원 변경 가능성 문제를 피할 수 있습니다
- they always have failure atomicity
- they are much easier to cache
Immutable objects are a very powerful concept. They take away a lot of the burden of trying to keep objects/variables consistent for all clients.
You can use them for low level, non-polymorphic objects - like a CPoint class - that are used mostly with value semantics.
Or you can use them for high level, polymorphic interfaces - like an IFunction representing a mathematical function - that is used exclusively with object semantics.
Greatest advantage: immutability + object semantics + smart pointers make object ownership a non-issue, all clients of the object have their own private copy by default. Implicitly this also means deterministic behavior in the presence of concurrency.
Disadvantage: when used with objects containing lots of data, memory consumption can become an issue. A solution to this could be to keep operations on an object symbolic and do a lazy evaluation. However, this can then lead to chains of symbolic calculations, that may negatively influence performance if the interface is not designed to accommodate symbolic operations. Something to definitely avoid in this case is returning huge chunks of memory from a method. In combination with chained symbolic operations, this could lead to massive memory consumption and performance degradation.
So immutable objects are definitely my primary way of thinking about object-oriented design, but they are not a dogma. They solve a lot of problems for clients of objects, but also create many, especially for the implementers.
You should specify what language you're talking about. For low-level languages like C or C++, I prefer to use mutable objects to conserve space and reduce memory churn. In higher-level languages, immutable objects make it easier to reason about the behavior of the code (especially multi-threaded code) because there's no "spooky action at a distance".
A mutable object is simply an object that can be modified after it's created/instantiated, vs an immutable object that cannot be modified (see the Wikipedia page on the subject). An example of this in a programming language is Pythons lists and tuples. Lists can be modified (e.g., new items can be added after it's created) whereas tuples cannot.
I don't really think there's a clearcut answer as to which one is better for all situations. They both have their places.
If a class type is mutable, a variable of that class type can have a number of different meanings. For example, suppose an object foo
has a field int[] arr
, and it holds a reference to a int[3]
holding the numbers {5, 7, 9}. Even though the type of the field is known, there are at least four different things it can represent:
A potentially-shared reference, all of whose holders care only that it encapsulates the values 5, 7, and 9. If
foo
wantsarr
to encapsulate different values, it must replace it with a different array that contains the desired values. If one wants to make a copy offoo
, one may give the copy either a reference toarr
or a new array holding the values {1,2,3}, whichever is more convenient.The only reference, anywhere in the universe, to an array which encapsulates the values 5, 7, and 9. set of three storage locations which at the moment hold the values 5, 7, and 9; if
foo
wants it to encapsulate the values 5, 8, and 9, it may either change the second item in that array or create a new array holding the values 5, 8, and 9 and abandon the old one. Note that if one wanted to make a copy offoo
, one must in the copy replacearr
with a reference to a new array in order forfoo.arr
to remain as the only reference to that array anywhere in the universe.A reference to an array which is owned by some other object that has exposed it to
foo
for some reason (e.g. perhaps it wantsfoo
to store some data there). In this scenario,arr
doesn't encapsulate the contents of the array, but rather its identity. Because replacingarr
with a reference to a new array would totally change its meaning, a copy offoo
should hold a reference to the same array.A reference to an array of which
foo
is the sole owner, but to which references are held by other object for some reason (e.g. it wants to have the other object to store data there--the flipside of the previous case). In this scenario,arr
encapsulates both the identity of the array and its contents. Replacingarr
with a reference to a new array would totally change its meaning, but having a clone'sarr
refer tofoo.arr
would violate the assumption thatfoo
is the sole owner. There is thus no way to copyfoo
.
In theory, int[]
should be a nice simple well-defined type, but it has four very different meanings. By contrast, a reference to an immutable object (e.g. String
) generally only has one meaning. Much of the "power" of immutable objects stems from that fact.
If you return references of an array or string, then outside world can modify the content in that object, and hence make it as mutable (modifiable) object.
Immutable means can't be changed, and mutable means you can change.
Objects are different than primitives in Java. Primitives are built in types (boolean, int, etc) and objects (classes) are user created types.
Primitives and objects can be mutable or immutable when defined as member variables within the implementation of a class.
A lot of people people think primitives and object variables having a final modifier infront of them are immutable, however, this isn't exactly true. So final almost doesn't mean immutable for variables. See example here
http://www.siteconsortium.com/h/D0000F.php.
Mutable instances is passed by reference.
Immutable instances is passed by value.
Abstract example. Suppose that exist a file named txtfile in my HDD. Now, when you ask txtfile from me, I can return it in two modes:
- Create a shortcut to txtfile and pas shortcut to you, or
- Take a copy for txtfile and pas copy to you.
In first mode, returned txtfile is a mutable file, because when you do changes in shortcut file, you do changes in original file too. Advantage of this mode is that each returned shortcut required less memory (on RAM or in HDD) and disadvantage is that everyone (not only me, owner) have permissions to modify file content.
In second mode, returned txtfile is an immutable file, because all changes in received file does not refer to the original file. Advantage of this mode is that only me (owner) can modify original file and disadvantage is that each returned copy required memory (in RAM or in HDD).
참고URL : https://stackoverflow.com/questions/214714/mutable-vs-immutable-objects
'Programing' 카테고리의 다른 글
SVN, Branch를 사용하는 방법? (0) | 2020.06.01 |
---|---|
ActionBarActivity에 비해 AppCompatActivity의 향상된 기능은 무엇입니까? (0) | 2020.06.01 |
ASP.NET Identity의 IUserSecurityStampStore 란 무엇입니까 (0) | 2020.06.01 |
FragmentPagerAdapter는 Android.Support.V4.App에만 있고 Android.App에는 없습니다. (0) | 2020.06.01 |
std :: decay는 무엇이며 언제 사용해야합니까? (0) | 2020.06.01 |