Clean Code 6. 객체와 자료 구조


자료 추상화

자료를 세세하게 공개하기 보다는 추상적인 개념으로 표현하는 편이 좋다. 각 값마다 get/set을 제공하는 것은 구현을 외부로 노출하는 셈이다.

구체적인 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); 
}

구현을 완전히 숨겼다.

자료/객체 비대칭

  • 객체 : 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개한다.
  • 자료 구조 : 자료를 그대로 공개하며 별다른 함수는 제공하지 않는다.
절차적인 도형 (Procedural Shape) - 자료 구조 사용
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(); 
  }
}
다형적인 도형 (Polymorphic Shape) - 객체 사용
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;
  } 
}
 새 함수 추가새 클레스 (또는 자료 구조) 추가
자료 구조기존 자료 구조 변경없이 가능모든 함수 변경
객체 지향모든 객체마다 함수 추가기존 함수 변경없이 가능

객체 지향 코드에서 어려운 변경은 절차적인 코드에서 쉬우며, 절차적인 코드에서 어려운 변경은 객체 지향 코드에서 쉽다.

디미터 법칙

모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다.

클래스 C의 메서드 f는 다음 객체의 메서드만 호출해야 한다.

  • 클래스 C
  • f가 생성한 객체
  • f 인수로 넘어온 객체
  • C 인스턴스 변수에 저장된 객체

다음의 코드들은 피하는게 좋다.

  • 기차 충돌(train wreck) : 한줄로 길게 연결된 코드
final String outputDir = ctxt.getOption().getScratchDir().getAbsolutePath();

이런 코드는 피하는게 좋다.

Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
  • 잡종 구조 : 절반은 객체, 절발은 자료 구조. 양쪽 세상에 단점만 모아놓은 구조다. 새로운 자료 구조 추가도 어렵고, 새로운 함수 추가도 어렵다.
  • 구조체 감추기

위 예제코드에서 왜 임시 폴더의 절대경로가 필요할까 ? 한참 아래로 내려가 보니 다음 코드가 있었다.

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

만약 해당 폴더에 임시 파일을 만드는게 목적이라면 ctxt 객체에게 시키는게 더 좋은 방법이다.

BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);

자료 전달 객체 (DTO: Data Transfer Object)

public class Address { 
  public String street; 
  public String streetExtra; 
  public String city; 
  public String state; 
  public String zip;
}

DTO는 굉장히 유용한 객체이다. 소켓에서 받은 메세지나 ORM용 객체를 해석할때 유용하다.

활성 레코드는 DTO의 특수한 형태다. 공개 변수가 있거나 비공개 변수에 get/set이 있는 자료 구조지만, 대게 save/find도 제공한다. 이런 활성 레코드에 비즈니스 규칙을 추가해 자료 구조를 객체로 취급하는 개발자가 흔하다. 활성 레코드는 자료 구조로 취급해야 한다. 비즈니스 규칙을 담으면서 내부 자료를 숨기는 객체는 따로 생성한다.

이 글이 도움이 되셨다면 공감 및 광고 클릭을 부탁드립니다 :)