불변 객체가 좋은 경우 왜 사람들이 가변 객체를 계속 생성합니까? [닫은]


250

불변의 객체 ¹가 좋고 간단하고 동시 프로그래밍에서 이점을 제공하는 경우 프로그래머가 왜 가변적 인 객체를 계속 만드는가?

Java 프로그래밍에 4 년의 경험이 있으며 클래스를 만든 후 사람들이 IDE에서 getter 및 setter를 생성하는 것이 가장 중요합니다 (따라서 변경 가능). 인식이 부족하거나 대부분의 시나리오에서 가변 객체를 사용하여 벗어날 수 있습니까?


¹ 불변 개체 는 생성 된 후 상태를 수정할 수없는 개체입니다.
² 가변 객체 는 생성 된 후에 수정할 수있는 객체입니다.


42
아래의 Péter에서 언급 한 합법적 인 이유와는 달리 "게으른 개발자"는 "멍청한"개발자보다 일반적인 이유이며 "멍청한 개발자"이전에는 "알지 못하는 개발자"도 있습니다.
Joachim Sauer

201
모든 복음주의 프로그래머 / 블로거에게 1000 명의 열렬한 블로그 독자가 있습니다. 즉시 독자를 재창조하고 최신 기술을 채택합니다. 모든 사람들에게 1 만 명의 작업을 수행하고 제품을 문 밖으로 가져 오는 연삭 석에 코가있는 10,000 명의 프로그래머가 있습니다. 그 사람들은 몇 년 동안 노력해 왔으며 신뢰할 수있는 기술을 사용하고 있습니다. 새로운 기술이 널리 채택 될 때까지 기다렸다가 실제 이점을 보여주기 전에 채택합니다. 그들을 바보라고 부르지 말고 게으른 게 아니라 대신 "바쁘다"고 부르십시오.
이진 걱정

22
@BinaryWorrier : 불변 개체는 "새로운 것"이 아닙니다. 도메인 객체에 많이 사용되지는 않았지만 Java와 C #은 처음부터 사용했습니다. 또한 : "게으른"은 항상 나쁜 단어는 아니며, 어떤 종류의 "게으른"은 개발자에게 절대적인 이점입니다.
Joachim Sauer 2016

11
@Joachim : "lazy"가 위의 중추적 인 의미로 사용되었다는 것이 상당히 명백하다고 생각합니다. 갑자기 달의 풍미가됩니다 . 나는 그들이 나쁜 일이라고 주장하지 않으며 (그렇지 않다), 그들은 자신의 자리를 갖지 못하고 (명백히 그렇게합니다), 사람들은 최신 좋은 말씀을 듣지 못했기 때문에 사람들에게 쉽게 가십시오. "게으른"의견에 대해 당신을 탓하지 않고, 나는 그것을 완화 시키려고 노력했다는 것을 알고 있습니다.
이진 걱정

116
-1, 불변 개체는 '좋은'것이 아닙니다. 특정 상황에 다소 적합합니다. 어떤 기술이든 다른 기술을 말하는 사람은 모든 상황에서 다른 기술보다 객관적으로 '좋거나'나쁘다.
GrandmasterB

답변:


326

가변 및 불변 객체는 모두 고유 한 용도, 장단점을 갖습니다.

불변의 객체는 실제로 인생을 더 단순하게 만듭니다. 그것들은 특히 객체가 아이덴티티가 없어 쉽게 교체 될 수있는 값 유형에 적용 할 수 있습니다. 또한 동시 프로그래밍 방식을보다 안전하고 깨끗하게 만들 수 있습니다 (대부분의 동시성 버그는 스레드간에 공유되는 변경 가능한 상태로 인해 발생합니다). 그러나 크고 복잡한 개체의 경우 모든 단일 변경에 대해 새 개체 복사본을 만드는 것은 비용이 많이 들고 지루할 수 있습니다. 또한 고유 한 ID를 가진 개체의 경우 기존 개체를 변경하는 것이 새롭고 수정 된 복사본을 만드는 것보다 훨씬 간단하고 직관적입니다.

게임 캐릭터를 생각해보십시오. 게임에서 속도는 최우선 순위이므로, 변경 가능한 오브젝트로 게임 캐릭터를 표현하면 거의 변경 될 때마다 게임 캐릭터의 새로운 사본이 생성되는 대체 구현보다 훨씬 빠르게 게임을 실행할 수 있습니다.

또한 현실 세계에 대한 우리의 인식은 필연적으로 변경 가능한 객체에 기반합니다. 주유소에서 연료를 차에 채우면 빈 탱크가있는 낡은 차가 연속 된 새 차로 바뀌지 않은 것처럼 자동차가 같은 물체로 인식됩니다 (즉 , 상태 가 변경 되는 동안 동일성 이 유지됨 ). 탱크가 점점 더 가득 차있는 자동차 인스턴스. 따라서 프로그램에서 실제 도메인을 모델링 할 때마다 변경 가능한 객체를 사용하여 실제 엔터티를 나타내는 도메인 모델을 구현하는 것이 더 간단하고 쉽습니다.

이러한 합법적 인 이유와는 별도로, 사람들이 가변 객체를 계속 생성하는 가장 가능성이 높은 원인은 관성의 마음, 즉 변화에 대한 저항입니다. 오늘날 대부분의 개발자는 불변성 (및 포함하는 패러다임, 함수형 프로그래밍)이 영향력의 영역에서 "유행"이되기 전에 잘 훈련을 받았으며 거래의 새로운 도구와 방법에 대한 최신 정보를 유지하지 않습니다. 실제로, 우리 인간 대부분은 새로운 아이디어와 과정에 긍정적으로 저항 합니다. "저는 nn 년 동안 이와 같은 프로그래밍을 해왔 으며 최신 바보 같은 유행은 신경 쓰지 않습니다!"


27
맞습니다. 특히 GUI 프로그래밍에서 가변 객체는 매우 편리합니다.
Florian Salihovic

23
그것은 단지 저항이 아니며, 많은 개발자들이 최신의 가장 위대한 것을 시험해보고 싶어하지만, 새로운 개발자들이이 새로운 관행을 적용 할 수있는 평균적인 개발자 환경에서 얼마나 자주 등장하는지? 불변 상태를 시험해보기 위해 취미 프로젝트를 작성할 수있는 사람도 없을 것입니다.
Steven Evers

29
이것에 대한 두 가지 작은 경고 : (1) 움직이는 게임 캐릭터를 가져 가라. Point예를 들어 .NET 의 클래스는 변경할 수 없지만 변경의 결과로 새 포인트를 작성하는 것이 쉽고 저렴합니다. “이동 부품”을 분리하여 불변의 캐릭터에 애니메이션을 적용하는 것은 매우 저렴합니다 (그러나 일부 측면은 변경 가능합니다). (2) "대형 및 / 또는 복잡한 대상"은 불변 일 수 있습니다. 문자열은 종종 크며 일반적으로 불변성의 이점이 있습니다. 한때 불변의 복잡한 그래프 클래스를 다시 작성하여 코드를 더 간단하고 효율적으로 만들었습니다. 이러한 경우 변경 가능한 빌더를 갖는 것이 핵심입니다.
Konrad Rudolph

4
@KonradRudolph, 좋은 지적, 감사합니다. 복잡한 객체에서 불변성을 사용하는 것을 배제하려는 것은 아니지만 그러한 클래스를 정확하고 효율적으로 구현하는 것은 사소한 작업과는 거리가 멀고 필요한 추가 노력이 항상 정당화되는 것은 아닙니다.
Péter Török

6
당신은 국가 대 정체성에 대해 좋은 지적을합니다. 이것이 Clojure의 저자 인 Rich Hickey가 Clojure에서 둘을 깨뜨린 이유입니다. 1/2 탱크의 가스를 가진 자동차가 1/4 탱크의 자동차를 가진 자동차와 같지 않다고 주장 할 수 있습니다. 그것들은 같은 정체성을 가지고 있지만, 동일하지는 않습니다. 현실의 모든 "틱"은 세상의 모든 물체의 복제물을 만들고, 두뇌는 단순히 공통의 정체성과 함께 이들을 연결합니다. Clojure에는 시간을 나타내는 참조, 원자, 에이전트 등이 있습니다. 그리고 실제 시간에 대한지도, 벡터 및 목록.
Timothy Baldridge

130

나는 당신이 가장 분명한 대답을 놓쳤다 고 생각합니다. 가변성은 기본 언어의 기본값이므로 대부분의 개발자는 가변 객체를 만듭니다. 대부분의 사람들은 코드를 기본 설정에서 지속적으로 수정하는 것보다 더 정확하거나 그렇지 않은 것보다 시간과 관련이 있습니다. 그리고 불변성은 다른 어떤 접근법보다 만병 통치약이 아닙니다. 일부 답변이 이미 지적했듯이 일부 일이 쉬워 지지만 다른 일이 훨씬 어려워집니다.


9
내가 아는 대부분의 최신 IDE에서는 게터와 세터를 모두 생성하는 것과 마찬가지로 게터 만 생성하는 데 실질적으로 동일한 노력이 필요합니다. 이 추가 것은 사실이지만 final, const등 여분의 노력을 조금 걸립니다 ... 당신은 코드 템플릿 :-)를 설정하지 않는 한
페테르 토록

10
@ PeterTörök 추가 노력이 아닙니다. 동료 코딩 담당자가 코딩 스타일이 자신의 경험에 매우 친숙하다는 사실을 알게되어 효율적으로 교수형에 처하게 될 것입니다. 그것은 또한 이런 종류의 것을 방해합니다.
Onorio Catenacci

5
더 많은 의사 소통과 교육으로 극복 할 수 있습니다. 예를 들어, 기존 코드베이스에 실제로 도입하기 전에 팀원들에게 새로운 코딩 스타일에 대해 이야기하거나 프레젠테이션하는 것이 좋습니다. 좋은 프로젝트는 공통의 코딩 스타일을 갖는 것이 사실인데, 이는 모든 (과거와 현재의) 프로젝트 멤버가 선호하는 코딩 스타일의 합병이 아닙니다. 따라서 코딩 관용구를 도입하거나 변경하는 것은 팀의 결정이어야합니다.
Péter Török

3
@ PéterTörök 명령형 언어에서 가변 객체는 기본 동작입니다. 변경 불가능한 객체 만 원하는 경우 기능 언어로 전환하는 것이 가장 좋습니다.
emory

3
@ PéterTörök 불변성을 프로그램에 포함시키는 것은 모든 세터를 버리는 것 외에는 아무것도 없다고 가정하는 것은 조금 순진합니다. 여전히 프로그램 상태가 점진적으로 변경 될 수있는 방법이 필요하며,이를 위해서는 빌더 또는 아이스 불변성 또는 변경 가능한 프록시가 필요합니다.이 모두는 세터를 삭제하는 데 걸리는 노력의 약 10 억 배가 걸립니다.
Asad Saeeduddin

49
  1. 변경 가능성이 있습니다. 도메인 기반 설계 원칙은 변경 가능해야하는 것과 변경 불가능한 것에 대한 확실한 이해를 제공합니다. 당신이 그것에 대해 생각한다면, 당신은 물체에 대한 모든 상태의 변화가 그것을 파괴하고 재구성해야하는 시스템과 그것을 참조하는 모든 물체에 대해 생각하는 것은 비현실적이라는 것을 알게 될 것입니다. 복잡한 시스템을 사용하면 전체 시스템의 객체 그래프를 완전히 지우고 다시 만들 수 있습니다.

  2. 대부분의 개발자는 동시성 (또는 정보에 의해 일반적으로 모범 사례로 간주되는 다른 많은 문제)에 중점을 두어야 할 정도로 성능 요구 사항이 중요한 곳에서는 아무것도 만들지 않습니다.

  3. 양방향 관계와 같이 불변의 객체로는 단순히 할 수없는 일이 있습니다. 한 개체에 연결 값을 설정하면 ID가 변경됩니다. 따라서 다른 개체에 새 값을 설정하면 변경됩니다. 문제는 첫 번째 객체의 참조가 더 이상 유효하지 않다는 것입니다. 참조가있는 객체를 나타내는 새 인스턴스가 만들어 졌기 때문입니다. 계속하면 무한 회귀가 발생합니다. 귀하의 질문을 읽은 후 약간의 사례 연구를 수행했습니다. 불변성을 유지하면서 그러한 기능을 허용하는 대체 방법이 있습니까?

        public class ImmutablePerson { 
    
         public ImmutablePerson(string name, ImmutableEventList eventsToAttend)
         {
              this.name = name;
              this.eventsToAttend = eventsToAttend;
         }
         private string name;
         private ImmutableEventList eventsToAttend;
    
         public string Name { get { return this.name; } }
    
         public ImmutablePerson RSVP(ImmutableEvent immutableEvent){
             // the person is RSVPing an event, thus mutating the state 
             // of the eventsToAttend.  so we need a new person with a reference
             // to the new Event
             ImmutableEvent newEvent = immutableEvent.OnRSVPReceived(this);
             ImmutableEventList newEvents = this.eventsToAttend.Add(newEvent));
             var newSelf = new ImmutablePerson(name, newEvents);
             return newSelf;
         }
        }
    
        public class ImmutableEvent { 
         public ImmutableEvent(DateTime when, ImmutablePersonList peopleAttending, ImmutablePersonList peopleNotAttending){
             this.when = when;     
             this.peopleAttending = peopleAttending;
             this.peopleNotAttending = peopleNotAttending;
         }
         private DateTime when; 
         private ImmutablePersonList peopleAttending;
         private ImmutablePersonList peopleNotAttending;
         public ImmutableEvent OnReschedule(DateTime when){
               return new ImmutableEvent(when,peopleAttending,peopleNotAttending);
         }
         //  notice that this will be an infinite loop, because everytime one counterpart
         //  of the bidirectional relationship is added, its containing object changes
         //  meaning it must re construct a different version of itself to 
         //  represent the mutated state, the other one must update its
         //  reference thereby obsoleting the reference of the first object to it, and 
         //  necessitating recursion
         public ImmutableEvent OnRSVPReceived(ImmutablePerson immutablePerson){
               if(this.peopleAttending.Contains(immutablePerson)) return this;
               ImmutablePersonList attending = this.peopleAttending.Add(immutablePerson);
               ImmutablePersonList notAttending = this.peopleNotAttending.Contains( immutablePerson ) 
                                    ? peopleNotAttending.Remove(immutablePerson)
                                    : peopleNotAttending;
               return new ImmutableEvent(when, attending, notAttending);
         }
        }
        public class ImmutablePersonList
        {
          private ImmutablePerson[] immutablePeople;
          public ImmutablePersonList(ImmutablePerson[] immutablePeople){
              this.immutablePeople = immutablePeople;
          }
          public ImmutablePersonList Add(ImmutablePerson newPerson){
              if(this.Contains(newPerson)) return this;
              ImmutablePerson[] newPeople = new ImmutablePerson[immutablePeople.Length];
              for(var i=0;i<immutablePeople.Length;i++)
                  newPeople[i] = this.immutablePeople[i];
              newPeople[immutablePeople.Length] = newPerson;
          }
          public ImmutablePersonList Remove(ImmutablePerson newPerson){
              if(immutablePeople.IndexOf(newPerson) != -1)
              ImmutablePerson[] newPeople = new ImmutablePerson[immutablePeople.Length-2];
              bool hasPassedRemoval = false;
              for(var i=0;i<immutablePeople.Length;i++)
              {
                 hasPassedRemoval = hasPassedRemoval || immutablePeople[i] == newPerson;
                 newPeople[i] = this.immutablePeople[hasPassedRemoval ? i + 1 : i];
              }
              return new ImmutablePersonList(newPeople);
          }
          public bool Contains(ImmutablePerson immutablePerson){ 
             return this.immutablePeople.IndexOf(immutablePerson) != -1;
          } 
        }
        public class ImmutableEventList
        {
          private ImmutableEvent[] immutableEvents;
          public ImmutableEventList(ImmutableEvent[] immutableEvents){
              this.immutableEvents = immutableEvents;
          }
          public ImmutableEventList Add(ImmutableEvent newEvent){
              if(this.Contains(newEvent)) return this;
              ImmutableEvent[] newEvents= new ImmutableEvent[immutableEvents.Length];
              for(var i=0;i<immutableEvents.Length;i++)
                  newEvents[i] = this.immutableEvents[i];
              newEvents[immutableEvents.Length] = newEvent;
          }
          public ImmutableEventList Remove(ImmutableEvent newEvent){
              if(immutableEvents.IndexOf(newEvent) != -1)
              ImmutableEvent[] newEvents = new ImmutableEvent[immutableEvents.Length-2];
              bool hasPassedRemoval = false;
              for(var i=0;i<immutablePeople.Length;i++)
              {
                 hasPassedRemoval = hasPassedRemoval || immutableEvents[i] == newEvent;
                 newEvents[i] = this.immutableEvents[hasPassedRemoval ? i + 1 : i];
              }
              return new ImmutableEventList(newPeople);
          }
          public bool Contains(ImmutableEvent immutableEvent){ 
             return this.immutableEvent.IndexOf(immutableEvent) != -1;
          } 
        }
    

2
@AndresF., 불변의 객체 만 사용하여 양방향 관계로 복잡한 그래프를 유지하는 방법에 대해 다른 의견을 가지고 있다면 듣고 싶습니다. (나는 우리가 수집 / 배열 객체임을 동의 할 수있는 가정)
smartcaveman

2
@AndresF., (1) 첫 번째 진술은 보편적이지 않았으므로 거짓이 아닙니다. 실제로 응용 프로그램 개발에서 매우 일반적으로 발생하는 특정 경우에 어떻게 사실인지 설명하는 코드 예제를 제공했습니다. (2) 양방향 관계는 일반적으로 변경이 필요합니다. Java가 범인이라고 믿지 않습니다. 내가 말했듯이, 나는 당신이 제안 할 건설 적인 대안 을 평가하게되어 기쁩니다 . 그러나이 시점에서 당신은 "당신이 그렇게 말했기 때문에 틀렸다"고 말하는 것처럼 들립니다.
smartcaveman

14
@smartcaveman (2)에 대해서도, 나는 동의하지 않는다. 일반적으로, "양방향 관계"는 변이성과 직교하는 수학적 개념이다. 일반적으로 Java로 구현되면 변경이 필요합니다 (이 시점에서 귀하와 동의했습니다). 그러나 대안 구현을 생각할 수 있습니다 Relationship(a, b). 생성자와 함께 두 객체 사이의 관계 클래스 ; 관계를 생성하는 시점에서 엔티티 ab이미 존재하며 관계 자체도 변경할 수 없습니다. 이 접근법이 자바에서는 실용적이지 않다. 가능하다는 것뿐입니다.
Andres F.

4
경우 당신이 무슨 말을하는지에 따라 @AndresF., 그래서 R는 IS Relationship(a,b)와 모두 ab불변, 어느 쪽 ab에 대한 참조를 개최 것이다 R. 이것이 작동하려면 참조가 정적 클래스와 같은 다른 곳에 저장되어야합니다. 당신의 의도를 정확하게 이해하고 있습니까?
smartcaveman

9
Chthulhu가 게으름을 통해 지적함에 따라 불변 데이터에 대한 양방향 관계를 저장할 수 있습니다. 방법은 다음과 같습니다. haskell.org/haskellwiki/Tying_the_Knot
Thomas Eding

36

필자는 "순전히 기능적인 데이터 구조"를 읽고 있으며, 가변 객체를 사용하여 구현하기가 훨씬 쉬운 데이터 구조가 몇 가지 있다는 것을 알게되었습니다.

이진 검색 트리를 구현하려면 매번 새 트리를 반환해야합니다. 새 트리는 수정 된 각 노드의 복사본을 만들어야했습니다 (수정되지 않은 분기는 공유 됨). 삽입 기능의 경우 이것은 나쁘지 않지만 삭제 및 재조정 작업을 시작했을 때 일이 빨리 비효율적이었습니다.

또 다른 사실은 객체 지향 코드를 작성하는 데 몇 년이 걸릴 수 있으며 동시성 문제가 발생할 수있는 방식으로 코드가 실행되지 않으면 공유 가능한 가변 상태가 얼마나 될 수 있는지 실제로 알지 못한다는 것입니다.


3
오카 사키 책인가요?
기계 달팽이

네. 좀 건조하지만 좋은 정보의 톤 ...
Paul Sanwald

4
재밌습니다. 저는 항상 오카 사키의 빨강 / 검은 나무가 훨씬 단순하다고 생각했습니다. 10 줄 정도 가장 큰 장점은 이전 버전을 실제로 유지하려는 경우입니다.
Thomas Ahle

마지막 문장은 과거에 사실 이었을지 모르지만 현재 하드웨어 추세 등을 고려할 때 미래에도 계속 될 것이라는 분명하지는 않습니다.
jk.

29

내 관점에서 볼 때, 그것은 인식의 부족입니다. 다른 알려진 JVM 언어 (Scala, Clojure)를 살펴보면 코드에서 변경 가능한 객체를 거의 볼 수 없으므로 단일 스레딩이 충분하지 않은 시나리오에서 사람들이 사용하기 시작합니다.

저는 현재 Clojure를 배우고 있으며 스칼라 (4 년 + Java에서도)에 대한 경험이 거의 없으며 국가 인식에 따라 코딩 스타일이 변경됩니다.


아마도 "인기"보다는 "알려진"이 더 나은 단어 선택 일 것입니다.
Den

네 맞습니다.
Florian Salihovic

5
+1 : 동의합니다 : 스칼라와 하스켈을 배우고 나면 Java에서 final을 사용하고 어디에서나 C ++에서 const를 사용하는 경향이 있습니다. 또한 가능한 경우 불변 객체를 사용하며, 가변 객체가 여전히 자주 필요하지만 불변 객체를 얼마나 자주 사용할 수 있는지는 놀랍습니다.
Giorgio

5
나는 2 년 반 후에 나의이 의견을 읽었으며, 나의 의견은 불변성에 찬성하여 바뀌었다. 현재 프로젝트 (Python에 있음)에서 가변 객체를 사용하는 경우는 거의 없습니다. 영구 데이터조차도 불변입니다. 일부 작업의 결과로 새 레코드를 만들고 더 이상 필요하지 않을 때 이전 레코드를 삭제하지만 디스크의 레코드는 업데이트하지 않습니다. 말할 것도없이, 동시 다중 사용자 응용 프로그램을 지금까지 구현하고 유지 관리하기가 훨씬 쉬워졌습니다.
Giorgio

13

나는 하나 개의 주요 기여 요인이 무시되었다고 생각 : 자바 콩 개체를 돌연변이, 그리고 (특히 소스 고려)의 특정 스타일에 크게 의존 꽤 많은 사람들이 걸릴 것 같다하는 (또는 심지어 의) 정식 예 어떻게 모든 자바 작성해야합니다.


4
+1, 게터 / 세터 패턴은 첫 번째 데이터 분석 후 일종의 기본 구현으로 너무 자주 사용됩니다.
Jaap

"아마도 다른 사람들이하고있는 방식이기 때문에"큰 문제 일 것입니다. "Hello World"프로그램의 경우 가장 쉬울 것입니다. 수많은 변경 가능한 속성을 통해 객체 상태 변경을 관리합니다. "Hello World"이해 수준보다 약간 까다 롭습니다. 20 년 후 저는 20 세기 프로그래밍의 정점에 구조가없는 객체의 모든 속성에 대해 getX 및 setX 메소드를 작성하는 것이 매우 놀랍습니다. 100 % 변경 가능성이있는 공공 자산에 직접 액세스하는 것보다 한 걸음 떨어져 있습니다.
Darrell Teague

12

직장에서 근무한 모든 엔터프라이즈 Java 시스템은 Hibernate 또는 Java Persistence API (JPA)를 사용합니다. Hibernate와 JPA는 본질적으로 시스템이 변경 가능한 객체를 사용하도록 지시합니다. 왜냐하면 그것들의 전체 전제는 데이터 객체의 변경을 감지하고 저장하는 것이기 때문입니다. 많은 프로젝트에서 Hibernate가 제공하는 개발의 용이성은 불변의 객체의 이점보다 더 매력적입니다.

분명히 변경 가능한 객체는 최대 절전 모드보다 훨씬 오래 사용되었으므로 최대 절전 모드는 변경 가능한 객체의 인기의 원래 원인이 아닐 수 있습니다. 변경 가능한 객체의 인기로 인해 최대 절전 모드가 번창 할 수있었습니다.

그러나 오늘날 많은 주니어 프로그래머가 Hibernate 또는 다른 ORM을 사용하여 엔터프라이즈 시스템에서 이빨을 자르면 아마도 가변 객체를 사용하는 습관을 들이게 될 것입니다. 최대 절전 모드와 같은 프레임 워크는 변경 가능한 개체의 인기를 끌고 있습니다.


훌륭한 지적입니다. 모든 사람에게 모든 것을 제공하기 위해, 그러한 프레임 워크는 선택의 여지가 거의 없지만 가장 낮은 공통 분모로 떨어지고, 반사 기반 프록시를 사용하고 유연성을 가지게됩니다. 물론 이것은 국가 전이 규칙이 거의 없거나 전혀없는 시스템을 만들거나 불행히도이를 구현할 수있는 일반적인 수단을 만듭니다. 나는 많은 프로젝트 이후에 편의성, 확장 성 및 정확성에 더 나은 것이 무엇인지 완전히 확신하지 못합니다. 나는 하이브리드를 생각하는 경향이 있습니다. 동적 ORM 양호하지만 어떤 필드가 필요하고 어떤 상태가 변경 될 수 있는지에 대한 정의가 있습니다.
Darrell Teague

9

아직 언급되지 않은 주요 요점은 객체의 상태를 변경 가능하게하면 해당 상태를 캡슐화하는 객체 의 ID 를 변경할 수 없다는 것입니다.

많은 프로그램은 본질적으로 변경 가능한 실제 사물을 모델링하도록 설계되었습니다. 오전 12시 51 분에 일부 변수 AllTrucks에 객체 # 451에 대한 참조가 있다고 가정합니다. 개체 # 451은 해당 시점의 모든 트럭에 어떤화물이 포함되어 있는지 (12:51 am) 데이터 구조의 근본이며 일부 변수는 BobsTruck객체 # 24601에 대한 참조를 얻는 데 사용할 수있는 것은 해당 순간에 밥 트럭에 어떤화물이 포함되어 있는지를 나타내는 객체를 가리 킵니다 (12:51 am). 오전 12:52에 일부 트럭 (Bob 포함) AllTrucks이로 드 및 언로드되고, 데이터 구조가 업데이트되어 화물이 모든 트럭에 있음을 나타내는 데이터 구조에 대한 참조를 보유합니다.

어떻게 BobsTruck됩니까?

각 트럭 개체의 '화물'속성이 변경 불가능한 경우 개체 # 24601은 Bob의 트럭이 오전 12시 51 분에 있었던 상태를 영원히 나타냅니다. BobsTruck객체 # 24601에 대한 직접 참조를 보유하고 있다면 업데이트하는 코드 AllTrucks도 업데이트되지 않으면 BobsTruckBob의 트럭의 현재 상태를 나타내는 것이 중단됩니다. 또한 BobsTruck어떤 형태의 변경 가능한 객체에 저장 되지 않는 한 업데이트하는 코드가 업데이트 AllTrucks할 수 있는 유일한 방법 은 코드가 명시 적으로 프로그래밍 된 경우입니다.

BobsTruck모든 물체를 불변으로 유지하면서 Bob의 트럭 상태를 관찰하는 데 사용할 수 있기를 원한다면 어떤 특정 시간에 있거나 가진 BobsTruck값을 고려할 때 불변의 함수 일 수 있습니다 AllTrucks. 그때. 하나는 불변의 함수 쌍을 가질 수도 있습니다. 그중 하나는 위와 같고 다른 하나는 함대 상태와 새로운 트럭 상태에 대한 참조를 수락하고 새로운 함대 상태에 대한 참조를 반환합니다 Bob의 트럭이 새로운 상태를 가질 것이라는 점을 제외하고는 이전과 일치했습니다.

불행히도, Bob의 트럭 상태에 접근하고자 할 때마다 그러한 기능을 사용해야하는 것은 다소 성 가시고 성 가실 수 있습니다. 대안적인 접근법은 객체 # 24601이 항상 그리고 영원히 (누군가가 그것에 대한 참조를 보유하는 한) Bob의 트럭 의 현재 상태를 나타낼 것이라고 말하는 것 입니다. Bob의 트럭의 현재 상태에 반복적으로 액세스하려는 코드는 매번 시간이 많이 걸리는 기능을 실행할 필요가 없습니다. 객체 # 24601이 Bob의 트럭이라는 것을 찾기 위해 검색 기능을 한 번만 수행하면됩니다. Bob의 트럭의 현재 상태를보고 싶을 때마다 해당 오브젝트에 액세스하십시오.

기능적 접근 방식은 단일 스레드 환경이나 스레드가 데이터를 변경하는 대신 주로 데이터를 관찰하는 다중 스레드 환경에서 이점이 없습니다. 에 포함 된 객체 참조를 복사하는 관찰자 스레드AllTrucks그런 다음 표시된 트럭 상태를 검사하여 참조를 잡은 순간 모든 트럭의 상태를 확인합니다. 옵저버 스레드가 최신 데이터를보고 싶을 때마다 참조를 다시 가져올 수 있습니다. 반면, 단일 불변 개체로 표현 된 전체 함대 상태를 갖는 것은 각 스레드가 자체 장치에 남겨두면 새로운 "함대 상태"개체를 생성하기 때문에 두 스레드가 서로 다른 트럭을 동시에 업데이트 할 가능성을 배제합니다. 트럭의 새로운 상태와 서로의 오래된 상태. 각 스레드가 변경되지 않은 경우에만 CompareExchange업데이트 AllTrucks하는 데 사용하고 실패에 응답하는 경우 정확성이 보장 될 수 있습니다.CompareExchange상태 오브젝트를 재생성하고 조작을 재 시도함으로써, 하나 이상의 스레드가 동시 쓰기 조작을 시도하면, 모든 쓰레드가 단일 스레드에서 수행 된 것보다 성능이 일반적으로 떨어집니다. 스레드가 이러한 동시 작업을 더 많이 시도할수록 성능이 저하됩니다.

개별 트럭 오브젝트가 변경 가능하지만 변경 불가능한 ID 가있는 경우 멀티 스레드 시나리오가 더 깨끗해집니다. 특정 트럭에서 한 번에 하나의 스레드 만 작동 할 수 있지만 다른 트럭에서 작동하는 스레드는 간섭없이 작동 할 수 있습니다. 불변 객체를 사용하는 경우에도 이러한 동작을 에뮬레이트 할 수있는 방법이 있지만 (예 : "AllTrucks"객체를 정의하여 XXX에 속하는 트럭의 상태를 SSS로 설정하려면 "As of [Time], [XXX]에 속하는 트럭의 상태는 이제 [SSS]입니다. 그 밖의 모든 상태는 [Old value of AllTrucks] "입니다. 이러한 개체를 생성하는 것은 경합이있을 때에도 충분히 빠릅니다.CompareExchange루프는 오래 걸리지 않을 것입니다. 다른 한편으로, 이러한 데이터 구조를 사용하면 특정 사람의 트럭을 찾는 데 필요한 시간이 실질적으로 증가합니다. 변경 불가능한 ID를 가진 변경 가능한 오브젝트를 사용 하면 해당 문제점을 피할 수 있습니다.


8

옳고 그름은 없습니다, 그것은 단지 당신이 선호하는 것에 달려 있습니다. 어떤 사람들은 한 패러다임을 선호하는 언어를 선호하고 한 데이터 모델은 다른 패러다임을 선호하는 이유가 있습니다. 그것은 당신의 선호와 당신이 달성하고자하는 것에 달려 있습니다.

귀하의 질문에 대답하는 가장 빠르고 빠른 방법 은 불변성 대 돌연변이 성의 장단점을 극복하는 것 입니다.


7

이 블로그 게시물을 확인하십시오 : http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html . 불변의 객체가 변경 가능한 것보다 나은 이유를 요약합니다. 다음은 간단한 인수 목록입니다.

  • 불변 개체는 구성, 테스트 및 사용이 더 간단합니다.
  • 진정한 불변의 객체는 항상 스레드 안전
  • 그들은 시간적 결합을 피하는 데 도움이됩니다
  • 그들의 사용법은 부작용이 없습니다 (방어 사본 없음)
  • 신원 변경 가능성 문제를 피할 수 있습니다
  • 그들은 항상 실패 원 자성을 가지고
  • 그들은 캐시하기가 훨씬 쉽다

사람들은 여전히 ​​OOP를 명령 식 절차 프로그래밍과 혼합하기 때문에 내가 이해하는 한 가변 객체를 사용하고 있습니다.


5

Java에서 불변의 객체는 객체의 모든 속성을 취하는 생성자가 필요합니다 (또는 생성자는 다른 인수 또는 기본값에서 생성합니다). 해당 속성 final 로 표시 되어야 합니다 .

데이터 바인딩 과 관련하여 주로 4 가지 문제가 있습니다 .

  1. Java 생성자 반영 메타 데이터는 인수 이름을 보유하지 않습니다.
  2. Java 생성자 및 메소드에는 이름 지정된 매개 변수 (라벨이라고도 함)가 없으므로 많은 매개 변수와 혼동됩니다.
  3. 다른 불변의 객체를 상속 할 때는 적절한 순서의 생성자를 호출해야합니다. 이것은 필드 중 하나를 포기하지 않고 그대로 두는 점에서 다소 까다로울 수 있습니다.
  4. Spring MVC Data Binding, Hibernate 등과 같은 대부분의 바인딩 기술은 인수가없는 기본 생성자에서만 작동합니다 (주석이 항상 존재하지 않았기 때문입니다).

@ConstructorProperties변경 불가능한 오브젝트를 작성하기 위해 다른 가변 빌더 오브젝트 (일반적으로 유창함)를 작성하고 같은 주석을 사용하여 # 1 및 # 2를 완화 할 수 있습니다 .


5

아무도 성능 최적화 이점을 언급하지 않았다는 것에 놀랐습니다. 언어에 따라 컴파일러는 데이터가 변하지 않을 것이라는 것을 알고 있기 때문에 불변 데이터를 처리 할 때 많은 최적화를 수행 할 수 있습니다. 모든 종류의 항목을 건너 뛰어 엄청난 성능 이점을 제공합니다.

또한 불변 객체는 거의 모든 종류의 상태 버그를 제거합니다.

어려워서 모든 언어에 적용되는 것은 아니며 대부분의 사람들이 명령형 코딩을 배웠기 때문에 그렇게 크지는 않습니다.

또한 나는 대부분의 프로그래머가 자신의 상자에 만족하고 종종 완전히 이해하지 못하는 새로운 아이디어에 저항하는 것을 발견했습니다. 일반적으로 사람들은 변화를 좋아하지 않습니다.

또한 대부분의 프로그래머 상태는 나쁘다는 것을 기억하십시오. 야생에서 수행되는 대부분의 프로그래밍은 끔찍하며 이해력과 정치가 부족하기 때문에 발생합니다.


4

사람들이 강력한 기능을 사용하는 이유는 무엇입니까? 사람들이 메타 프로그래밍, 게으름 또는 동적 타이핑을 사용하는 이유는 무엇입니까? 답은 편리함입니다. 가변 상태는 매우 쉽습니다. 위치를 쉽게 업데이트 할 수 있으며 변경 가능 상태가 변경 가능 상태보다 작업 생산성이 더 높은 프로젝트 크기의 임계 값이 상당히 높기 때문에 선택이 한동안 당신을 물지 않습니다.


4

프로그래밍 언어는 컴퓨터에서 실행되도록 설계되었습니다. CPU, RAM, 캐시, 디스크 등 컴퓨터의 모든 중요한 빌딩 블록은 변경 가능합니다. 그들이 (BIOS) 아닌 경우, 그들은 불변이며 새로운 불변 ​​객체를 만들 수 없습니다.

따라서 불변 개체 위에 구축 된 프로그래밍 언어는 구현시 표현상의 차이가 발생합니다. 그리고 C와 같은 초기 언어의 경우 그것은 큰 걸림돌이었습니다.


1

변경 가능한 객체가 없으면 상태가 없습니다. 물론,이를 관리 할 수 ​​있고 둘 이상의 스레드에서 객체를 참조 할 가능성이있는 경우에 좋습니다. 그러나 프로그램은 다소 지루할 것입니다. 많은 소프트웨어, 특히 웹 서버는 데이터베이스, 운영 체제, 시스템 라이브러리 등에서 변경 가능성을 밀어 냄으로써 변경 가능한 객체에 대한 책임을 회피합니다. 실질적인 문제로, 이는 프로그래머가 변경 가능성 문제에서 벗어나 웹 (및 기타)을 만듭니다. 저렴한 개발. 그러나 변경 가능성은 여전히 ​​존재합니다.

일반적으로 세 가지 유형의 클래스가 있습니다. 일반 스레드로부터 안전하지 않은 클래스는주의해서 보호하고 보호해야합니다. 자유롭게 사용할 수있는 불변 클래스; 자유롭게 사용할 수 있지만 매우주의해서 작성해야하는 변경 가능한 스레드 안전 클래스. 첫 번째 유형은 번거로운 유형이며 최악의 유형은 세 번째 유형으로 생각 되는 유형입니다. 물론, 첫 번째 유형은 작성하기 쉬운 유형입니다.

나는 보통 매우 조심스럽게 지켜봐야 할 보통의 가변 클래스가 많이 있습니다. 멀티 스레드 상황에서 치명적인 포옹을 피할 수 있어도 필요한 동기화로 모든 것이 느려집니다. 그래서 나는 변하기 쉬운 클래스의 불변 사본을 만들고 그것을 사용할 수있는 사람에게 건네주고 있습니다. orignal이 변경 될 때마다 새로운 불변 ​​사본이 필요하기 때문에 때때로 원본의 사본이 100 개있을 수 있습니다. 나는 가비지 콜렉션에 전적으로 의존합니다.

요약하면 스레드가 안전하지 않고 변경 가능한 객체는 여러 스레드를 사용하지 않는 경우 좋습니다. (그러나 멀티 스레딩은 모든 곳에서 활기를 띠고 있습니다. 조심하십시오!) 로컬 변수로 제한하거나 엄격하게 동기화하면 안전하게 사용할 수 있습니다. 다른 사람들의 입증 된 코드 (DB, 시스템 호출 등)를 사용하여 피할 수 있다면 그렇게하십시오. 불변 클래스를 사용할 있다면 그렇게하십시오. 그리고 나는 생각 에, 일반적으로 사람들은 멀티 스레딩 문제 중 하나 모르고 또는 그들을 두려워하고 (다른 곳에 대한 책임을 밀거나 오히려) 멀티 스레딩 피하기 위해 트릭의 모든 종류를 사용하여 (현명)입니다.

PS로서 Java getter와 setter가 손을 떼고 있음을 느낍니다. 이것을 확인하십시오 .


7
불변 상태는 여전히 상태입니다.
Jeremy Heiler 2016

3
@JeremyHeiler : 사실,하지만 뭔가의 상태입니다 입니다 변경할. 아무것도 변경하지 않으면 상태가 하나도 없으며 상태가 전혀없는 것과 같습니다.
RalphChapin 2016 년

1

많은 사람들이 좋은 답변을 얻었으므로 당신이 만졌던 것을 지적하고 매우 진실하고 여기 다른 곳에서는 언급하지 않은 것을 지적하고 싶습니다.

세터와 게터를 자동으로 생성하는 것은 끔찍하고 끔찍한 아이디어이지만 절차 적 사고를하는 사람들이 OO를 사고 방식으로 강요하는 첫 번째 방법입니다. 속성과 함께 세터와 게터는 기본적으로 필요한 것이 아니라 필요할 때만 만들어야합니다.

실제로 게터가 꽤 규칙적으로 필요하지만 코드에 세터 나 쓰기 가능한 속성이 존재하는 유일한 방법은 객체가 완전히 인스턴스화 된 후 잠겨지는 빌더 패턴을 통하는 것입니다.

많은 클래스는 생성 후 변경이 가능하며 속성을 직접 조작해서는 안됩니다. 대신 실제 비즈니스 로직을 사용하여 메소드 호출을 통해 속성을 조작하도록 요청해야합니다 (예, 세터는 거의 동일합니다) 재산을 직접 조작하는 것)

이제 이것은 "스크립팅"스타일 코드 / 언어에도 실제로 적용되지 않고 다른 사람을 위해 만든 코드에 적용되며 수년 동안 다른 사람들이 반복해서 읽을 것으로 기대합니다. 나는 Groovy를 너무나 엉망으로 만들고 대상에 큰 차이가 있기 때문에 최근에 그 구별을 시작해야했습니다.


1

가변 객체는 객체를 인스턴스화 한 후 배수 값을 설정해야 할 때 사용됩니다.

6 개의 매개 변수를 가진 생성자가 없어야합니다. 대신 setter 메소드를 사용하여 오브젝트를 수정하십시오.

예를 들어 글꼴, 방향 등의 설정자가있는 Report 개체가 있습니다.

요약하자면, 가변은 객체로 설정할 상태가 많고 생성자 서명이 너무 길지 않을 때 유용합니다.

편집 : 빌더 패턴을 사용하여 객체의 전체 상태를 구축 할 수 있습니다.


3
이는 인스턴스화 후 변경 성과 변경이 여러 값을 설정하는 유일한 방법 인 것처럼 표시됩니다. 완벽을 기하기 위해 빌더 패턴 은 동일하거나 더 강력한 기능을 제공하며 불변성을 희생하기 위해 필요하지 않습니다. new ReportBuilder().font("Arial").orientation("landscape").build()
gnat

1
"매우 긴 생성자 서명을 갖는 것은 실용적이지 않습니다.": 항상 매개 변수를 더 작은 개체로 그룹화하고 이러한 개체를 매개 변수로 생성자에 전달할 수 있습니다.
Giorgio

1
@cHao 10 또는 15 개의 속성을 설정하려면 어떻게해야합니까? 또한 명명 된 메소드 withFont가 a를 반환하는 것은 좋지 않습니다 Report.
Tulains Córdova

1
속성을 10 또는 15로 설정하는 한, (1) Java가 이러한 모든 중간 객체의 구성을 제거하는 방법을 알고 있었고 (2) 다시 이름이 표준화 된 경우 그렇게하는 코드는 어색하지 않습니다. 불변성의 문제는 아닙니다. 잘하는 방법을 모르는 Java의 문제입니다.
cHao

1
@cHao 20 개의 속성에 대한 콜 체인을 만드는 것은 추악합니다. 못생긴 코드는 품질이 낮은 코드 인 경향이 있습니다.
Tulains Córdova

1

나는 가변 객체를 사용하는 것이 명령 적 사고에서 비롯된다고 생각합니다. 가변 변수의 내용을 단계별로 변경하여 결과를 계산합니다 (부작용에 의한 계산).

기능적으로 생각하면 함수를 적용하고 이전 값에서 새 값을 만들어 불변 상태를 유지하고 시스템의 후속 상태를 나타냅니다.

기능적 접근 방식은보다 깨끗하고 강력 할 수 있지만 복사로 인해 매우 비효율적 일 수 있으므로 점진적으로 수정하는 공유 데이터 구조로 돌아 가려고합니다.

내가 가장 합리적이라고 생각하는 단점은 다음과 같습니다. 불변 객체로 시작한 다음 구현 속도가 빠르지 않으면 변경 가능한 객체로 전환합니다. 이러한 관점에서, 가변 객체를 처음부터 체계적으로 사용하는 것은 어떤 종류의 조기 최적화 로 간주 될 수 있습니다 .

그렇다면 왜 많은 프로그래머들이 가변 객체를 사용합니까? 두 가지 이유로 IMHO :

  1. 많은 프로그래머가 명령형 (프로 시저 또는 객체 지향) 패러다임을 사용하여 프로그래밍하는 방법을 배웠으므로 변경 가능성은 계산 정의에 대한 기본 접근 방식입니다.
  2. 많은 프로그래머는 성능이 너무 일찍 걱정되는 반면 기능적으로 올바른 프로그램을 작성하는 데 먼저 초점을 맞춘 다음 병목 현상을 찾아 최적화하는 것이 더 효과적입니다.

1
문제는 단지 속도가 아닙니다. 변경 가능한 유형을 사용하지 않고는 단순히 구현할 수없는 유용한 구성 유형이 많이 있습니다. 무엇보다도 두 스레드 간의 통신을 위해서는 최소한 하나의 데이터를 넣을 수 있고 다른 하나는 읽을 수있는 공유 객체에 대한 참조가 있어야합니다. 또한 "이 객체의 속성 P 및 Q 변경"을 말하는 것보다 "이 객체를 가져 와서 P 값을 제외하고 같은 객체를 새로 만든 다음, Q "를 제외하고는 그대로입니다.
supercat

1
나는 FP 전문가는 아니지만 AFAIK (1) 하나의 스레드에서 불변 값을 만들고 다른 스레드에서 다시 읽음으로써 스레드 통신을 달성 할 수 있습니다 (AFAIK, 이것은 Erlang 접근법입니다) (2) AFAIK 속성 설정에 대한 표기법 크게 변경되지 않습니다 (예 : Haskell에서 setProperty 레코드 값은 Java record.setProperty (value)에 해당). 속성 설정 결과가 새로운 불변 ​​레코드이기 때문에 시맨틱 만 변경됩니다.
Giorgio

1
"둘 다 데이터를 넣을 수 있고 다른 사람이 읽을 수있는 공유 객체에 대한 참조를 가져야합니다."보다 정확하게, 객체를 변경할 수 없게 만들 수 있습니다 (모든 멤버는 C ++로 구성되거나 final로 Java로 설정). 건설자. 그런 다음이 변경 불가능한 오브젝트가 생산자 스레드에서 소비자 스레드로 전달됩니다.
Giorgio

1
(2) 나는 불변 상태를 사용하지 않는 이유로 이미 성능을 나열했습니다. 물론 (1)과 관련하여 스레드 간의 통신을 구현하는 메커니즘에는 변경 가능한 상태가 필요하지만 프로그래밍 모델에서이를 숨길 수 있습니다 (예 : 액터 모델 ( en.wikipedia.org/wiki/Actor_model ) 참조 ). 따라서 하위 레벨에서 통신을 구현하기 위해 변경이 필요한 경우에도 변경 불가능한 오브젝트를 앞뒤로 보내는 스레드간에 통신하는 상위 추상화 레벨을 가질 수 있습니다.
Giorgio

1
나는 당신을 완전히 이해하지 못하지만 모든 값을 변경할 수없는 순수한 기능적 언어조차도 프로그램 상태, 즉 현재 프로그램 스택 및 변수 바인딩과 같은 하나의 변경 가능한 것이 있습니다. 그러나 이것이 실행 환경입니다. 어쨌든, 나는 우리가 잠시 채팅에서 이것을 토론 하고이 질문의 메시지를 정리하는 것이 좋습니다.
Giorgio

1

나는 당신이 자바에 대해 묻는다는 것을 알고 있지만 objective-c에서 항상 가변 대 불변을 사용합니다. 불변 배열 NSArray와 변속 배열 NSMutableArray가 있습니다. 이들은 정확한 사용을 처리하기 위해 최적화 된 방식으로 특별히 작성된 두 개의 다른 클래스입니다. 배열을 만들고 내용을 변경하지 않으면 NSArray를 사용합니다. NSArray는 더 작은 객체이며 가변 배열과 비교하여 훨씬 빠릅니다.

따라서 변경 불가능한 Person 객체를 생성하면 생성자와 게터 만 필요하므로 객체가 더 작고 메모리가 적게 사용되므로 프로그램이 실제로 더 빨라집니다. 생성 후 객체를 변경해야하는 경우 변경 가능한 Person 객체가 더 좋으므로 새 객체를 만드는 대신 값을 변경할 수 있습니다.

따라서 객체로 수행하려는 작업에 따라 가변 대 불변을 선택하면 성능과 관련하여 큰 차이가 생길 수 있습니다.


1

여기에 주어진 다른 많은 이유들 외에, 문제는 주류 언어가 불변성을 잘 지원하지 않는다는 것입니다. 최소한 const 또는 final과 같은 추가 키워드를 추가해야하고, 많은 인수 또는 코드 길이가 긴 빌더 패턴이있는 읽을 수없는 생성자를 사용해야한다는 점에서 불변성에 대해 처벌을받습니다.

불변성을 염두에 둔 언어에서는 훨씬 쉽습니다. 선택적으로 명명 된 인수를 사용하여 Person 클래스를 정의하고 변경된 속성으로 사본을 작성하려면이 스칼라 스 니펫을 고려하십시오.

case class Person(id: String, firstName: String, lastName: String)

val joe = Person("123", "Joe", "Doe")
val spouse = Person(id = "124", firstName = "Mary", lastName = "Moe")
val joeMarried = joe.copy(lastName = "Doe-Moe")

따라서 개발자가 불변성을 채택하도록하려면 이것이 더 현대적인 프로그래밍 언어로 전환하는 것을 고려할 수있는 이유 중 하나입니다.


이것은 이전 24 답변에서 만들어지고 설명 된 점에 비해 실질적인 것을 추가하지 않는 것 같습니다
gnat

@gnat 앞의 답변 중 대부분의 주류 언어가 불변성을 적절히 지원하지 않는다고 지적하는 것은 무엇입니까? 나는 그 점이 단순히 만들어지지 않았다고 생각하지만 (이것은 확인) IMHO는 매우 중요한 장애물입니다.
Hans-Peter Störr

이 하나의 예를 들어 자바에서이 문제를 설명하는 깊이 간다. 그리고 적어도 3 개의 다른 답변이 간접적으로 관련되어 있습니다
gnat

0

불변은 값을 변경할 수 없음을 의미하며 변하기 쉬움은 기본 요소와 객체로 생각하면 값을 변경할 수 있음을 의미합니다. 오브젝트는 내장 유형이라는 점에서 Java의 기본 요소와 다릅니다. 프리미티브는 int, boolean 및 void와 같은 유형으로 빌드됩니다.

많은 사람들은 최종 수정자가 앞에있는 기본 요소와 객체 변수는 변경할 수 없다고 생각하지만 이것은 사실이 아닙니다. 따라서 final은 변수에 대해 불변성을 의미하지는 않습니다. 코드 샘플은 이 링크 를 확인하십시오 .

public abstract class FinalBase {

    private final int variable; // Unset

    /* if final really means immutable than
     * I shouldn't be able to set the variable
     * but I can.
     */
    public FinalBase(int variable) { 
        this.variable = variable;
    }

    public int getVariable() {
        return variable;
    }

    public abstract void method();
}

// This is not fully necessary for this example
// but helps you see how to set the final value 
// in a sub class.
public class FinalSubclass extends FinalBase {

    public FinalSubclass(int variable) {
        super(variable);
    }

    @Override
    public void method() {
        System.out.println( getVariable() );
    }

    @Override
    public int getVariable() {

        return super.getVariable();
    }

    public static void main(String[] args) {
        FinalSubclass subclass = new FinalSubclass(10);
        subclass.method();
    }
}

-1

인터페이스 창과 같이 "실제"변경 가능한 "객체"를 모델링하는 것이 좋은 이유라고 생각합니다. 누군가화물 포트 작동을 제어하기 위해 소프트웨어를 작성하려고 할 때 OOP가 발명되었다는 것을 읽은 것을 막연히 기억합니다.


1
OOP의 기원에 대해이 글을 읽은 곳을 아직 찾을 수 없지만 Wikipedia에 따르면 대형 컨테이너 운송 회사 OOCL 의 일부 지역 정보 시스템 은 Smalltalk로 작성되었습니다.
Alexey

-2

Java는 많은 경우 가변 개체를 요구합니다. 예를 들어 방문자 나 실행 가능한 개체에서 무언가를 계산하거나 반환하려는 경우 멀티 스레딩 없이도 최종 변수가 필요합니다.


5
final실제로 약 1/4 단계 중 하나입니다 대한 불변성. 왜 언급했는지 모르겠습니다.
cHao

실제로 내 게시물을 읽었습니까?
Ralf H

실제로 질문을 읽었습니까? :) 그것은 전혀 관련이 없습니다 final. 이러한 맥락에서 그것을 가져 오면 특정 유형의 돌연변이 를 막는 것이 final중요 할 때 " 돌연변이 성 "과의 이상한 이상한 결합을 불러 일으킨다. (BTW, 내 final
다운 보트가 아닙니다

나는 당신이 여기 어딘가에 유효한 지점이 없다고 말하지 않습니다. 나는 당신이 그것을 잘 설명하고 있지 않다고 말하고 있습니다. 나는 당신이 아마 어디로 가고 있는지 반쯤 볼 수 있지만 더 나아가 야합니다. 그대로 혼란스러워 보입니다.
cHao

1
실제로 변경 가능한 객체가 필요한 경우는 거의 없습니다 . 변경 가능한 객체를 사용하여 코드가 더 명확하거나 더 빠른 경우가 있습니다. 그러나 대부분의 디자인 패턴은 기본적으로 기본적으로 지원하지 않는 언어에 대한 함수형 프로그래밍의 절반에 해당하는 표현 일뿐입니다. 재미있는 점은 FP는 매우 선택된 장소 (즉, 부작용이 발생해야하는 장소)에서만 변경이 필요하며, 이러한 장소는 일반적으로 수, 크기 및 범위가 엄격하게 제한되어 있다는 것입니다.
cHao
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.