6장 객체와 자료구조

Seunghee Ryu·2023년 12월 11일
0

클린 코드

목록 보기
6/18

  • 새로운 자료 타입 추가에 대한 유연성이 필요할 때는 객체, 새로운 동작에 대한 유연성이 필요하면 자료 구조와 절차적인 코드를 사용하는것이 좋다
  • 상황에 맞는 방법을 선택하라

1. 자료 추상화

  • 구현을 감추려면 추상화가 필요하다
  • 추상 인터페이스를 제공해 사용자가 구현을 몰느채 자료의 핵심을 조작할 수 있어야 진정한 의미의 클래스다
  • 자료는 추상적인 개념으로 표현하는 것이 좋다

// 구체적인 Point 클래스
public class Point { 
  public double x; 
  public double y;
}

// 추상적인 Point 클래스
public interface Point {
  double getX();
  double getY();
  void setCartesian(double x, double y); 
  double getR();
  double getTheta();
  void setPolar(double r, double theta); 
}

자료와 객체의 차이

  • 객체 : 추상화 뒤에 자료를 숨긴 채 자료를 다루는 함수만 공개한다
  • 자료 구조 : 자료를 그대로 공개하며 별다른 함수를 제공하지 않는다

// 절차지향적인 코드
public class Square { 
  public Point topLeft; 
  public double side;
}

public class Rectangle { 
  public Point topLeft; 
  public double height; 
  public double width;
}

public class Circle { 
  public Point center; 
  public double radius;
}

public class Geometry {
  public final double PI = 3.141592653589793;
  
  public double area(Object shape) throws NoSuchShapeException {
    if (shape instanceof Square) { 
      Square s = (Square)shape; 
      return s.side * s.side;
    } else if (shape instanceof Rectangle) { 
      Rectangle r = (Rectangle)shape; 
      return r.height * r.width;
    } else if (shape instanceof Circle) {
      Circle c = (Circle)shape;
      return PI * c.radius * c.radius; 
    }
    throw new NoSuchShapeException(); 
  }
}

// 객체지향적인 코드
public class Square implements Shape { 
  private Point topLeft;
  private double side;
  
  public double area() { 
    return side * side;
  } 
}

public class Rectangle implements Shape { 
  private Point topLeft;
  private double height;
  private double width;

  public double area() { 
    return height * width;
  } 
}

public class Circle implements Shape { 
  private Point center;
  private double radius;
  public final double PI = 3.141592653589793;

  public double area() {
    return PI * radius * radius;
  } 
}
  • 절차지향적인 코드에서는 if문을 통해 절차적으로 처리한다
  • 새로운 참수를 추가하고 싶다면 geometry 내부에 하나의 함수를 새로 만들기만 하면 된다
  • 기존의 자료 구조에 영향을 끼치지 않는다
  • 새로운 도형인 오각형을 넣는다고 하면 geometry에 속한 모든 함수를 변경해줘야 한다
  • 객체를 사용하여 만들었을 경우 변수를 감추고 함수로만 외부에서 사용하도록 열어준다
  • 새로운 함수를 추가하고 싶다면 모든 클래스에 해당 함수를 구현해줘야 한다
  • 새로운 도형인 오각형을 추가하고 싶다면 새로운 클래스를 만들어 그에 맞는 메서드만 구현해주면 된다

3. 디미터 법칙

  • 객체는 조회 함수로 내부 구조를 공개하면 안된다

  • 클래스 C의 메서드 f는 이하의 메서드만 호출해야 한다
      1. Class C
      1. f가 생성한 객체
      1. f 인수로 넘어온 객체
      1. C instance 변수에 저장된 객체

기차 충돌


final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
  • 여러 객체가 한 줄로 이어진 기차처럼 보인다고 해서 이런 코드를 기차 충돌이라고 부른다
  • 위 코드는 피하고 아래와 같이 나누는 것이 좋다

Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
  • 이렇게 작성하는 경우 ctxt, optx, scratchDir이 객체인지 자료구조인지를 먼저 명확하게 해야한다
  • 객체인 경우 내부 구조가 노출되었기 때문에 디미터 법칙을 위반하게 된다
  • 자료 구조인 경우는 내부 구조를 노출하기 때문에 디미터 법칙이 적용되지 않는다

final String outputDir = ctxt.options.scratchDir.absolutePath; 
  • 자료 구조인 경우에는 의미없는 getter, setter를 사용하는 것보다 .을 이용해 잇는것이 좋다

잡종 구조

  • 객체와 자료구조가 섞인 잡종 구조는 피하는 것이 좋다

4. 해결책

구조체 감추기

  • ctxt, options, scratchDir이 객체일 때

String outFile = outputDir + "/" + className. replace( '.', '/') + ".class";
FileOutputStream fout = new FileOutputStream(outFile);
BufferedOutputStream bos = new BufferedOutputStream(fout);

// 위임
BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);
  • 절대 경로를 얻으려는 이유가 임시 파일을 생성하기 위함임을 알았다
  • 임시 파일을 생성하는 것을 해당 객체가 아닌 ctxt 객체에 위임을 하는 경우 ctxt는 불필요하게 내부 구조를 노출할 필요가 없어진다

자료 전달 객체

  • 공개 변수만 있고 함수가 없는 클래스(DTO : Data Transfer object)
  • 데이터를 전달만하며 가공되지 않은 원천정보를 애플리케이션 코드에서 사용할 객체로 변환하는 과정 중 가장 처음 사용하는 구조체

활성 레코드

  • DTO의 특수한 형태
  • DTO에서 save나 find같은 탐색 함수도 제공한다
  • 활성 레코드에 비즈니스 규칙 메소드를 추가하면 잡종 구조가 된다
  • 이를 해결하기 위해 활성 레코드는 자료 구조로 취급한다

개인적인 감상

  • DTO, DAO, VO의 구분이 어려웠기에 이번 기회를 통해 정리를 해보았다
  • 객체인지 자료 구조인지 구별하지 않고 사용해왔고 개념에 대한 정의 또한 없었기 때문에 객체와 자료구조에 대해 생각해 볼 수 있었다

추가적인 정리

  • private를 사용해서 외부에 노출시키고 싶지 않은 데이터를 보호하는데 private로 접근자를 설정한 변수가 외부로 노출되지 않는가?
  • 프로그래밍적으로는 노출되지 않지만 getter와 setter를 통해서 조작이 가능하다
  • 그렇기에 추상화와 메서드명을 통해 대략의 목적은 유추할 수 있으나 세부적인 내용은 유추하지 못하도록 작성한다
  • 자료구조는 객체가 데이터와 메서드를 분리시켜 객체의 역할을 축소시킨 모습니다
  • 자료구조를 사용하면 모듈에 동작을 정의하게 되면 이런 형태의 코딩 방식을 절차지향적 프로그래밍이라고 한다
  • 자료구조는 객체이지만 동작을 가지지 않기 때문에 외부의 동작에 사용되는 절차지향적 프로그래밍을 따르게 된다
  • 모든 것이 객체라는 생각은 미신이다

DAO, DTO, VO

  • DAO
    - Data Access Object
    - DB의 데이터에 접근하기 위한 객체
    - 직접 DB에 접근하여 data를 삽입, 삭제 , 조회 등 조작할 수 있는 기능을 수행한다
  • DTO
    - Data Transfer Object
    - 계층 간 데이터 교환을 위한 Java Bean을 의미
    - 로직을 가지지 않는 데이터 객체
    - getter, setter 메소드만 가진 클래스를 의미한다
  • VO
    - Value Object
    - Read-only 속성을 가진 값 오브젝트
    - 값 타입을 표현하기 위해 불변 클래스를 만들어 사용한다
    - 따라서 getter 기능만 존재한다

0개의 댓글