불변객체와 가변 동반 클래스

ttomy·2023년 2월 27일
0

불변 객체는 많은 장점이 있지만 단점도 있다.

다른 값을 가진 객체는 새로 만들어져야 한다.
-> 인스턴스의 빈번한 생성으로 성능의 손해를 볼 수 있다.

public class Line {
   private final List<Boolean> bridges;

   public Line(LineStrategy lineStrategy, int bridgeCount) {
       this.bridges = lineStrategy.generate(bridgeCount);
       validate();
   }

   public Position moveFrom(Position startPosition) {
       int position = startPosition.getPosition();
       validateOutBound(position);

       if (isRightMoveAble(position) && isBridgeExist(position)) {
           return new Position(position + 1);
       }
       if (isLeftMoveAble(position) && isBridgeExist(position - 1)) {
           return new Position(position - 1);
       }
       return new Position(position);
   }

위와 같이 line에서 moveFrom을 할때마다 새 Position이 생성되어야 한다면 인스턴스 생성에 자원이 많이 소모될 수 있다.

이에 대한 보완방법으로 가변 동반 클래스를 활용하는 방법이 있다.

가변 동반 클래스 - StringBuffer

본래 불변인 객체의 관련값을 수정해 줄 수 있는 클래스를 동반해 제공할 수 있다.

불변 객체인 String의 가변 동반 클래스 중 하나인 StringBuffer를 보며
정말로 불변객체의 단점을 보완하는지 살펴보고자 한다.
StringBuffer의 append()는 String의 새 인스턴스를 생성해가지 않으며 조작을 해 줄수 있을까?

  • StringBuffer의 구성

StringBuffer는 AbstractStringBuilder를 상속하고 있다.

  • StringBuffer의 append(String)

StringBuffer의 append()에서는 super.append()로
AbstractStringBuilder의 append()를 사용한다.

내가 기대하기로는 이 append가 String의 길이를 늘릴 때,
불변객체의 새로운 인스턴스를 생성하지 않으며 abstractStringBuilder의 문자열을 바꿀 수 있어야 할 것이다. 아래에서 마저 AbstractStringBuilder의 append()를 살펴보겠다.

  • AbstractStringBuilder의 구성

이 value 배열에 append()의 인자로 들어온 String이 쌓여갈 것이다.

  • AbstractStringBuilder의 append

  • putStringAt()

  • String의 getBytes()

큰 흐름만 보자면 System.arraycopy()를 통해
AbstractStringBuilder의 value에 새로 append될 str이 복사되는 식으로 이후에 String으로 반환될 배열 value가 확장된다.

간단한 예시

조악한 예시로 가변 동반 클래스의 느낌만 보는 식으로 Position의 가변 동반 클래스를 만들며 내용을 확인해본다.

  • PositionMover
public class PositionMover {
    private int position;

    public PositionMover() {
        this.position = 0;
    }

    public void move(int distance) {
        this.position += distance;
    }

    public Position toPosition() {
        return Position.from(position);
    }
}
  • 사용
    public Position moveFrom2(Position startPosition) {
        PositionMover pm = new PositionMover();
        pm.move(2);
        pm.move(-1);
        pm.move(3);
        pm.move(4);
        
        Position moved = pm.toPosition();
    }

Position에 다단계 연산이 필요하다 가정했을 때
PositionMover을 통해 Position 움직임의 중간과정에서는 Position을 생성하지 않으며 이동을 다뤄볼 수 있다.

Reference

현구막 기술 블로그

0개의 댓글