[Clean Code][TIL] 객체와 자료구조
이 포스트는 로버트 C.마틴의 Clean Code 속 6장(118p ~ 128p) 내용에 대한 정리입니다.
“새로운 자료 타입을 추가하는 유연성이 필요하면 객체가 더 적합하다.
다른 경우로 새로운 동작을 추가하는 유연성이 필요하면 자료구조와 절차적인 코드가 더 적합하다.” (6장 128p)
자료의 추상화
자료를 표현할 때, 자료 구조를 외부에 전부 보여주는 것 보다는 추상적인 개념 으로 표현하여
사용자가 내부 구현을 모른 채 자료의 핵심을 조작할 수 있도록 하는 것이 이상적입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Shape {
public:
virtual double area();
}
class Square : public Shape {
double side;
public:
double area() {
return side* side;
}
}
class Rectangle : public Shape {
double width;
double height;
public:
double area() {
return width * height;
}
}
위의 코드에서 area() 함수말고 paint_area() 를 추가하고 싶다면 어떻게 해야 할까요?
Shape 클래스에 virtual paint_area() 를 추가하고 Square, Rectangle 클래스에서
paint_area() 함수를 Overriding 해야 합니다. 지금은 추가해야 하는 클래스가 2개 밖에 없지만
상속 받는 클래스가 많아질수록 추가해야할 코드가 많아질 것입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Shape {
public:
virtual ~Shape() {}
};
class Square : public Shape {
public:
Square(double in) : side(in) {}
double side;
};
class Rectangle : public Shape {
public:
Rectangle(double wid, double hei) : width(wid), height(hei) {}
double width;
double height;
};
class Geometry {
public:
double area(Shape *shape) {
if (Square *square = dynamic_cast<Square *>(shape)) {
return square->side * square->side;
} else if (Rectangle *rectangle = dynamic_cast<Rectangle *>(shape)) {
return rectangle->width * rectangle->height;
}
return 0;
}
};
이번에는 클래스가 절차지향인 구조이지만 이전 코드와 다르게
Geometry 클래스에서 함수만 추가하고 Square, Rectangle 클래스에서는 아무 작업을 할 필요가 없습니다.
즉, 객체 지향에서 어려운 변경은 절차적인 코드에서 쉬우며,
절차적인 코드에서 어려운 변경은 객체 지향 코드에서 쉽습니다.
따라서 작성하는 코드에 따라 최적인 해결책을 선택해야 합니다.
디미터의 법칙
모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙으로
객체는 조회 함수로 내부 구조를 공개하면 안된다는 뜻입니다.
다음과 같은 코드가 디미터의 법칙을 어긴 코드입니다.
1
string dir_path = system.get_options().get_dir().get_path();
Options, Directory 가 객체라면 get_~() 메서드를 사용하는 것 자체가 위반이지만
자료 구조라면 다음과 같이 디미터의 법칙을 위반하지 않고 수정할 수 있습니다.
1
2
3
Options options = system.get_options();
Directory directory = options.get_dir();
string path = directory.get_path();