오봉이와 함께하는 개발 블로그
내 코드가 그렇게 이상한가요? - 응집도 : 흩어져 있는 것들 본문
응집도 : 흩어져 있는 것들
아래 작성하는 내용들은 다소 불친절 할 수 있으나, 내가 책을 보고 느낀 내용을 작성하려 한다.
책 내용이 필요한 분은 구글에 검색하여 찾아보심이 좋을 거 같다.
static 메서드 오용
static 메서드를 "잘못" 사용하지 말자는 내용이다.
static int add(int amount1, int amount2) {
return amount1 + amount2;
}
SomeClass someVar = OrderManager.add(moneyData1.amount, moneyData2.amount);
위 코드를 예시로 보면 맞을까? amount는 moneyData에 있는데 왜 계산은 OrderManager의 add에서 하는가
어떤 상황에서 static 사용?
그렇다면 책에서는 어떤 상황에서 static을 사용하라 하는가?
- 로그 출력
- 포맷 변환
- 팩토리 메서드
와 같이 클래스의 응집도와 상관 없는 것들에는 사용해도 좋다고 한다.
초기화 로직분산
잘 설계된 클래스가 있는데, 생성 시에만
new SomeClass(3000) // 일반
new SomeClass(10000) // 프리미엄
과 같이 생성한다 가정하자. 지금 당장 생각나는 문제점으로
- 정책 변경 시 해당 코드 모두 찾아서 변경
- 휴먼 에러 (개발자의 실수로 인해 잘못된 코드 작성)
가 생각난다.
그럼 어떻게 실수를 방지할까
static final int somePolicy1 = 3000;
static final int somePolicy2 = 10000;
static SomeClass somePolicy1() {
return new SomeClass(somePolicy1);
}
static SomeClass somePolicy2() {
return new SomeClass(somePolicy2);
}
SomeClass someClass1 = SomeClass.somePolicy1();
SomeClass someClass1 = SomeClass.somePolicy2();
전체적으로 이름은 알아보기 힘들게 적어놨지만, 적절한 작명만 하면(대충해도 적당히 알아서 잘 하면) 무조건 알아볼 수 있을 거 같다.
범용 처리 클래스
이 장표에서는 Common, Utils와 같은 우리가 자주 보고, 만들 클래스에 대해 설명한다.
짧게 요약하면 어플리케이션의 횡단 관심사가 아닌 경우는 가급적 자제하라는 말이다.
예를 들어 로깅 처리, 트랜잭션 타임 체크 같은 경우 전체 프로그램를 관통하기 때문에 범용 처리 클래스에 넣어도 되겠지만, 전화번호/이메일 유효성 검사 혹은 n개의 클래스에서 공통으로 사용하는 특정 값 연산 등은 적절하지 않다.
처음에는 같다고 생각했지만, 요구사항 변경으로 수정할 일이 발생하면 다시 찢어서 분리하는 것이 더 문제가 될 수 있기 때문에 가급적이면 객체지향 원칙을 생각하자.
결과를 리턴하는 데 매개변수 사용하지 않기
class ActorManager {
void shift (Location int x, int y) {
location.x += x;
location.y += y;
}
}
코드의 문제점은 두 가지로 보인다
- 중복 구현될 가능성
- 책임 떠넘김
책임을 다른 곳에 넘겨놨기 때문에 다른 곳에서 Location을 사용한다면shift
함수가 다른 곳에 구현될 가능성이 높다.
2번을 했기 때문에 1번의 가능성이 더 높아진다.
수정하려면 첫째로 Location에 shift를 구현하고, 두번째로 함수형의 불변을 차용해서 수정할 수 있다.
class Location {
final int x;
final int y;
Location(final int x, final int y) {
this.x = x;
this.y = y;
}
Location shift (final int shiftX, final int shiftY) {
final int nextX = this.x + shiftX;
final int nextY = this.y + shiftY;
return new Location(nextX, nextY);
}
}
매개변수가 너무 많은 경우
int recoverMagicPort(int currentMagicPoint, int originalMaxMagicPort,
List<Integer> maxMagicPointIncrements, int recoveryAmount) {
.......
}
책에서 간략하게 가져온 예시 함수다.
특정 클래스에 있는 함수에서 매개변수가 저렇게 많다면 저게 해당 클래스에 있는 인스턴스 변수의 전부일까? 분명 저거보다 더 많을 것이다.
정말 많이 봐줘서 함수의 코드도 짧고, 클래스 인스턴스 변수도 저게 전부라고 가정했을 때는 문제가 없을까?
사람이 문제가 될 수 있다. 매개변수에 잘못된 값을 넣는다거나 하는 휴먼 에러가 발생할 수 있기 때문에 매개변수가 많은 경우 클래스로 감싸야 한다.
클래스로 감싸면 휴먼 에러 방지만 할 수 있는 것이 아니라, 각 클래스에 역할을 분담시키면서 응집도를 높일 수 있다.
예를 들어 int currentMagicPoint
를 계산하는 로직이 각 클래스 여기저기 분산되어 있을 텐데, 특정 클래스에 인스턴스 변수로 충분하게 의미 있는 다른 값들과 같이 사용하면 하나의 클래스를 통해 여기저기 분산되어 있는 비즈니스 로직을 하나의 클래스에 모을 수 있다.
메서드 체인
stream().~~~
을 말하는 것이 아니다.party.members[memberId].equipments.armor = newArmor;
이게 문제다.
자세한 내용은 데메테르 법칙(혹은 디미터 법칙)을 참조하자.
요약
이번 5장 응집도 : 흩어져 있는 것들
내용을 간단하게 정리하면 어떤 방법을 통해 클래스 응집도를 향상시킬 수 있는지
에 대해 말하고 있는 걸로 보인다.
SOLID 원칙에 따르면 SRP(Single Responsibility Principle) 단일 책임 원칙을 지키는 방법에 대해 설명하는 것이 아닐까 싶다.
로버트 마틴의 클린 아키텍쳐에서는 단일 책임 원칙을 다음과 같이 정의한다고 한다. "하나의 모듈은 오직 하나의 액터에 대해서만 책임져야 한다."
모듈이란 소스 파일로 함수와 데이터르 응집된 집합 즉 클래스 를 말하는 거 같다.
위에 설명하는 내용 중 무엇 하나 단일 책임 원칙을 벗어나자고 하는 게 없어 보인다.
간단해 보이지만 지키기 참 어려운 거 같다.
'이론' 카테고리의 다른 글
내 코드가 그렇게 이상한가요? - 조건 분기 : 미궁처럼 복잡한 분기 처리를 무너뜨리는 방법 (1) | 2024.01.09 |
---|---|
CleanCode - 단위 테스트 (1) | 2024.01.04 |
CleanCode - 경계 (1) | 2024.01.04 |
CleanCode - 오류 처리 (0) | 2024.01.03 |
내 코드가 그렇게 이상한가요? - 컬렉션 : 중첩을 제거하는 구조화 테크닉 (0) | 2024.01.01 |