리팩토링 - 냄새 6. 가변 데이터
들어가기
해당 포스팅은 인프런 백기선님의 '리팩토링'을 학습 후 정리한 내용입니다.
냄새 6. 가변 데이터
Mutable Data
- 데이터를 변경하다보면 예상치 못했던 결과나 해결하기 어려운 버그가 발생하기도 한다.
- 함수형 프로그래밍 언어는 데이터를 변경하지 않고 복사본을 전달한다. 하지만 그밖의 프로그래밍 언어는 데이터 변경을 허용하고 있다. 따라서 변경되는 데이터 사용 시 발생할 수 있는 리스크를 관리할 수 있는 방법을 적용하는 것이 좋다.
- 관련 리팩토링
- “변수 캡슐화하기 (Encapsulate Variable)”를 적용해 데이터를 변경할 수 있는 메소드를 제한하고 관리할 수 있다.
- “변수 쪼개기 (Split Variable)”을 사용해 여러 데이터를 저장하는 변수를 나눌 수 있다.
- “코드 정리하기 (Slide Statements)”를 사용해 데이터를 변경하는 코드를 분리하고 피할 수 있다.
- “함수 추출하기 (Extract Function)”으로 데이터를 변경하는 코드로부터 사이드 이팩트가 없는 코드를 분리할 수 있다.
- “질의 함수와 변경 함수 분리하기 (Separate Query from Modifier)”를 적용해서 클라이언트가 원하는 경우에만 사이드 이팩트가 있는 함수를 호출하도록 API를 개선할 수 있다.
- 가능하다면 “세터 제거하기 (Remove Setting Method)”를 적용한다.
- 계산해서 알아낼 수 있는 값에는 “파생 변수를 질의 함수로 바꾸기 (Replace Derived Variable with Query)”를 적용할 수 있다.
- 변수가 사용되는 범위를 제한하려면 “여러 함수를 클래스로 묶기 (Combine Functions into Class)”또는 “여러 함수를 변환 함수로 묶기 (Combine Functions into Transform)”을 적용할 수 있다.
- “참조를 값으로 바꾸기 (Change Reference to Value)”를 적용해서 데이터 일부를 변경하기 보다는 데이터 전체를 교체할 수 있다.
 
  
변수 쪼개기
Split Variable
- 어떤 변수가 여러번 재할당 되어도 적절한 경우
- 반복문에서 순회하는데 사용하는 변수 또는 인덱스
- 값을 축적시키는데 사용하는 변수
 
- 그밖에 경우에 재할당 되는 변수가 있다면 해당 변수는 여러 용도로 사용되는 것이며 변수를 분리해야 더 이해하기 좋은 코드를 만들 수 있다.
- 변수 하나 당 하나의 책임(Responsibility)을 지도록 만든다.
- 상수를 활용하자. (자바스크립트의 const, 자바의 final)
 
예제코드
public class Order {
    public double discount(double inputValue, int quantity) {
        if (inputValue > 50) inputValue = inputValue - 2;
        if (quantity > 100) inputValue = inputValue - 1;
        return inputValue;
    }
}
냄새
매개변수인 inputValue 의 값을 그대로 활용한다
해결
변수를 쪼개보자!
리팩토링 후
public class Order {
    public double discount(double inputValue, int quantity) {
        double result = inputValue;
        if (inputValue > 50) result -= 2;
        if (quantity > 100) result -= 1;
        return result;
    }
}
설명
inputValue 를 변수화 하여 사용한다.
질의 함수와 변경 함수 분리하기
Separate Query from Modifier
- “눈에 띌만한” 사이드 이팩트 없이 값을 조회할 수 있는 메소드는 테스트 하기도 쉽고, 메소
 드를 이동하기도 편하다.
- 명령-조회 분리 (command-query separation) 규칙:
- 어떤 값을 리턴하는 함수는 사이드 이팩트가 없어야 한다.
 
- “눈에 띌만한 (observable) 사이드 이팩트”
- 가령, 캐시는 중요한 객체 상태 변화는 아니다. 따라서 어떤 메소드 호출로 인해, 캐시 데이터를 변경하더라도 분리할 필요는 없다
 
예제코드
public class Billing {
    private Customer customer;
    private EmailGateway emailGateway;
    public Billing(Customer customer, EmailGateway emailGateway) {
        this.customer = customer;
        this.emailGateway = emailGateway;
    }
    public double getTotalOutstandingAndSendBill() {
        double result = customer.getInvoices().stream()
                .map(Invoice::getAmount)
                .reduce((double) 0, Double::sum);
        sendBill();
        return result;
    }
    private void sendBill() {
        emailGateway.send(formatBill(customer));
    }
    private String formatBill(Customer customer) {
        return "sending bill for " + customer.getName();
    }
}
냄새
getTotalOutstandingAndSendBill 함수의 호출할 경우 sendBill() 불필요한 함수를 호출한다.
해결
조회 함수와 명렴 함수를 분리하자!
리팩토링 후
public class Billing {
    private Customer customer;
    private EmailGateway emailGateway;
    public Billing(Customer customer, EmailGateway emailGateway) {
        this.customer = customer;
        this.emailGateway = emailGateway;
    }
    public double getTotalOutstanding() {
        return customer.getInvoices().stream()
                .map(Invoice::getAmount)
                .reduce((double) 0, Double::sum);
    }
    public void sendBill() {
        emailGateway.send(formatBill(customer));
    }
    private String formatBill(Customer customer) {
        return "sending bill for " + customer.getName();
    }
}
설명
getTotalOutstanding() 함수는 조회만하고 senBill() 함수는 따로 호출하여 명령과 조회를 분리한다.
세터 제거하기
Remove Setting Method
해당 포스팅은 인프런 백기선님의 '리팩토링'을 학습 후 정리한 내용입니다.
Mutable Data
- 데이터를 변경하다보면 예상치 못했던 결과나 해결하기 어려운 버그가 발생하기도 한다.
- 함수형 프로그래밍 언어는 데이터를 변경하지 않고 복사본을 전달한다. 하지만 그밖의 프로그래밍 언어는 데이터 변경을 허용하고 있다. 따라서 변경되는 데이터 사용 시 발생할 수 있는 리스크를 관리할 수 있는 방법을 적용하는 것이 좋다.
- 관련 리팩토링- “변수 캡슐화하기 (Encapsulate Variable)”를 적용해 데이터를 변경할 수 있는 메소드를 제한하고 관리할 수 있다.
- “변수 쪼개기 (Split Variable)”을 사용해 여러 데이터를 저장하는 변수를 나눌 수 있다.
- “코드 정리하기 (Slide Statements)”를 사용해 데이터를 변경하는 코드를 분리하고 피할 수 있다.
- “함수 추출하기 (Extract Function)”으로 데이터를 변경하는 코드로부터 사이드 이팩트가 없는 코드를 분리할 수 있다.
- “질의 함수와 변경 함수 분리하기 (Separate Query from Modifier)”를 적용해서 클라이언트가 원하는 경우에만 사이드 이팩트가 있는 함수를 호출하도록 API를 개선할 수 있다.
- 가능하다면 “세터 제거하기 (Remove Setting Method)”를 적용한다.
- 계산해서 알아낼 수 있는 값에는 “파생 변수를 질의 함수로 바꾸기 (Replace Derived Variable with Query)”를 적용할 수 있다.
- 변수가 사용되는 범위를 제한하려면 “여러 함수를 클래스로 묶기 (Combine Functions into Class)”또는 “여러 함수를 변환 함수로 묶기 (Combine Functions into Transform)”을 적용할 수 있다.
- “참조를 값으로 바꾸기 (Change Reference to Value)”를 적용해서 데이터 일부를 변경하기 보다는 데이터 전체를 교체할 수 있다.
 
변수 쪼개기
Split Variable
- 어떤 변수가 여러번 재할당 되어도 적절한 경우
- 반복문에서 순회하는데 사용하는 변수 또는 인덱스
- 값을 축적시키는데 사용하는 변수
 
- 그밖에 경우에 재할당 되는 변수가 있다면 해당 변수는 여러 용도로 사용되는 것이며 변수를 분리해야 더 이해하기 좋은 코드를 만들 수 있다.
- 변수 하나 당 하나의 책임(Responsibility)을 지도록 만든다.
- 상수를 활용하자. (자바스크립트의 const, 자바의 final)
 
예제코드
public class Order {
    public double discount(double inputValue, int quantity) {
        if (inputValue > 50) inputValue = inputValue - 2;
        if (quantity > 100) inputValue = inputValue - 1;
        return inputValue;
    }
}
냄새
매개변수인 inputValue 의 값을 그대로 활용한다
해결
변수를 쪼개보자!
리팩토링 후
public class Order {
    public double discount(double inputValue, int quantity) {
        double result = inputValue;
        if (inputValue > 50) result -= 2;
        if (quantity > 100) result -= 1;
        return result;
    }
}
설명
inputValue 를 변수화 하여 사용한다.
질의 함수와 변경 함수 분리하기
Separate Query from Modifier
- “눈에 띌만한” 사이드 이팩트 없이 값을 조회할 수 있는 메소드는 테스트 하기도 쉽고, 메소
 드를 이동하기도 편하다.
- 명령-조회 분리 (command-query separation) 규칙:
- 어떤 값을 리턴하는 함수는 사이드 이팩트가 없어야 한다.
 
- “눈에 띌만한 (observable) 사이드 이팩트”
- 가령, 캐시는 중요한 객체 상태 변화는 아니다. 따라서 어떤 메소드 호출로 인해, 캐시 데이터를 변경하더라도 분리할 필요는 없다
 
예제코드
public class Billing {
    private Customer customer;
    private EmailGateway emailGateway;
    public Billing(Customer customer, EmailGateway emailGateway) {
        this.customer = customer;
        this.emailGateway = emailGateway;
    }
    public double getTotalOutstandingAndSendBill() {
        double result = customer.getInvoices().stream()
                .map(Invoice::getAmount)
                .reduce((double) 0, Double::sum);
        sendBill();
        return result;
    }
    private void sendBill() {
        emailGateway.send(formatBill(customer));
    }
    private String formatBill(Customer customer) {
        return "sending bill for " + customer.getName();
    }
}
냄새
getTotalOutstandingAndSendBill 함수의 호출할 경우 sendBill() 불필요한 함수를 호출한다.
해결
조회 함수와 명렴 함수를 분리하자!
리팩토링 후
public class Billing {
    private Customer customer;
    private EmailGateway emailGateway;
    public Billing(Customer customer, EmailGateway emailGateway) {
        this.customer = customer;
        this.emailGateway = emailGateway;
    }
    public double getTotalOutstanding() {
        return customer.getInvoices().stream()
                .map(Invoice::getAmount)
                .reduce((double) 0, Double::sum);
    }
    public void sendBill() {
        emailGateway.send(formatBill(customer));
    }
    private String formatBill(Customer customer) {
        return "sending bill for " + customer.getName();
    }
}
설명
getTotalOutstanding() 함수는 조회만하고 senBill() 함수는 따로 호출하여 명령과 조회를 분리한다.
세터 제거하기
Remove Setting Method
- 반복문에서 순회하는데 사용하는 변수 또는 인덱스
- 값을 축적시키는데 사용하는 변수
- 변수 하나 당 하나의 책임(Responsibility)을 지도록 만든다.
- 상수를 활용하자. (자바스크립트의 const, 자바의 final)
public class Order {
    public double discount(double inputValue, int quantity) {
        if (inputValue > 50) inputValue = inputValue - 2;
        if (quantity > 100) inputValue = inputValue - 1;
        return inputValue;
    }
}매개변수인 inputValue 의 값을 그대로 활용한다
변수를 쪼개보자!
public class Order {
    public double discount(double inputValue, int quantity) {
        double result = inputValue;
        if (inputValue > 50) result -= 2;
        if (quantity > 100) result -= 1;
        return result;
    }
}
inputValue 를 변수화 하여 사용한다.
Separate Query from Modifier
- “눈에 띌만한” 사이드 이팩트 없이 값을 조회할 수 있는 메소드는 테스트 하기도 쉽고, 메소
 드를 이동하기도 편하다.
- 명령-조회 분리 (command-query separation) 규칙:- 어떤 값을 리턴하는 함수는 사이드 이팩트가 없어야 한다.
 
- “눈에 띌만한 (observable) 사이드 이팩트”- 가령, 캐시는 중요한 객체 상태 변화는 아니다. 따라서 어떤 메소드 호출로 인해, 캐시 데이터를 변경하더라도 분리할 필요는 없다
 
예제코드
public class Billing {
    private Customer customer;
    private EmailGateway emailGateway;
    public Billing(Customer customer, EmailGateway emailGateway) {
        this.customer = customer;
        this.emailGateway = emailGateway;
    }
    public double getTotalOutstandingAndSendBill() {
        double result = customer.getInvoices().stream()
                .map(Invoice::getAmount)
                .reduce((double) 0, Double::sum);
        sendBill();
        return result;
    }
    private void sendBill() {
        emailGateway.send(formatBill(customer));
    }
    private String formatBill(Customer customer) {
        return "sending bill for " + customer.getName();
    }
}냄새
getTotalOutstandingAndSendBill 함수의 호출할 경우 sendBill() 불필요한 함수를 호출한다.
해결
조회 함수와 명렴 함수를 분리하자!
리팩토링 후
public class Billing {
    private Customer customer;
    private EmailGateway emailGateway;
    public Billing(Customer customer, EmailGateway emailGateway) {
        this.customer = customer;
        this.emailGateway = emailGateway;
    }
    public double getTotalOutstanding() {
        return customer.getInvoices().stream()
                .map(Invoice::getAmount)
                .reduce((double) 0, Double::sum);
    }
    public void sendBill() {
        emailGateway.send(formatBill(customer));
    }
    private String formatBill(Customer customer) {
        return "sending bill for " + customer.getName();
    }
}
설명
getTotalOutstanding() 함수는 조회만하고 senBill() 함수는 따로 호출하여 명령과 조회를 분리한다.
세터 제거하기
Remove Setting Method
• 세터를 제공한다는 것은 해당 필드가 변경될 수 있다는 것을 뜻한다.
• 객체 생성시 처음 설정된 값이 변경될 필요가 없다면 해당 값을 설정할 수 있는 생성자를 만들고 세터를 제거해서 변경될 수 있는 가능성을 제거해야 한다.
예제코드
public class Person {
    private String name;
    private int id;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}
냄새
회원을 식별하는 필드인 id 는 변경을 하면 안되지만, setter 를 사용한다.
해결
setter 메서드를 없앤다.
파생 변수를 질의 함수로 바꾸기
Replace Derived Variable with Query
- 변경할 수 있는 데이터를 최대한 줄이도록 노력해야 한다.
- 계산해서 알아낼 수 있는 변수는 제거할 수 있다.
- 계산 자체가 데이터의 의미를 잘 표현하는 경우도 있다.
- 해당 변수가 어디선가 잘못된 값으로 수정될 수 있는 가능성을 제거할 수 있다.
 
- 계산에 필요한 데이터가 변하지 않는 값이라면, 계산의 결과에 해당하는 데이터 역시 불변 데이터기 때문에 해당 변수는 그대로 유지할 수 있다.
예제코드
public class ProductionPlan {
    private double production;
    private List<Double> adjustments = new ArrayList<>();
    public void applyAdjustment(double adjustment) {
        this.adjustments.add(adjustment);
        this.production += adjustment;
    }
    public double getProduction() {
        return this.production;
    }
}
냄새
applyAdjustment() 함수는 매개변수의 값을 list에 넣어줌과 동시에 총 합을 profuction 필드에 저장을 하는데, 이는 list 에서 총합을 알 수 있는 불필요한 변수이다.  
해결
production 필드를 제거 후 list 에 저장된 총합을 return 하도록 한다.
리팩토링 후
public class ProductionPlan {
    private List<Double> adjustments = new ArrayList<>();
    public void applyAdjustment(double adjustment) {
        this.adjustments.add(adjustment);
    }
    public double getProduction() {
        return adjustments.stream().mapToDouble(Double::valueOf).sum();
    }
}
설명
불필요한 production 필드 제거 후, getProduction 에서 총합을 계산한다.
여러 함수를 변환 함수로 묶기
Combine Functions into Transform
- 관련있는 여러 파생 변수를 만들어내는 함수가 여러곳에서 만들어지고 사용된다면 그러한 파생 변수를 “변환 함수 (transform function)”를 통해 한 곳으로 모아둘 수 있다.
- 소스 데이터가 변경될 수 있는 경우에는 “여러 함수를 클래스로 묶기 (Combine Functions into Class)”를 사용하는 것이 적절하다.
- 소스 데이터가 변경되지 않는 경우에는 두 가지 방법을 모두 사용할 수 있지만, 변환 함수를 사용해서 불변 데이터의 필드로 생성해 두고 재사용할 수도 있다.
객체 통쨰로 넘기기(preserve whole method) 와 같은 방식을 사용한다.
참조를 값으로 바꾸기
-Change Reference to Value
- 레퍼런스 (Reference) 객체 vs 값 (Value) 객체
- https://martinfowler.com/bliki/ValueObject.html
- “Objects that are equal due to the value of their properties, in this case their x and y coordinates, are called value objects.”
- 값 객체는 객체가 가진 필드의 값으로 동일성을 확인한다.
- 값 객체는 변하지 않는다.
 
- 어떤 객체의 변경 내역을 다른 곳으로 전파시키고 싶다면 레퍼런스, 아니라면 값 객체를 사용한다.
예제코드
private TelephoneNumber officeTelephoneNumber;
    public String getOfficeAreaCode() {
        return this.officeTelephoneNumber.getAreaCode();
    }
    public void setOfficeAreaCode(String areaCode) {
        this.officeTelephoneNumber.setAreaCode(areaCode);
    }
    public String getOfficeNumber() {
        return this.officeTelephoneNumber.getNumber();
    }
    public void setOfficeNumber(String number) {
        this.officeTelephoneNumber.setNumber(number);
    }
냄새
TelephoneNumber 객체를 레퍼런스 객체가 아닌 값 객체로 사용할 경우 set
해결
setter 를 통해 수정하지 않고 새로운 값 객체를 만들어준다.
리팩토링 후
Person
public class Person {
    private TelephoneNumber officeTelephoneNumber;
    public String getOfficeAreaCode() {
        return this.officeTelephoneNumber.getAreaCode();
    }
    public String getOfficeNumber() {
        return this.officeTelephoneNumber.getNumber();
    }
    public void setOfficeAreaCode(String areaCode) {
        this.officeTelephoneNumber = new TelephoneNumber(areaCode, this.getOfficeNumber());
    }
    public void setOfficeNumber(String number) {
        this.officeTelephoneNumber = new TelephoneNumber(this.getOfficeAreaCode(), number);
    }
}
TelephoneNumber
public class TelephoneNumber {
    private final String areaCode;
    private final String number;
    public TelephoneNumber(String areaCode, String number) {
        this.areaCode = areaCode;
        this.number = number;
    }
    public String getAreaCode() {
        return areaCode;
    }
    public String getNumber() {
        return number;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TelephoneNumber that = (TelephoneNumber) o;
        return Objects.equals(areaCode, that.areaCode) && Objects.equals(number, that.number);
    }
    @Override
    public int hashCode() {
        return Objects.hash(areaCode, number);
    }
}
값 객체이기 때문에 setter 를 없애고 equals, hashCode 메서드를 각 구현해준다.
                
                    
        
    
    
    
    
    
                
                
                
                
                    
                        
                            
                            
                            Author And Source
                            
                            이 문제에 관하여(리팩토링 - 냄새 6. 가변 데이터), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
                                
                                https://velog.io/@coconenne/리팩토링-냄새-6.-가변-데이터
                            
                            
                            
                                저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
                            
                            
                                
                                
                                 우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)
                            
                            
                        
                    
                
                
                
            
- 계산 자체가 데이터의 의미를 잘 표현하는 경우도 있다.
- 해당 변수가 어디선가 잘못된 값으로 수정될 수 있는 가능성을 제거할 수 있다.
public class ProductionPlan {
    private double production;
    private List<Double> adjustments = new ArrayList<>();
    public void applyAdjustment(double adjustment) {
        this.adjustments.add(adjustment);
        this.production += adjustment;
    }
    public double getProduction() {
        return this.production;
    }
}applyAdjustment() 함수는 매개변수의 값을 list에 넣어줌과 동시에 총 합을 profuction 필드에 저장을 하는데, 이는 list 에서 총합을 알 수 있는 불필요한 변수이다.
production 필드를 제거 후 list 에 저장된 총합을 return 하도록 한다.
public class ProductionPlan {
    private List<Double> adjustments = new ArrayList<>();
    public void applyAdjustment(double adjustment) {
        this.adjustments.add(adjustment);
    }
    public double getProduction() {
        return adjustments.stream().mapToDouble(Double::valueOf).sum();
    }
}
불필요한 production 필드 제거 후, getProduction 에서 총합을 계산한다.
Combine Functions into Transform
- 관련있는 여러 파생 변수를 만들어내는 함수가 여러곳에서 만들어지고 사용된다면 그러한 파생 변수를 “변환 함수 (transform function)”를 통해 한 곳으로 모아둘 수 있다.
- 소스 데이터가 변경될 수 있는 경우에는 “여러 함수를 클래스로 묶기 (Combine Functions into Class)”를 사용하는 것이 적절하다.
- 소스 데이터가 변경되지 않는 경우에는 두 가지 방법을 모두 사용할 수 있지만, 변환 함수를 사용해서 불변 데이터의 필드로 생성해 두고 재사용할 수도 있다.
객체 통쨰로 넘기기(preserve whole method) 와 같은 방식을 사용한다.
참조를 값으로 바꾸기
-Change Reference to Value
- 레퍼런스 (Reference) 객체 vs 값 (Value) 객체
- https://martinfowler.com/bliki/ValueObject.html
- “Objects that are equal due to the value of their properties, in this case their x and y coordinates, are called value objects.”
- 값 객체는 객체가 가진 필드의 값으로 동일성을 확인한다.
- 값 객체는 변하지 않는다.
 
- 어떤 객체의 변경 내역을 다른 곳으로 전파시키고 싶다면 레퍼런스, 아니라면 값 객체를 사용한다.
예제코드
private TelephoneNumber officeTelephoneNumber;
    public String getOfficeAreaCode() {
        return this.officeTelephoneNumber.getAreaCode();
    }
    public void setOfficeAreaCode(String areaCode) {
        this.officeTelephoneNumber.setAreaCode(areaCode);
    }
    public String getOfficeNumber() {
        return this.officeTelephoneNumber.getNumber();
    }
    public void setOfficeNumber(String number) {
        this.officeTelephoneNumber.setNumber(number);
    }
냄새
TelephoneNumber 객체를 레퍼런스 객체가 아닌 값 객체로 사용할 경우 set
해결
setter 를 통해 수정하지 않고 새로운 값 객체를 만들어준다.
리팩토링 후
Person
public class Person {
    private TelephoneNumber officeTelephoneNumber;
    public String getOfficeAreaCode() {
        return this.officeTelephoneNumber.getAreaCode();
    }
    public String getOfficeNumber() {
        return this.officeTelephoneNumber.getNumber();
    }
    public void setOfficeAreaCode(String areaCode) {
        this.officeTelephoneNumber = new TelephoneNumber(areaCode, this.getOfficeNumber());
    }
    public void setOfficeNumber(String number) {
        this.officeTelephoneNumber = new TelephoneNumber(this.getOfficeAreaCode(), number);
    }
}
TelephoneNumber
public class TelephoneNumber {
    private final String areaCode;
    private final String number;
    public TelephoneNumber(String areaCode, String number) {
        this.areaCode = areaCode;
        this.number = number;
    }
    public String getAreaCode() {
        return areaCode;
    }
    public String getNumber() {
        return number;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TelephoneNumber that = (TelephoneNumber) o;
        return Objects.equals(areaCode, that.areaCode) && Objects.equals(number, that.number);
    }
    @Override
    public int hashCode() {
        return Objects.hash(areaCode, number);
    }
}
값 객체이기 때문에 setter 를 없애고 equals, hashCode 메서드를 각 구현해준다.
                
                    
        
    
    
    
    
    
                
                
                
                
                    
                        
                            
                            
                            Author And Source
                            
                            이 문제에 관하여(리팩토링 - 냄새 6. 가변 데이터), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
                                
                                https://velog.io/@coconenne/리팩토링-냄새-6.-가변-데이터
                            
                            
                            
                                저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
                            
                            
                                
                                
                                 우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)
                            
                            
                        
                    
                
                
                
            
- https://martinfowler.com/bliki/ValueObject.html
- “Objects that are equal due to the value of their properties, in this case their x and y coordinates, are called value objects.”
- 값 객체는 객체가 가진 필드의 값으로 동일성을 확인한다.
- 값 객체는 변하지 않는다.
private TelephoneNumber officeTelephoneNumber;
    public String getOfficeAreaCode() {
        return this.officeTelephoneNumber.getAreaCode();
    }
    public void setOfficeAreaCode(String areaCode) {
        this.officeTelephoneNumber.setAreaCode(areaCode);
    }
    public String getOfficeNumber() {
        return this.officeTelephoneNumber.getNumber();
    }
    public void setOfficeNumber(String number) {
        this.officeTelephoneNumber.setNumber(number);
    }TelephoneNumber 객체를 레퍼런스 객체가 아닌 값 객체로 사용할 경우 set
setter 를 통해 수정하지 않고 새로운 값 객체를 만들어준다.
public class Person {
    private TelephoneNumber officeTelephoneNumber;
    public String getOfficeAreaCode() {
        return this.officeTelephoneNumber.getAreaCode();
    }
    public String getOfficeNumber() {
        return this.officeTelephoneNumber.getNumber();
    }
    public void setOfficeAreaCode(String areaCode) {
        this.officeTelephoneNumber = new TelephoneNumber(areaCode, this.getOfficeNumber());
    }
    public void setOfficeNumber(String number) {
        this.officeTelephoneNumber = new TelephoneNumber(this.getOfficeAreaCode(), number);
    }
}
public class TelephoneNumber {
    private final String areaCode;
    private final String number;
    public TelephoneNumber(String areaCode, String number) {
        this.areaCode = areaCode;
        this.number = number;
    }
    public String getAreaCode() {
        return areaCode;
    }
    public String getNumber() {
        return number;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TelephoneNumber that = (TelephoneNumber) o;
        return Objects.equals(areaCode, that.areaCode) && Objects.equals(number, that.number);
    }
    @Override
    public int hashCode() {
        return Objects.hash(areaCode, number);
    }
}
값 객체이기 때문에 setter 를 없애고 equals, hashCode 메서드를 각 구현해준다.
Author And Source
이 문제에 관하여(리팩토링 - 냄새 6. 가변 데이터), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@coconenne/리팩토링-냄새-6.-가변-데이터저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)