1. 의미있는 이름
의도를 분명히 밝혀라
- 클래스로 정의하기
이름을 정할 때
1) 존재 이유
2) 수행 기능
3) 사용 방법
정보를 잘못 전달하지 말 것
1) 약속된 용어에 대한 사용
ex) String accountList = "송혜교, 전지현, 김태희, 고소영"; (X)
-> List를 쓰는 것은 List 타입일 때 사용해야 정보를 올바르게 전달할 수 있다
2) 유사한 이름을 사용하지 않는다
3) 개념 단위로 묶을 때 유사 표기법을 사용하기
4) 구분하기 어려운 대문자/ 소문자 사용 지양
의미있게 구분하기 : 각 메서드 뒤에 1, A, B 등으로만 구분하는 것은 좋지 못한 구분이다
발음하기 쉬운 이름짓기
검색하기 쉬운 이름 사용하기
인코딩 피하기
이름에 데이터 타입을 명시하는 것은 이후 형변환 등으로 타입이 변경되어도 변수명은 동일하기 때문에, 혼동을 줄 수 있다
~ 클래스 & 객체 : 명사, 명사구
~ 메서드 : 접근자 - get, 변경자 - set, 조건자 - is
한 개념에 한 단어 사용하기 : 클래스마다 유사한 메서드에 fetch, retrieve, get으로 각각 부르면 혼동을 줄 수 있다
의미있는 맥락 추가하기 : 고유 변수만으로는 의미가 충분하지 않을 때, 접두어를 사용해 의미를 명확히 한다
+ 불필요한 맥락 제거하기
2. 함수
1) 작게 만들어라
- 블록과 들여쓰기를 통해 클린한 코드 작성
- 함수의 기능은 한 가지여야 한다
- 섹션으로 나눌 수 있는 함수는 기능이 하나 이상이라는 뜻이므로, 나눌 수 없는 단계까지 나눠야 한다
- 하나의 함수 다음에는 추상화 수준이 한 단계씩 낮은 함수가 온다
~ 예를 들어, "DB Table의 데이터를 읽는다." → "DB의 Connection 맺고 객체를 반환한다." → "Connection 객체를 이용해서 SQL문을 실행시켜 데이터를 가져온다."
2) Switch
Switch문을 추상 팩토리에 숨겨 클린 코드 작성하기
public abstract class Employee {
public abstract boolean isPayday();
public abstract Money calculatePay();
public abstract void deliverPay();
}
public interface EmployeeFactory {
public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}
@Configure
public class EmployeeFactoryImpl implements EmployeeFactory {
public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
switch(r.type){
case COMMISSION:
return new ComissionedEmployee(r);
case HOURLY:
return new HourlyEmployee(r);
case SALARIED:
return new SalariedEmployee(r);
}
}
}
@Service
public class EmployService{
private EmployeeFactoryImpl employeeFactory;
public Money getTotalAmount(EmployeeRecord r){
Employee e = employeeFactory.makeEmployee(r);
if(e.isPayday()){
return e.calculatePay();
else
return Money.ZERO;
}
3) 서술적인 이름 사용하기
이름을 보며 유추한 기능이 그대로 동작할 수 있도록 이름 정하기
4) 함수, 인수 타입
~ Output(Or Result) 타입
appendFooter(s)
객체 지향적인 코드는 this를 사용하므로 appendFooter(s)보다는 report.appendFooter();가 더 명확한 코드이다
일반적으로 출력 인수는 피해야 한다. 함수에서 상태를 변경해야 한다면 함수가 속한 객체 상태를 변경하는 방식이 더 좋다
5) 사이드 이펙트 주의하기
6) 명령과 조회 분리하기
* 혼용된 코드
@Test
public void getListDirectoryTest() {
String directoryPath = "jinho/x";
try {
boolean isExisted = Optional.ofNullable(webHdfsService.getListDirectory(directoryPath).execute().body())
.isPresent();
log.info("[RESULT] isExisted ::: {}", isExisted);
if(!isExisted){
Optional.ofNullable(webHdfsService.makeNewDirectory(directoryPath).execute().body())
.orElse(new HashMap<String, Boolean>())
.getOrDefault("boolean", false);
}
RespFileStatuses dirList = webHdfsService.getListDirectory(directoryPath).execute().body();
log.info("[RESULT] GetListDirectory ::: {}", dirList);
} catch (IOException e) {
log.error(e.getMessage());
}
}
* 분리한 코드
@SpringBootTest
@Slf4j
public class HdfsServiceTest {
@Autowired
private WebHdfsService webHdfsService;
@Autowired
private HdfsService hdfsService;
@Test
public void getListDirectoryTest() {
String directoryPath = "jinho";
try {
if(!hdfsService.isExistedDirectory(directoryPath))
hdfsService.makeDirectory(directoryPath);
RespFileStatuses dirList = hdfsService.getListDirectory(directoryPath);
log.info("[RESULT] GetListDirectory ::: {}", dirList);
} catch (IOException e) {
log.error(e.getMessage());
}
}
}
@Service
@RequiredArgsConstructor
public class HdfsService {
private final WebHdfsService webHdfsService;
public boolean isExistedDirectory(String toPath) throws IOException {
Optional<RespFileStatuses> response = Optional.ofNullable(webHdfsService.getListDirectory(toPath).execute().body());
return response.isPresent();
}
public boolean makeDirectory(String toPath) throws IOException {
return Optional.ofNullable(webHdfsService.makeNewDirectory(toPath).execute().body())
.orElse(new HashMap<String, Boolean>())
.getOrDefault("boolean", false);
}
public RespFileStatuses getListDirectory(String toPath) throws IOException {
return webHdfsService.getListDirectory(toPath).execute().body();
}
}
7) 오류 코드보다 예외를 사용하기
Enum 클래스 등으로 오류 코드를 정의해 사용하는 경우 비즈니스 로직과 예외처리 로직이 섞일 수 있다.
Try - Catch가 더 명확하다
3. 기존 코드와 스타일 맞추기
4 - 1. 객체와 자료구조
객체의 인스턴스 변수를 정의할 때 항상 private로 하는데, get/setter를 사용하는 경우 그 의미가 퇴색된다
OOP를 지키는 클래스는 get/setter가 아닌 추상 인터페이스를 제공해 사용자가 구현체를 모르는 상태로 기능을 수행할 수 있어야 한다
1) 객체 : 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개한다
2) 자료 구조 : 자료를 그대로 공개하며 함수를 제공하지 않는다. 자료 구조를 다루는 함수는 외부에 존재한다
* 절차 지향 코드(자료 구조)의 예시
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(Obeject shape) throw 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 c.radius * c.radius;
}
throw NoSuchShapeException()'
}
}
- 각 도형 클래스는 자료 구조
- 도형을 다루는 책임은 Geometry 클래스에게 있다
- 도형의 둘레를 구하는 함수를 추가하고 싶은 경우, G. 클래스에 함수를 추가하면 된다. 자료 구조에는 아무 영향이 없다
- 다만, 자료 구조(도형)를 추가하고 싶은 경우에는 G. 클래스 및 그 밖의 자료 구조에 의존하는 모든 클래스를 수정해야 한다
* 객체 지향 코드(객체)의 예시
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 width;
private double height;
public double area(){
return width * height;
};
}
- 새 도형을 추가하는 경우 Shape 인터페이스를 상속하기만 하면 된다
- 그러나 새로운 기능을 추가하고 싶은 경우, 인터페이스를 수정하고 이를 상속하는 모든 도형 클래스를 수정해야 한다
~ 종류의 추가는 쉽지만, 기능을 추가하는 것에 보다 많은 자원이 필요하다
-> 때에 따라 적절한 방식을 사용해야 한다
4 - 2. 디미터 법칙
모듈은 자신이 조작하는 객체의 내부 구현을 몰라야 한다
* 자료 구조에는 이 법칙이 적용되지 않는다 ~ 자료 구조의 모든 상태 값은 public으로 외부에 공개되어야 하기 때문
~ 객체에게 적절한 책임을 할당하고 적절한 추상화 수준으로 정의한다면, 모듈에서 해당 함수는 불필요하게 여러 함수를 탐색할 필요가 없다
4 - 3. 자료 전달 객체 : DTO
자료 구조체의 전형적인 형태는 public 변수만 있고, 비즈니스 로직이 없는 형태로, DTO라고 한다
'개발공부 > 원티드 챌린지 정리' 카테고리의 다른 글
9월 백엔드 챌린지 정리 : 클린 코드 (3) (0) | 2024.03.14 |
---|---|
9월 백엔드 챌린지 정리 : 클린 코드 (2) (1) | 2024.02.25 |
8월 백엔드 챌린지 : 도커 프로 (3) (0) | 2023.10.02 |
8월 백엔드 챌린지 : 도커 프로 (2) (0) | 2023.10.02 |
8월 백엔드 챌린지 : 도커 프로 (1) (0) | 2023.09.21 |