ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Refactoring] 8. 산탄총 수술
    클린코드 2022. 9. 11. 15:28
     

    코딩으로 학습하는 리팩토링 - 인프런 | 강의

    리팩토링은 소프트웨어 엔지니어가 갖춰야 할 기본적인 소양 중 하나입니다. 이 강의는 인텔리J와 자바를 사용하여 보다 실용적인 방법으로 다양한 코드의 냄새와 리팩토링 기술을 설명하고 직

    www.inflearn.com

    | 인프런 - 백기선님의 코딩으로 학습하는 리팩토링 강의를 수강하며 정리한 글입니다.


    산탄총 수술
    • 어떠한 변화가 발생하였을 때 여러 모듈, 함수, 클래스를 변경해야 하는 상황
    • 변경 사항이 여러 곳에 흩어진다면 찾아서 수정하기 어렵고, 중요한 변경 사항을 놓칠 수 있다.
    • 관련 리팩토링 기술
      • 함수 옮기기 또는 필드 옮기기 -> 필요한 변경 내역을 하나의 클래스로 모을 수 있다.
      • 여러 함수를 클래스로 묶기 -> 비슷한 데이터를 사용하는 여러 함수를 하나의 클로스로 모을 수 있다.
      • 단계 쪼개기 -> 공통으로 사용되는 함수의 결과물들을 하나로 묶을 수 있다.
      • 함수 인라인, 클래스 인라인 -> 흩어진 로직을 하나로 묶을 수 있다.

    Refactoring 1. 필드 옮기기

    • 좋은 데이터 구조를 가지고 있다면, 해당 데이터에 기반한 어떤 행위를 코드(메서드)로 옮기는 것은 간편하고 쉬워진다.
    • 처음에는 타당해 보이는 구조여도, 도메인이 추가되거나 수정하는 과정에서 틀린 의사 결정이 될 수 있다.
    • 근거
      • 어떤 데이터를 항상 어떤 레코드와 함께 전달할 경우
      • 어떤 레코드를 변경할 때 다른 레코드의 필드도 수정해야 할 경우
      • 여러 레코드에 동일한 필드를 수정해야 할 경우
    Before
    public class Customer {
    
        private String name;
    
        private double discountRate;
    
        private CustomerContract contract;
    
        public Customer(String name, double discountRate) {
            this.name = name;
            this.discountRate = discountRate;
            this.contract = new CustomerContract(dateToday());
        }
    
        public double getDiscountRate() {
            return discountRate;
        }
    
        public void becomePreferred() {
            this.discountRate += 0.03;
            // 다른 작업들
        }
    
        public double applyDiscount(double amount) {
            BigDecimal value = BigDecimal.valueOf(amount);
            return value.subtract(value.multiply(BigDecimal.valueOf(this.discountRate))).doubleValue();
        }
    
        private LocalDate dateToday() {
            return LocalDate.now();
        }
    }
    
    public class CustomerContract {
    
        private LocalDate startDate;
    
        public CustomerContract(LocalDate startDate) {
            this.startDate = startDate;
        }
    }
    After
    public class Customer {
    
        private String name;
    
        private CustomerContract contract;
    
        public Customer(String name, double discountRate) {
            this.name = name;
            this.contract = new CustomerContract(dateToday(), discountRate);
        }
    
        public void becomePreferred() {
            contract.setDiscountRate(contract.getDiscountRate() + 0.03);
            // 다른 작업들
        }
    
        public double applyDiscount(double amount) {
            BigDecimal value = BigDecimal.valueOf(amount);
            return value.subtract(value.multiply(BigDecimal.valueOf(contract.getDiscountRate()))).doubleValue();
        }
    
        private LocalDate dateToday() {
            return LocalDate.now();
        }
    }
    
    public class CustomerContract {
    
        private LocalDate startDate;
    
        private double discountRate;
    
        public CustomerContract(LocalDate startDate, double discountRate) {
            this.startDate = startDate;
            this.discountRate = discountRate;
        }
    
        public LocalDate getStartDate() {
            return startDate;
        }
    
        public void setStartDate(LocalDate startDate) {
            this.startDate = startDate;
        }
    
        public double getDiscountRate() {
            return discountRate;
        }
    
        public void setDiscountRate(double discountRate) {
            this.discountRate = discountRate;
        }
    }

    => discountRate 필드 옮기기(Customer -> CustomerContract)


    Refactoring 2. 함수 인라인

    • 함수 추출하기와 반대되는 리팩토링
    • 간혹 함수 본문이 함수 이름보다 의도가 잘 표현될 때가 존재한다.
    • 함수 리팩토링이 잘못된 경우에 여러 함수를 인라인하여 커다란 함수를 만든 후, 다음에 다시 함수 추출하기를 적용할 수 있다.
    • 단순히 메서드 호출을 감싸는 우회형 메서드라면 인라인으로 없앨 수 있다.
    • 상속 구조에서 오버라이딩하고 있는 메서드는 인라인 할 수 없다.
    Before
    public class Driver {
    
        private int numberOfLateDeliveries;
    
        public Driver(int numberOfLateDeliveries) {
            this.numberOfLateDeliveries = numberOfLateDeliveries;
        }
    
        public int getNumberOfLateDeliveries() {
            return this.numberOfLateDeliveries;
        }
    }
    
    public class Rating {
    
        public int rating(Driver driver) {
            return moreThanFiveLateDeliveries(driver) ? 2 : 1;
        }
    
        private boolean moreThanFiveLateDeliveries(Driver driver) {
            return driver.getNumberOfLateDeliveries() > 5;
        }
    }
    After
    public class Driver {
    
        private int numberOfLateDeliveries;
    
        public Driver(int numberOfLateDeliveries) {
            this.numberOfLateDeliveries = numberOfLateDeliveries;
        }
    
        public int getNumberOfLateDeliveries() {
            return this.numberOfLateDeliveries;
        }
    }
    
    public class Rating {
    
        public int rating(Driver driver) {
            return driver.getNumberOfLateDeliveries() > 5 ? 2 : 1;
        }
    
    }

    Refactoring 3. 클래스 인라인

    • 클래스 추출하기와 반대되는 리팩토링
    • 리팩토링을 하는 도중 클래스의 책임을 옮기다 보면 클래스의 존재 유무가 빈약해지는 경우가 발생할 수 있다.
    • 먼저, 클래스 인라인을 통해 하나의 클래스로 모아둔 후 다음에 클래스를 추출하는 리팩토링을 적용할 수 있다.
    Before
    public class Shipment {
    
        private TrackingInformation trackingInformation;
    
        public Shipment(TrackingInformation trackingInformation) {
            this.trackingInformation = trackingInformation;
        }
    
        public TrackingInformation getTrackingInformation() {
            return trackingInformation;
        }
    
        public void setTrackingInformation(TrackingInformation trackingInformation) {
            this.trackingInformation = trackingInformation;
        }
    
        public String getTrackingInfo() {
            return this.trackingInformation.display();
        }
    }
    
    public class TrackingInformation {
    
        private String shippingCompany;
    
        private String trackingNumber;
    
        public TrackingInformation(String shippingCompany, String trackingNumber) {
            this.shippingCompany = shippingCompany;
            this.trackingNumber = trackingNumber;
        }
    
        public String display() {
            return this.shippingCompany + ": " + this.trackingNumber;
        }
    
        public String getShippingCompany() {
            return shippingCompany;
        }
    
        public void setShippingCompany(String shippingCompany) {
            this.shippingCompany = shippingCompany;
        }
    
        public String getTrackingNumber() {
            return trackingNumber;
        }
    
        public void setTrackingNumber(String trackingNumber) {
            this.trackingNumber = trackingNumber;
        }
    }
    After
    public class Shipment {
    
        private String shippingCompany;
    
        private String trackingNumber;
    
        public Shipment(String shippingCompany, String trackingNumber) {
            this.shippingCompany = shippingCompany;
            this.trackingNumber = trackingNumber;
        }
    
        public String display() {
            return this.shippingCompany + ": " + this.trackingNumber;
        }
    
        public String getShippingCompany() {
            return shippingCompany;
        }
    
        public void setShippingCompany(String shippingCompany) {
            this.shippingCompany = shippingCompany;
        }
    
        public String getTrackingNumber() {
            return trackingNumber;
        }
    
        public void setTrackingNumber(String trackingNumber) {
            this.trackingNumber = trackingNumber;
        }
    }

     

    '클린코드' 카테고리의 다른 글

    [Refactoring] 10. 데이터 뭉치  (0) 2022.09.13
    [Refactoring] 9. 기능 편애  (0) 2022.09.12
    [Refactoring] 7. 뒤엉킨 변경  (0) 2022.09.10
    [Refactoring] 6. 가변 데이터  (0) 2022.09.09
    [Refactoring] 5. 전역 데이터  (0) 2022.09.08

    댓글

Designed by Tistory.