개요
안녕하세요! 최근 끊임 없이 좋은 코드에 대한 고민을 이어나가고 있습니다. 저는 이에 대한 답을 여러 서적을 통해 찾고자 했는데요, 오늘은 “엘레강트 오브젝트”의 내용을 정리하며 저의 견해 또한 포함해보겠습니다.
1. 엘레강트 오브젝트의 짧게 둘러보기
•
-er로 끝나는 이름을 사용하지 마세요
◦
클래스의 이름은 무엇을 하는지가 아닌 무엇인지에 기반 해야 한다.
•
생성자 하나를 주 생성자로 만드세요
◦
하나의 주 생성자와 다수의 부 생성자를 사용한다.
•
생성자에 코드를 넣지 마세요
1. 출생
1.1 -er로 끝나는 이름을 사용하지 마세요
저자는 상당히 단호한 어투로 -er 에 대한 부정적인 견해를 말하고 있습니다. 여기서 말하는 -er은 어떤 것들이 있을까요? Controller, Manager 등이 있습니다.
사실 전 Controller 라는 네이밍을 굉장히 애용하는데요, Controller는 애플리케이션의 흐름을 처리한다는 역할에 잘 부합한다고 생각하기 때문입니다!
저자는 왜 -er 로 끝나는 이름을 지양하라고 말하는 걸까요?
클래스의 이름을 -er 방식으로 짓는다면 그건 객체들이 무엇을 하는지에 기반한 것이라고 합니다. 하지만 이름은 무엇인지에 기반을 하여 작성해야된다고 하는데요, 제가 생각한 저자의 의도는 다음과 같습니다.
대표적인 -er (Controller, Manager) 클래스들을 사용하면 그 클래스가 여러 가지 일을 수행하는 클래스로 여겨질 수 있으며, 이는 클래스가 많은 책임을 가질 수 있다.
하지만 저는 때에 따라 -er 접미사를 사용하는 것이 불가피 할 때도 있다고 생각합니다.
현재 진행하고 있는 우아한 프리 코스에서도 마찬가지 인데요, 애플리케이션의 분명한 시작점이 존재해야 된다고 생각합니다. 그리고 저는 그것이 흐름을 제어하는 역할을 가진 Controller 라고 생각합니다.
저자는 객체는 캡슐화된 데이터의 대표자로서 스스로 결정을 내리고 행동할 수 있는 자립 적인 존재라고 표현하고 있습니다. 따라서 Controller가 모든 책임을 갖는 것이 아닌 여러 도메인 객체에게 흐름을 요청하는 형태로 프로그래밍을 작성한다면 객체지향 다운 프로그래밍이 가능하다고 생각합니다.
1.2 생성자 하나를 주 생성자로 만드세요.
응집도가 높고 견고한 클래스에는 적은 수의 메서드와 상대적으로 더 많은 수의 생성자가 존재한다고 합니다.
프리 코스 기간 동안 진행했던 코드 리뷰에서도 제가 항상 강조했던 것이 많은 수의 메서드는 응집도를 낮춘다는 것이었는데요, 많은 생성자 또한 응집도를 높이는데 도움이 된다고 합니다.
생성자가 많은 거랑 응집도가 높은거랑 무슨 상관이야?
우선 생성자가 많다고 해서 항상 응집도가 높지는 않습니다. 하지만 생성자가 많으면 장점은 다음과 같습니다.
•
각각의 객체를 생성할 때 필요한 다양한 시나리오를 처리할 수 있다.
•
다양한 명시적 방법을 제공함으로써 사용자는 객체가 어떤 상태를 가질 수 있는지 알 수 있다.
•
객체의 생성을 정적 팩토리 메소드나 다양한 생성자를 통해 제어함으로써, 객체의 일관성 있는 상태를 유지할 수 있으며, 객체 내부의 복잡한 초기화 로직을 숨길 수 있다.
•
코드 중복을 줄이면서 동시에 다양한 시나리오에서 객체를 재사용할 수 있게 한다.
적절한 생성자의 개수를 찾는 것 역시 개발자의 주요한 덕목 중 하나입니다.
하지만 이렇게 이야기 하면 크게 와닿지 않습니다. 개발자는 코드로 말해야 하기 때문이죠!
자동차 경주 게임 미션의 코드 일부분을 가져와 봤습니다.
package racingcar.domain;
public class Car {
private final static int NAME_LENGTH_CRITERION = 5;
private final String name;
private final Position position;
private Car(final String name, Position position) {
this.name = name;
this.position = position;
}
public static Car fromName(String name) {
validateNameLength(name);
return new Car(name, new Position());
}
public static Car fromNameAndPosition(String name, int initialPosition) {
validateNameLength(name);
return new Car(name, new Position(initialPosition));
}
// ... (나머지 메소드와 검증 메소드)
}
Java
복사
주 생성자는 fromName()으로 기존에는 이름을 통한 자동차 객체의 생성을 허용하지만, 시나리오에 따라 시작 위치를 제공할 수도 있습니다. 이를 통해 다양한 시나리오에 유연하게 대처할 수 있습니다.
1.3 생성자에 코드를 넣지 마세요
코드가 없으면 생성자가 무슨 의미가 있지 싶을 수 있습니다. 여기서 말하는 코드는 생성자의 주요 역할 중 하나인 할당과 검증을 이야기 하는 것이 아닙니다!
생성자는 객체의 상태를 초기화 혹은 검증하는 역할을 할 뿐 별도의 로직을 수행하면 안됩니다.
잘못된 코드의 예시는 다음과 같습니다.
class Cash {
private int dollars;
Cash(String dollars) {
this.dollars = Integer.parseInt(dollars);
}
}
PHP
복사
다시 한번 강조하지만 핵심은 생성자에서는 오직 필드의 초기화만을 담당하고, 복잡한 초기화 로직이나 유효성 검사 등은 별도의 메소드나 팩토리 메소드 내에서 수행되어야 한다는 것입니다.
위 코드는 다음과 같이 바꿀 수 있습니다.
public class Cash {
private final int dollars;
private Cash(int dollars) {
if (dollars < 0) {
throw new IllegalArgumentException();
}
this.dollars = dollars;
}
public static Cash of(String dollarStr) {
try {
int parsedDollars = Integer.parseInt(dollarStr);
return new Cash(parsedDollars);
} catch (NumberFormatException e) {
throw new IllegalArgumentException();
}
}
}
Java
복사
생성자에서 코드를 없앰으로써 사용자가 쉽게 제어할 수 있는 투명한 객체를 만들 수 있고, 이를 테스트 하거나 재사용하기도 쉬워집니다.