Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 30 |
Tags
- Postman
- java
- Git
- H2 설치
- Spring
- golang
- 오블완
- JPA
- 클린 코드
- 스프링
- 티스토리챌린지
- 코드업
- 파이썬
- Codeup
- Vue.js
- GitHub
- go
- thymeleaf
- springboot
- Python
- MySQL
- mariadb
- Gradle
- 알고리즘
- spring security
- Spring Boot
- 롬복
- 객사오
- 기초100제
- 클린코드
Archives
- Today
- Total
nyximos.log
[Clean Code] 6장, 객체와 자료 구조 본문
클린코드, 애자일 소프트웨어 장인정신
Robert C. Martin
들어가며
- 왜 많은 프로그래머가 조회get 함수와 설정set 함수를 당연하게 공개해 비공개 변수를 외부에 노출할까?
자료 추상화
- 구현을 감추려면 추상화가 필요하다.
- 추상 인터페이스를 제공해 사용자가 구현을 모른 채 핵심을 조작할 수 있어야 진정한 의미의 클래스다.
- 자료를 세세하게 공개하기보다는 추상적인 개념으로 표현하는 편이 좋다.
- 아무 생각 없이 조회/설정 함수를 추가하는 방법이 가장 나쁘다.
구체적인 Point 클래스
- 직교좌표계 사용
- 계별적으로 좌표값을 읽고 설정하게 강제
- 구현을 노출한다.
- 변수를 private로 설정해도 각 값마다 get/set 함수를 제공한다면 구현을 외부로 노출하는 셈
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);
}
자료/객체 비대칭
- 객체와 자료 구조 사이의 차이
- 객체 : 추상화 뒤로 자료를 숨김, 자료를 다루는 함수만 공개
- 자료 구조 : 자료를 그대로 공개, 별다른 함수 제공 🙅♀️
절차적인 도형 클래스
- 각 도형 클래스 → 간단한 자료 구조 🙆♀️ 메서드 🙅♀️
- 도형이 동작하는 방식 → Geometry 클래스에서 구현
- 새 도형을 추가하고 싶다면 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();
}
}
객체 지향적인 도형 클래스
- area() → 다형 메서드
- 새 도형을 추가해도 기존 함수에 아무런 영향을 미치지 않는다.
- 새 함수를 추가하고 싶다면 도형 클래스 전부를 고친다.
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 sidth;
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;
}
}
객체와 자료 구조는 근본적으로 양분 된다.
- 객체 지향 코드에서 어려운 변경 → 절차적인 코드에서 쉽다.
- 절차적인 코드에서 어려운 변경 → 객체 지향 코드에서 쉽다.
- 분별 있는 프로그래머는 모든 것이 객체라는 생각이 미신임을 잘 안다.
- 때로는 단순한 자료 구조와 절차적인 코드가 가장 적합한 상황도 있다.
디미터 법칙
- 잘 알려진 휴리스틱 heuristic
- 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙
- 객체 : 자료를 숨기고, 함수를 공개 → get 함수로 내부 구조를 공개하면 안된다는 의미
클래스 C의 메서드 f는 다음과 같은 객체의 메서드만 호출해야 한다.
- 클래스 C
- f가 생성한 객체
- f 인수가 넘어온 객체
- C 인스턴스 변수에 저장된 객체
위 객체에서 허용된 메서드가 반환하는 객체의 메서드는 호출하면 안된다.
기차 충돌 train wreck
여러 객체가 한 줄로 이어진 기차처럼 보이기 때문에 피하는 편이 좋다.
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
다음과 같이 나누는 편이 좋다.
Options opts ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
ctxt, Options, ScratchDir이 객체인지 아니면 자료구조인지에 따라 위반 여부가 결정된다.
- 객체 → 내부 구조를 숨겨야 하므로 디미터 법칙 위반
- 자료구조 → 내부 구조를 노출하므로 디미터 법칙 적용 🙅♀️
이렇게 하면 문제가 간단해진다.
- 객체 → 비공개 변수와 공개 함수 포함
- 자료구조 → 무조건 함수 없이 공개 변수만 포함
그러나 단순한 자료 구조에도 조회 함수와 설정 함수를 정의하라 요구하는 프레임워크와 표준이 존재한다.
잡종 구조
- 절반은 객체, 절반은 자료구조
- 새로운 함수나 새로운 자료 구조를 추가하기 어렵다.
- 프로그래머가 함수나 타입을 보호할지 공개할지 확신하지 못해(더 나쁘게는 무지해) 어중간하게 내놓은 설계에 불과하다.
구조체 감추기
ctxt, options, scratchDir이 객체라면 → 내부 구조를 감춰야 한다.
임시 디렉터리의 절대 경로를 얻는 방법 1
ctxt.getAbsolutePathOfScratchDirectoryOption();
- ctxt객체에 공개해야 하는 메서드가 너무 많아진다.
임시 디렉터리의 절대 경로를 얻는 방법 2
ctxt.getScratchDirectoryOption().getAbsolutePath();
- getScratchDirectoryOption()이 객체가 아니라 자료 구조를 반환한다고 가정한다.
ctxt가 객체라면 → 뭔가를 하라고 말해야된다.
절대 경로가 필요한 이유가 임시 파일을 생성하기 위해서라면
ctxt에 임시 파일을 생성하라고 시키자.
BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);
- ctxt는 내부 구조를 드러내지 않는다.
- 모듈에서 해당 함수는 자신이 몰라야 하는 여러 객체를 탐색할 필요가 없다.
- 디미터 법칙을 위반하지 않는다.
자료 전달 객체
Data Transfer Object, DTO
- 공개 변수만 있고 함수가 없는 클래스
- 데이터베이스와 통신하거나 소켓에서 받은 메세지의 구문을 분석할 때 유용하다.
- DTO는 데이터베이스에 저장된 가공되지 않은 정보를 애플리케이션 코드에서 사용할 객체로 변환하는 일련의 단계에서 가장 처음으로 사용하는 구조체
public class Address{
private String street;
private String streetExtra;
private String city;
private String state;
private String zip;
public Address(String street, String streetExtra, String city, String state, String zip){
this.street = street;
this.streetExtra = streetExtra;
this.city = city;
this.state = state;
this.zip = zip;
}
public String getStreet(){
return street;
}
public String getStreetExtra(){
return streetExtra;
}
public String getCity(){
return city;
}
public String getState(){
return state;
}
public String getZip(){
return zip;
}
}
활성 레코드
- DTO의 특수한 형태
- 공개 변수가 있거나 비공개 변수에 조회/설정 함수가 있는 자료구조
- save나 find 같은 탐색 함수도 제공한다
- 데이터베이스 테이블이나 다른 소스에서 자료를 직접 변환한다.
- 비즈니스 규칙을 담으면서 내부 자료를 숨기는 객체는 따로 생성한다.
결론
객체
- 동작 공개 , 자료 숨김
- 기존 동작 변경 🙅♀️ 새 객체 타입 추가 → 쉬움
- 기존 객체에 새 동작 추가 → 어려움
자료 구조
- 별다른 동작 없이 자료 노출
- 기존 자료에 새 동작 추가 → 쉬움
- 기존 함수에 새 자료 구조 추가 → 어려움
시스템을 구현할 때
- 새로운 자료 타입을 추가하는 유연성이 필요하다. → 객체
- 새로운 동작을 추가하는 유연성이 필요하다. → 자료 구조 & 절차적인 코드
'Books' 카테고리의 다른 글
[Clean Code] 8장, 경계 (0) | 2022.08.02 |
---|---|
[Clean Code] 7장, 오류 처리 (0) | 2022.02.24 |
[Clean Code] 5장, 형식 맞추기 (0) | 2022.02.18 |
[Clean Code] 4장, 주석 (0) | 2022.02.16 |
[Clean Code] 3장, 함수 (0) | 2022.02.12 |