📖
Aaron's TECH BOOK
  • Intro
    • About me
  • Lecture
    • Kubernetes
      • Begin Kubernetes
    • Kafka
      • Begin Kafka
    • Kotlin
      • TDD, Clean Code Preview
      • woowa Kotlin
    • Java
      • Multithread Concurrency
      • The Java
    • Toby
      • Toby Spring 6
      • Toby Spring Boot
    • MSA
      • 01.Micro Service
      • 02.DDD 설계
      • 03.DDD 구현
      • 04.EDA 구현
    • Spring Boot
    • Spring Batch
    • Spring Core Advanced
    • Spring DB Part II
    • Spring DB Part I
    • JPA API and Performance Optimization
    • JPA Web Application
    • JPA Programming Basic
    • Spring MVC Part 2
      • 01.Thymeleaf
      • 02.ETC
      • 03.Validation
      • 04.Login
      • 05.Exception
    • Spring MVC Part 1
      • 01.Servlet
      • 02.MVC
    • Http
      • 01.Basic
      • 02.Method
      • 03.Header
    • Spring Core
    • Study
      • Concurrency issues
      • First Come First Served
      • Performance Test
      • TDD
      • IntelliJ
  • Book
    • Kafka Streams in Action
      • 01.카프카 스트림즈
      • 02.카프카 스트림즈 개발
      • 03.카프카 스트림즈 관리
    • Effective Kotlin
      • 01.좋은 코드
      • 02.코드 설계
      • 03.효율성
    • 이벤트 소싱과 MSA
      • 01.도메인 주도 설계
      • 02.객체지향 설계 원칙
      • 03-04.이벤트 소싱
      • 05.마이크로서비스 협업
      • 06.결과적 일관성
      • 07.CQRS
      • 08.UI
      • 09.클라우드 환경
    • 몽고DB 완벽 가이드
      • I. 몽고DB 시작
      • II. 몽고DB 개발
    • Kotlin Cookbook
      • 코틀린 기초
      • 코틀린 기능
      • ETC
    • Kotlin in Action
      • 함수/클래스/객체/인터페이스
      • 람다와 타입
      • 오버로딩과 고차 함수
      • 제네릭스, 애노테이션, 리플렉션
    • Kent Beck Tidy First?
    • 대규모 시스템 설계 기초
      • 01.사용자 수에 따른 규모 확장성
      • 02.개략적인 규모 추정
      • 03.시스템 설계 공략법
      • 04.처리율 제한 장치 설계
      • 05.안정 해시 설계
      • 06.키-값 저장소 설계
      • 07.유일 ID 생성기 설계
      • 08.URL 단축기 설계
      • 09.웹 크롤러 설계
      • 10.알림 시스템 설계
      • 11.뉴스 피드 시스템 설계
      • 12.채팅 시스템 설계
      • 13.검색어 자동완성 시스템
      • 14.유튜브 설계
      • 15.구글 드라이브 설계
      • 16.배움은 계속된다
    • 실용주의 프로그래머📖
    • GoF Design Patterns
    • 도메인 주도 개발 시작하기
      • 01.도메인 모델 시작하기
      • 02.아키텍처 개요
      • 03.애그리거트
      • 04.리포지터리와 모델 구현
      • 05.Spring Data JPA를 이용한 조회 기능
      • 06.응용 서비스와 표현 영역
      • 07.도메인 서비스
      • 08.애그리거트 트랜잭션 관리
      • 09.도메인 모델과 바운디드 컨텍스트
      • 10.이벤트
      • 11.CQRS
    • Effective Java 3/E
      • 객체, 공통 메서드
      • 클래스, 인터페이스, 제네릭
    • 소프트웨어 장인
    • 함께 자라기
    • Modern Java In Action
      • 01.기초
      • 02.함수형 데이터 처리
      • 03.스트림과 람다를 이용한 효과적 프로그래밍
      • 04.매일 자바와 함께
    • Refactoring
      • 01.리펙터링 첫 번째 예시
      • 02.리펙터링 원칙
      • 03.코드에서 나는 악취
      • 06.기본적인 리펙터링
      • 07.캡슐화
      • 08.기능 이동
      • 09.데이터 조직화
      • 10.조건부 로직 간소화
      • 11.API 리팩터링
      • 12.상속 다루기
    • 객체지향의 사실과 오해
      • 01.협력하는 객체들의 공동체
      • 02.이상한 나라의 객체
      • 03.타입과 추상화
      • 04.역할, 책임, 협력
      • 05.책임과 메시지
      • 06.객체 지도
      • 07.함께 모으기
      • 부록.추상화 기법
    • Clean Code
    • 자바 ORM 표준 JPA 프로그래밍
Powered by GitBook
On this page
  • 변수 쪼개기
  • 필드 이름 바꾸기
  • 파생 변수를 질의 함수로 바꾸기
  • 참조를 값으로 바꾸기
  • 값을 참조로 바꾸기
  • 매직 리터럴 바꾸기
  1. Book
  2. Refactoring

09.데이터 조직화

  • 데이터 구조는 프로그램에서 중요한 역할을 수행한다.

변수 쪼개기

역할이 둘 이상인 변수는 쪼개자.

여러 용도로 쓰인 변수는 코드를 읽는 과정에서 커다란 혼란을 주게 된다.

개요

Before

let temp = 2 * (height + width);
console.log(temp);
temp = height + width;
console.log(temp);

After

const perimeter = 2 * (height + width);
console.log(perimeter);
const area = height * width;
console.log(area);

절차

  1. 변수를 선언한 곳과 값을 처음 대입하는 곳에서 변수 이름을 바꾸기

    • 총합 계산, 문자열 연결, 스트림 쓰기 등에 흔히 사용되는 수집변수는 제외

  2. 가능하면 불변으로 선언

  3. 이 변수에 두 번째로 값을 대입하는 곳 앞까지의 모든 참조를 새로운 변수 이름으로 수정

  4. 두 번째 대입 시 변수를 원래 이름으로 다시 선언

  5. 테스트

  6. 반복

Example

function distanceTravelled(scenario, time) {
    let result;
    // 첫 번째 힘이 유발한 초기 가속도
    const primaryAcceleration = scenario.primaryForce / scenario.mass; // 1, 2
    let primaryTime = Math.min(time, scenario.delay);
    result = 0.5 * primaryAcceleration * primaryTime * primaryTime; // 3
    let secondaryTime = time - scenario.delay;
    if (secondaryTime > 0) {
        let primaryVelocity = primaryAcceleration * scenario.delay; // 3
        // 두 번째 힘까지 반영된 후의 가속도
        const secondaryAcceleration = (scenario.primaryForce + scenario.secondaryForce) / scenario.mass; // 4 -> 6 (1, 2)
        result += primaryVelocity * secondaryTime + 0.5 * secondaryAcceleration * secondaryTime * secondaryTime; // 3
    }
    return result;
}

필드 이름 바꾸기

데이터 구조는 프로그램을 이해하는 데 큰 역할을 한다.

데이터는 무슨 일이 벌어지는지 이해하는 열쇠다.

개요

Before

class Organization {
  get name() {}
}

After

class Organization {
  get title() {}
}

절차

  1. 레코드의 유효 범위가 제한적이라면 필드에 접근하는 모든 코드를 수정 후 테스트

    • 유효 범위가 제한적인 레코드는 절차 끝

  2. 레코드가 캡슐화되지 않았다면 레코드 캡슐화

  3. 캡슐화된 객체 안의 private 필드명 변경 후 그에 맞게 내부 메서드 수정

  4. 테스트

  5. 생성자의 매개변수 중 필드와 이름이 겹치는 게 있다면 함수 선언 바꾸기로 변경

  6. 접근자 이름 변경

Example

class Organization { // 2. 캡슐화
    constructor(data) {
        this._title = data.title; // 3. 필드명 변경 후 메서드 수정
        this._country = data._country;
    }
    get title() { return this._title; } // 3.
    set title(aString) { this._title = aString; } // 3.
    get country() { return this._country; }
    set country(aCountryCode) { this._country = aCountryCode; }
}

const organization = new Organization({ // 6. 접근자 이름 변경
    title: '애크미 구스베리',
    country: 'GB',
});

파생 변수를 질의 함수로 바꾸기

가변 데이터의 유효 범위를 가능한 좁혀야 한다.

값을 쉽게 계산해낼 수 있는 변수들을 모두 제거하자.

  • 새로운 데이터 구조를 생성하는 변형 연산은 예외다.

개요

Before

get discountedTotal() { return this._discountedTotal; }
set discountedTotal(aNumber) {
    const old = this._discount;
    this._discount = aNumber;
    this._discountTotal += old - aNumber;
}

After

get discountedTotal() { return this._baseTotal - this._discount; }
set discountedTotal(aNumber) { this._discount = aNumber; }

절차

  1. 변수 값이 갱신되는 지점 찾기

    • 필요 시 변수 쪼개기로 각 갱신 지점에서 변수 분리하기

  2. 해당 변수의 값을 계산해주는 함수 만들기

  3. 함수의 계산 결과가 변수의 값과 같은지 테스트

  4. 변수를 읽는 코드를 모두 함수 호출로 수정 후 테스트

  5. 변수를 선언하고 갱신하는 코드에 죽은 코드 제거하기 적용

Example

class ProductionPlan {
    constructor(production) {
        this._initialProduction = production; // 1. 변수 쪼개기
        this._productionAccumulator = 0; // 1.
        this._adjustments = [];
    }
    get production() { return this._initialProduction + this._productionAccumulator; } // 2. 변수 값 계산
    get calculatedProductionAccumulator() { return this._adjustments.reduce((sum, a) => sum + a.amount, 0); }
    applyAdjustment(anAdjustment) { this._adjustments.push(anAdjustment); }
}

참조를 값으로 바꾸기

참조로 다루는 경우는 내부 객체를 그대로 둔 채 객체의 속성만 갱신

값으로 다루는 경우 새로운 속성을 담은 객체로 기존 내부 객체를 대체

값 객체는 불변이기 때문에 자유롭게 활용하기 좋고, 분산 시스템과 동시성 시스템에서 유용하다.

  • 단, 객체를 공유하고자 한다면 공유 객체를 참조로 다뤄야 한다.

  • 반대 리팩터링 : 값을 참조로 바꾸기

개요

Before

class Product {
    applyDiscount(arg) {
        this._price.amount -= arg;
    }
}

After

class Product {
    applyDiscount(arg) {
        this._price = new Money(this._price.amount - arg, this._price.currency);
    }
}

절차

  1. 후보 클래스가 불변인지 확인하기 (불변으로 만들기)

  2. 필드들의 세터 제거하기

  3. 값 객체의 필드들을 사용하는 동치성 비교 메서드 만들기

    • JAVA 에서는 Object.equals(), Object.hashCode() method override.

Example

/** Person ********************************************/
class Person {
    constructor() { 
        this._telephoneNumber = new TelephoneNumber();
    }
    get officeAreaCode() { return this._telephoneNumber.areaCode; }
    set officeAreaCode(arg) { this._telephoneNumber = new TelephoneNumber(arg, this.officeNumber); } // 1.
    get officeNumber() { return this._telephoneNumber.number; }
    set officeNumber(arg) {  this._telephoneNumber = new TelephoneNumber(this.officeNumber, arg);} // 1.
}
/** TelephoneNumber ********************************************/
class TelephoneNumber {
    constructor(areaCode, number) { // 1. 불변으로 만들기
        this._areaCode = areaCode;
        this._number = number;
    }
    equals(other) { // 3. 동치성 비교
        if (!(other instanceof TelephoneNumber)) { return false; }
        return this.areaCode === other.areaCode && this.number === other.number;
    }
    get areaCode() { return this.areaCode; }
    get number() { return this.number; }
    // 2. 필드 세터 제거
}

값을 참조로 바꾸기

같은 데이터를 물리적으로 복제해 사용할 때 데이터를 갱신해야 한다면 값을 참조로 바꾸자.

  • 반대 리팩터링 : 참조를 값으로 바꾸기

개요

Before

let customer = new customer(customerData);

After

let customer = customerRepository.get(customer.id);

절차

  1. 같은 부류에 속하는 객체들을 보관할 저장소(객체) 만들기

  2. 생성자에서 특정 객체를 정확히 찾아낼 방법이 있는지 확인하기

  3. 호스트 객체의 생성자들을 수정하여 필요한 객체를 이 저장소에서 찾도록 하기

  4. 테스트

매직 리터럴 바꾸기

매직 리터럴 : 소스 코드의 여러 곳에 등장하는 일반적인 리터럴 값

숫자 대신 상수를 사용하여 코드 자체가 뜻을 분명하게 드러내도록 해보자.

  • 상수가 특별한 비교 로직에 주로 쓰인다면 함수 호출로 바꾸는 쪽을 선호.

    • aValue === "M" 대신 isMali(aValue)

  • 상수를 과용하지는 말자.

    • const ONE = 1

개요

Before

function potentialEnergy(mass, height) {
    return mass * 9.81 * height;
}

After

const STANDARD_GRAVITY = 9.81;
function potentialEnergy(mass, height) {
    return mass * STANDARD_GRAVITY * height;
}

절차

  1. 상수를 선언하고 매직 리터럴 대입

  2. 해당 리터럴이 사용되는 곳 찾기

  3. 리터럴을 상수로 대체한 후 테스트

Last updated 1 year ago