Blog
책 리뷰
자바 스프링 개발자를 위한 실용주의 프로그래밍

자바 스프링 개발자를 위한 실용주의 프로그래밍

객체지향

1장 절차지향과 비교하기

  • 순차지향과 절차지향은 엄밀히 다른 개념임.

    • 순차지향 프로그래밍 : 말 그대로 코드를 위에서 아래로 읽음.
    • 절차지향 프로그래밍 : 함수 지향 프로그래밍.
  • 절차지향은 책임을 프로시저로 나누고 프로시저에 할당.

  • 객체지향은 책임을 객체로 나누고 객체에 할당.

C언어는 객체지향을 구현할 수 없는 이유

객체를 추상화한 역할에 책임을 할당함. 예를 들어서

public interface Calcuable {
  void calc(int a, int b)
}

이런식으로 인터페이스를 만들어서 객체를 추상화하고, 이 인터페이스를 구현한 클래스를 만들어서 책임을 할당함.
그러나 C언어는 이런 추상화를 할 수 없으며, 추상화가 불가능하다는 특징 때문에 객체지향을 구현할 수 없음.

객체지향이란?

객체가 책임을 갖게 됐고 객체의 역할이 정해졌으며, 어떤 목표를 달성하기 위해서 서로 다른 객체와 협력을 함. 이게 객체지향의 본질임.

객체지향 사고방식, TDA

  • Tell Dont Ask : 객체에게 물어보지 말고 시켜라 라는 원칙

예를 들어서, 아래와 같은 코드가 있다고 치면

public class Shop {
  public void sell(Account account, Product product) {
    if (account.getBalance() > product.getPrice()) {
      ...
    }
  }
}

이 코드는 객체에게 물어보는 코드임. 객체에게 물어보는 것이 아니라 객체에게 시키는 코드로 바꾸면 아래와 같음.

public class Shop {
  public void sell(Account account, Product product) {
    if(account.canBuy(product)) {
      ...
    }
  }
}

이러한 변경을 통해 아래와 같은 3가지 이점을 얻을 수 있음.

  • 유연성 증가 : 각 객체가 자신의 상태와 동작을 관리하므로 외부에서 객체 내부의 상태를 직접 알 필요가 없음
  • 결합도 감소 : 객체 내부의 세부 사항이 감춰지므로 다른 객체와의 상호 의존성을 줄일 수 있음.
  • 재사용성 향상 : canBuy() 메서드를 다른 클래스에서도 사용할 수 있음.

2장 객체의 종류

VO

  • VO의 특징
    • 불변성 : 값이 변하지 않음.
      • 불변성을 지키기 위해서 모든 필드를 final로 선언.
      • 모든 값은 원시타입이어야 함 👉 참조 타입이 있다면 참조 타입 내의 값이 변경 가능할 수 있기 때문.
        • 👆 위와 같은 경우 안됨.
        • VO 안의 모든 함수는 순수함수여야 함. (항상 같은 값을 반환해야 하고 Random 이딴거 쓰면 안됨.)
        • final class로 선언되야 함. (상속되면 안됨.)
    • 동등성 : 값의 가치는 항상 같음.
      • equals()와 hashCode() 메서드를 오버라이딩해서 동등성을 보장. (값이 같으면 동일한 객체로 취급)
    • 자가검증 : 값은 그 자체로 올바름. 1은 사실 1.01이지 않을까? 같은 고민을 할 필요 X.

Entity

Entity는 3종류로 나눔.

  1. 도메인 엔티티
  2. DB 엔티티
  3. JPA 엔티티

프로그래밍 언어와 데이터베이스 분야에서 표현하고 싶은 유무형의 자산 정보를 지칭하는 데 개체라는 용어를 사용.

NoSQL에서 Entity

관계형 데이터베이스에서 사용하는 엔티티라는 용어는 도큐먼트 데이터베이스에서 도큐먼트라는 용어에 대응됨.

3장 행동

데이터 위주의 사고와 행동 위주의 사고

자동차를 만들어 달라는 요청에 두 개발자가 아래와 같이 구현함.

  • A 개발자
public class Car {
  private Frame frame;
  private Engine engine;
  private Tire tire;
  ...
}
  • B 개발자
public class Car {
  public void drive() {}
  public void changeDirection() {}
  public void accelerate() {}
  ...
}

A 개발자를 데이터 위주의 사고를 가진 개발자, B 개발자를 행동 위주의 사고를 가진 개발자라고 함.

  • 데이터 위주로 만들어진 클래스는 구조적인 데이터 덩어리를 만드는 데 사용하는 구조체와 다를바 없음.
    • 전혀 객체지향 스럽지 않음.
  • 객체를 구분 짓는 요인은 데이터가 아닌 행동임.

덕 타이핑이란

덕 테스트: 만약 어떤 새가 뒤뚱뒤뚱 걷고, 헤엄치고, 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다.

  • 덕 타이핑은 덕 테스트에서 유래한 용어임.
  • 위 말을 개발자 관점에서 보면 행동이 같다면 같은 클래스로 부르겠다.라는 의미.
  • Typescript는 덕타이핑을 지원.
class Duck {
  walk() {
    console.log('walk');
  }
  swim() {
    console.log('swim');
  }
}
 
class Person {
  walk() {
    console.log('walk');
  }
  swim() {
    console.log('swim');
  }
}
 
val duck: Duck = new Person();
  • 행동이 곧 역할을 정의하고 역할이 곧 객체를 정의함.

구현을 생각하면 데이터 위주의 사고가 된다

public class Car {
  private int degree; // 자동차의 각도(0도 ~ 360도)
 
  public void changeDirection(int degree) {
    this.degree = degree;
  }
}
  • 방향을 바꾸는 행동을 구현할려고 하다보니 degree라는 필드가 생김.
    • 데이터 위주의 사고로 돌아옴.
    • 구현을 고민했기 때문.
  • 행동을 고민하면서 구현이나 알고리즘을 고민해서는 안됨.
  • 협업시 인터페이스를 정의해놓고 각자 자리에서 본인의 역할을 다하면 됨.

인터페이스와 행동을 다르다

  • 인터페이스는 외부에서 어떤 객체에게 행동을 시키고자 할 때 메시지를 보낼 수 있는 창구.
  • 인터페이스란 어떤 행동을 지시하는 방법의 집합.

행동 위주의 사고를 하는 방법

  • 실체에 집중할 때 데이터 위주의 사고를 하고 역할에 집중할 때 행동 위주의 사고를 함.
    • ex : 자동차와 탈것, 자동차 -> 실체 / 탈것 -> 역할
  • 행동과 역할에 집중하라는 것이 단순히 추상화를 많이 하라는 뜻이 아님.

함수와 메서드의 차이

  • 함수의 각 입력값은 정확히 하나의 출력값으로 대응됨.
    • 같은 입력에 대해 항상 같은 출력을 해야함.
    • 같은 입력에 대해 두 개의 출력을 갖는 것은 함수가 아님.
    • 예를 들어 Vehicle 인터페이스의 구현체 Car와 Airplane이 있을 때 vehicle.move() 함수의 결과가 다를 수 있다면 그건 함수가 아님.
  • 메서드란 어떤 메시지를 처리해 달라는 요청을 받았을 때 이를 어떻게 처리하는지 방법(method)를 정의한 것.