리팩터링/리팩터링 기법

리팩터링 기법. 참조를 값으로 바꾸기(Change Reference to Value)

developer-tj 2023. 3. 16. 12:00
반응형

참조를 값으로 바꾸기(Change Reference to Value)는 객체를 참조로 전달하는 것이 아니라 복사해서 값으로 전달하는 방식으로 변경하는 리팩터링 기법입니다.
이 기법은 가변 데이터 문제를 해결할 수 있으며, 객체의 상태를 보호할 수 있습니다.

객체를 전달할 때 객체의 복사본을 전달하므로, 함수에서 객체의 값을 변경하더라도 원본 객체에는 영향을 주지 않습니다.

참조를 값으로 변경할 때는, 객체가 크거나 복사하는 데 많은 비용이 드는 경우를 제외하고는 크게 문제될 것이 없습니다.
객체가 커서 복사비용이 많이 든다면 참조로 전달하는 것이 더 효율적일 수 있습니다.

참조를 값으로 바꾸기를 적용하는 방법은 다음과 같습니다.

  1. 객체가 얕은 복사를 허용하는지 확인합니다. 객체가 얕은 복사를 허용하면 값으로 변경하기 쉽습니다.
  2. 객체가 깊은 복사를 필요로 하는 경우, 복사 생성자와 대입 연산자를 구현합니다.
  3. 해당 객체를 참조하던 변수를 값으로 변경합니다. 이때, 객체를 복사하거나, 객체를 직접 생성할 수 있습니다.
  4. 코드를 검증하고 테스트합니다. 변경된 코드가 기존 코드와 동작이 동일한지 검증합니다.

다음과 같은 예제 코드가 있습니다.


class Person {
public:
    Person(const std::string& name) : name_(name) {}
    std::string getName() const { return name_; }
    void setName(const std::string& name) { name_ = name; }
private:
    std::string name_;
};

class Order {
public:
    Order(Person& person, double amount) : person_(person), amount_(amount) {}
    double getAmount() const { return amount_; }
    std::string getPersonName() const { return person_.getName(); }
private:
    Person& person_;
    double amount_;
};

int main() {
    Person person("John");
    Order order(person, 100.0);
    std::cout << "Person name: " << order.getPersonName() << std::endl;
    std::cout << "Order amount: " << order.getAmount() << std::endl;

    // Person의 이름을 변경하면 Order에도 반영됩니다.
    person.setName("Tom");
    std::cout << "Person name: " << order.getPersonName() << std::endl;

    return 0;
}


출력


Person name: John
Order amount: 100
Person name: Tom

위 코드에서 Order 클래스의 생성자에서 Person 객체를 참조로 전달받아서 Person 객체의 값이 변경되면 Order 객체의 값도 변경됩니다.
이를 방지하기 위해서는 Order 클래스의 Person& person_; 맴버를 Person person_; 변경하여 값으로 가지면 됩니다.


class Person {
public:
    Person(const std::string& name) : name_(name) {}
    std::string getName() const { return name_; }
    void setName(const std::string& name) { name_ = name; }
private:
    std::string name_;
};

class Order {
public:
    Order(Person& person, double amount) : person_(person), amount_(amount) {}
    double getAmount() const { return amount_; }
    std::string getPersonName() const { return person_.getName(); }
private:
    Person person_;
    double amount_;
};

int main() {
    Person person("John");
    Order order(person, 100.0);
    std::cout << "Person name: " << order.getPersonName() << std::endl;
    std::cout << "Order amount: " << order.getAmount() << std::endl;

    // Person의 이름을 변경해도 Order에도 반영되지 않습니다.
    person.setName("Tom");
    std::cout << "Person name: " << order.getPersonName() << std::endl;

    return 0;
}

출력


Person name: John
Order amount: 100
Person name: John

Order 클래스 내부에서 Person 멤버 변수를 값으로 선언하여 사용하였습니다.
이를 통해 Order 객체는 Person 객체를 가지게 되어, Person 객체가 변경되어도 Order 객체의 값은 변하지 않습니다.