티스토리 뷰

목차

1. 자료구조 vs 객체

2. 객체 - 디미터 법칙

3. DTO

4. Active Record vs Data Mapper

 

 

 

 

 

1. 자료 구조 vs 객체

자료 구조(Data Structure) 객체(Object)
데이터 그 자체 비즈니스 로직과 관련
자료를 공개한다. 자료를 숨기고 추상화한다.
자료를 다루는 함수만 공개한다.
변수 사이에 조회 함수와 설정 함수로 변수를 다룬다고 객체가 되지 않는다. (getter, setter) 추상 인터페이스를 제공해 사용자가 구현을 모른 채 자료의 핵심을 조작할 수 있다.

 

예시 (1) Vehicle - 자료 구조

조회 함수와 설정 함수로 자료를 공개한다.

public interface Vehicle {
    double getFuelTankCapacityInGallons();
    double getGallonsOfGasoline();
}

public class Car implements Vehicle {
    double fuelTankCapacityInGallons;
    double gallonsOfGasoline;

    @Override
    public double getFuelTankCapacityInGallons() {
        return fuelTankCapacityInGallons;
    }

    @Override
    public double getGallonsOfGasoline() {
        return gallonsOfGasoline;
    }
}

 

예시 (1) Vehicle - 객체

자료를 숨기고 추상화한다. 자료를 다루는 함수만 공개한다.

public interface Vehicle {
    double getPercentFuelRemaining();
}

public class Car implements Vehicle {
    double fuelTankCapacityInGallons;
    double gallonsOfGasoline;


    @Override
    public double getPercentFuelRemaining() {
        return this.gallonsOfGasoline / this.fuelTankCapacityInGallons * 100;
    }
}

 

예시 (2) Shape - 자료 구조

절차적인 코드는 새로운 자료 구조(도형 클래스)를 추가하기 어렵다.

하지만 Geometry 클래스의 함수를 고쳐야 한다.

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();
    }
}

 

예시 (2) 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;
    }
}

상황에 맞는 선택을 하면 된다.

  • 자료 구조를 사용하는 절차적인 코드는 기본 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽다.
  • 절차적인 코드는 새로운 자료 구조를 추가하기 어렵다. 그러려면 모든 함수를 고쳐야 한다.
  • 객체지향 코드는 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉽다.
  • 객체지향 코드는 새로운 함수를 추가하기 어렵다. 그러려면 모든 클래스를 고쳐야 한다.

 

2. 객체 - 디미터 법칙

디미터 법칙은 잘 알려진 휴리스틱으로 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙이다.

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

(휴리스틱 : 경험에 기반하여 문제를 해결 하기 위해 발견한 방법. 의사 결정을 단순화 하기 위한 법칙들.)

  • 클래스 C
  • 자신이 생성한 객체
  • 자신의 인수로 넘어온 객체
  • C 인스턴스 변수에 저장된 객체

위의 객체에서 허용된 메서드가 반환하는 객체의 메서드는 호출하면 안된다.

다음과 같은 코드를 기차 충돌이라 부른다.

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

 

디미터 법칙을 위반하는지 여부는 ctxt, options, scratchDir이 자료 구조인지 객체인지에 달렸다.

자료 구조라면 당연히 내부 구조를 노출하므로 디미터 법칙이 적용되지 않고 다음과 같이 구현할 수 있다.

final String outputDir = ctxt.options.scratchDir.absolutePath;

객체라면 내부 구조를 숨겨야 하므로 디미터 법칙을 위한한다.

따라서 객체에게 뭔가를 하라고 메세지를 전달해야지 속을 드러내라고 말하면 안된다.

코드가 데이터를 가져오는 근본 원인을 찾아 다음과 같이 코드를 수정하도록 한다.

임시 디렉터리의 절대 경로를 얻으려는 이유가 임시 파일을 생성하기 위한 목적이기 때문에

객체에게 임시 파일을 생성하라고 명령하도록 수정한다.

BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);

 

3. DTO

DTO(Data Transfer Object) : 자료 구조

다른 계층 가 데이터를 교환할 때 사용

  • 로직 없이 필드만 갖는다.
  • 일반적으로 클래스 명이 Dto or DTO로 끝난다.
  • getter/setter를 갖기도 한다.

 

Beans

Java Beans : 데이터 표현이 목적인 자바 객체

  • 멤버 변수는 private 속성이다.
  • getter와 setter를 가진다.

 

4. Active Record vs Data Mapper

Active Record

Database row를 객체에 매핑하는 패턴

비즈니스 로직 메서드를 추가해 객체로 취급하는 건 바람직하지 않다.

비즈니스 로직을 담으면서 내부 자료를 숨기는 객체는 따로 생성한다.

  • 객체가 row를 담을 뿐 아니라 database에 대한 접근을 포함한다.
  • Person의 속성을 담을 뿐 아니라, 생성/수정도 객체 안에서 수행할 수 있다.
  • 사례 - Ruby on rails

 

Data Mapper

  • row를 담는 객체와 database에 접근할 수 있는 객체가 분리 되어 있다.
  • Person은 값만 담고 있고, 생성/수정 등 액션은 PersonMapper에서 담당 한다.
  • 사례-Hibernate

 

 

 

 

 

 

 

출처

Clean Code(클린 코드) 애자일 소프트웨어 장인 정신 - 로버트 C. 마틴 지음

https://zero-base.co.kr/category_dev_camp/cleancode_1book

728x90

'책 내용 정리 > 클린코드(clean code)' 카테고리의 다른 글

[클린코드] 8. 경계  (0) 2022.03.28
[클린코드] 7. 오류 처리  (0) 2022.03.25
[클린코드] 5. 형식 맞추기  (0) 2022.03.03
[클린코드] 4. 주석  (0) 2022.01.17
[클린코드] 9. 단위 테스트  (0) 2021.12.14