데이터 체계화 - ChoDragon9/posts GitHub Wiki

데이터 체계화

데이터 연동에 있어서 직접/간접/단방향/양방향 등과 같은 내용을 다룬다.

필드 자체 캡슐화(Self Encapsulate Field)

필드에 직접 접근하던 중 그 필드로의 결합의 문제가 생길 때 읽기/쓰기 메서드를 작성해서 메서드를 통해서만 접근

// Before
class MyClass {
  constructor () {
    this._low = null
    this._high = null
  }
  includes (arg) {
    return arg >= this._low && arg <= this._high
  }
}

// After
class MyClass {
  constructor () {
    this._low = null
    this._high = null
  }
  includes (arg) {
    return arg >= this.getLow() && arg <= this.getHigh()
  }
  getLow () { return this._low }
  getHigh () { return this._high }
}

데이터 값을 객체로 전환(Replace Data Value with Object)

데이터 항목에 데이터나 기능을 더 추가해야 할 때

// Before
class Order {
  constructor (customer) {
    this.customer = customer
  }
  getCustomer () { return this.customer }
  setCustomer (customer) { this.customer = customer }
}

// After
class Customer {
  constructor (name) {
    this.name = name
  }
  getName () { return this.name }
}
class Order {
  constructor (customer) {
    this.customer = new Customer(customer)
  }
  getCustomer () { return this.customer.getName() }
  setCustomer (customer) {
    this.customer = new Customer(customer) 
  }
}

값을 참조로 전환(Change Value to Reference)

클래스에 같은 인스턴스가 많이 들어 있어서 이것들을 하나의 객체로 바꿔야 할 때

// Before
class Customer {
  constructor (name) {
    this.name = name
  }
  getName () { return this.name }
}
class Order {
  constructor (customer) {
    this.customer = new Customer(customer)
  }
  getCustomer () { return this.customer.getName() }
  setCustomer (customer) {
    this.customer = new Customer(customer) 
  }
}

// After
class Customer {
  constructor (name) {
    this.name = name
  }
  getName () { return this.name }
  static create (name) { // Factory Method
    return new Customer(name)
  }
}
class Order {
  constructor (customer) {
    this.customer = Customer.create(customer)
  }
  getCustomer () {
    return this.customer.getName()
  }
  setCustomer (customer) {
    this.customer = Customer.create(customer) 
  }
}

참조를 값으로 전환(Change Reference to Value)

참조 객체가 작고 수정할 수 없고 관리하기 힘들 때

배열을 객체로 전환(Replace Array with Object)

배열을 구성하는 특정 원소가 별의별 의미를 지닐 때 각 배열을 각 원소마다 필드가 하나씩 있는 객체로 전환한다.

배열은 흔히 데이터 정리에 사용되는 구조로 같은 비슷한 데이터 구조일 때만 사용하자.

// Before
const row = []
row[0] = "Liverpool"
row[1] = 15

// After
const row = new Performance()
row.setName("Liverpool")
row.setWins(15)

관측 데이터 복제(Duplicate Observed Data)

도메인 데이터는 GUI 컨트롤 안에서만 사용 가능한데, 도메인 메서드가 그 데이터에 접근해야 할 때(Observer Pattern)

클래스의 단방향 연결을 양방향으로 전환(Change Unidirectional Association to Bidirectional)

두 클래스가 서로의 기능을 사용해야 하는 데 한 방향으로만 연결되어 있을

클래스의 양방향 연결을 단방향으로 전환(Change Bidirectional Association to Unidirectional)

두 클래스가 양방향으로 연결되어 있는 데 한 클래스가 다른 클래스의 기능을 더 이상 사용하지 않게 되었을 때

마법 숫자를 기호 상수로 전환(Replace Magic Number with Symbolic Constant)

특수 의미를 지닌 리터럴 숫자가 있을 때 의미를 살린 이름의 상수를 작성한 후 리터럴 숫자를 그 상수로 교체하자

필드 캡슐화(Encapsulate Field)

public 필드가 있을 때 그 필드를 private로 만들고 필드용 읽기/쓰기 메서드를 작성하자

컬렉션 캡슐화(Encapsulate Collection)

메서드가 컬렉션을 반환할 때 그 메서드가 읽기 전용 뷰를 반환하게 수정하고 추가 메서드와 삭제 메서드를 작성하자

// Before
class Person {
  constructor () {
    this.set = new Set()
  }
  getCourses () {
    return this.set
  }
  setCourses (courses) {
    this.set = courses
  }
}

// After
class Person {
  constructor () {
    this.set = new Set()
  }
  getCourses () {
    return new Set(...this.set)
  }
  addCourses (course) {
    this.set.add(course)
  }
  removeCourses (course) {
    this.set.delete(course)
  }
}

분류 부호를 하위클래스로 전환(Replace Type Code with Subclasses)

클래스 기능에 영향을 주는 변경불가 분류 부호가 있을 때 분류 부호를 하위 클래스로 만들자

// Before
class Employee {
  constructor () {
    this.ENGINEER = 9
    this.SALESMAN = 4
    this.type = 0
  }
}

// After
class Employee {
  constructor () { }
}
class Engineer extends Exployee {
  constructor () { super() }
}
class Salesman extends Exployee {
  constructor () { super() }
}

분류 부호를 상태/전략 패턴으로 전환(Replace Type Code with State/Strategy)

분류 부호가 클래스의 기능에 영향을 주지만 하위클래스로 전환할 수 없을 때 그 분류 부호를 상태 객체로 만들자

class Employee {
  constructor () {
    this.ENGINEER = 9
    this.SALESMAN = 4
    this.type = 0
  }
}

// After
class Employee {
  constructor () {
    this.type = null
  }
  setType (type) {
    this.type = EmployeeType.newType(type)
  }
}
class EmployeeType {
  static newType (type) {
    switch (type) {
      case 'ENGINEER':
        return new Engineer()
      case 'SALESMAN':
        return new Salesman()
    }
  }
}
class Engineer {
  constructor () { }
}
class Salesman {
  constructor () { }
}

하위클래스를 필드로 전환(Replace Subclass with Fields)

여러 하위클래스가 상수 데이터를 반환하는 메서드만 다룰 때 각 하위 클래스의 메서드를 상위클래스 필드로 전환하고 하위클래스는 전부 삭제하자

// Before
class Person {
  getCode () { }
}
class Male extends Person {
  getCode () {
    return 'M'
  }
}
class Female extends Person {
  getCode () {
    return 'F'
  }
}

// After
class Person {
  constructor (isMale, code) {
    this.code = code
    this.isMale = isMale
  }
  static createMale () {
    return new Person(true, 'M')
  }
  static createFemale () {
    return new Person(false, 'F')
  }
}