💡 개요
오늘은 객체 지향 프로그래밍에서 사용되는 이론인 ‘디미터의 법칙’에 대해 정리해 보자.
📕 디미터의 법칙
디미터의 법칙이란 ‘친구의 친구는 내 친구가 아니다’ 를 나타내는 법칙으로 자신이 조작하는 객체의 내부 구현 내용을 최소한으로 알아야 한다는 것이다.
이를 통해 객체 간의 불필요한 의존성을 줄이고 결합도도 낮추는 것이다.
말로만 들으면 이해하기 어려우니 아래의 디미터 법칙이 적용되지 않은 코드로 설명하겠다.
public class Car {
private CarName carName;
private Position position;
...
// getter 아직 없음
}
public class CarName {
private final String name;
...
public String getName() {
return name;
}
}
public class Position {
private int idx;
...
public int getIdx() {
return idx;
}
}
위의 Car 클래스는 자동차 이름을 나타내는 CarName과 자동차의 위치를 나타내는 Position을 의존하고 있다.
만약 Car 객체를 사용하는 Service에서 Car.CarName, Car.Position의 값이 필요하다면 어떻게 해야 할까?
가장 쉬운 방법은 인텔리제이나 이클립스에서 지원하는 getter 자동완성을 사용하면 될 것이다.
아래는 Car 클래스에서 getter 자동 완성을 한 코드이다.
public class Car {
private CarName carName;
private Position position;
...
public CarName getCarName() {
return carName;
}
public Position getPosition() {
return position;
}
}
보다시피 IDE는 CarName, Position 객체를 통째로 반환한다.
이런 코드는 디미터 법칙 위반이다.
왜냐하면 Car를 의존하고 있는 RaceServiceImpl 코드에선 Car.getter를 사용한다면 CarName, Position 객체도 의존하게 되기 때문이다.
다음은 Car 객체를 사용하는 RaceServiceImpl.racing() 메서드이다.
public class RaceServiceImpl implements RaceService {
@Override
public void racing(List<Car> carList, Round round) {
int curRound = 0;
while (round.hasMoreRounds(curRound++)) {
carList.forEach(car -> car.moveForwardIfRandomBiggerThenStandard(getRandom()));
carList.forEach(car -> OutputView.printCarPosition(car.getCarName().getName(), car.getPosition().getIdx()));
OutputView.printMessage("\\n");
}
}
...
}
carList.forEach(car -> OutputView.printCarPosition(car.getCarName().getName(), car.getPosition().getIdx()));
위 코드에서 car.getCarName(). getName(), car.getPosition(). getIdx() 부분은 메서드 체이닝(코드에 있는.)이 두 번 나온다.
그렇기 때문에 현재 RaceServiceImpl 클래스는 Car 뿐만 아니라 CarName, Position까지 의존하게 되는 것이다.
자바 코드를 작성해 봐서 알겠지만, 불필요한 의존관계는 유지보수를 어렵게 만든다.
그렇기 때문에 디미터 법칙에선 웬만하면 메서드 체이닝은 한 번까지만 제한하는 편이다.
디미터 법칙을 적용한 코드는 다음과 같다.
public class Car {
private CarName carName;
private Position position;
...
/**
* 디미터 법칙 적용
* @return CarName.name
*/
public String getCarName() {
return carName.getName();
}
/**
* 디미터 법칙 적용
* @return Position.idx
*/
public int getPosition() {
return position.getIdx();
}
}
public class RaceServiceImpl implements RaceService {
@Override
public void racing(List<Car> carList, Round round) {
int curRound = 0;
while (round.hasMoreRounds(curRound++)) {
carList.forEach(car -> car.moveForwardIfRandomBiggerThenStandard(getRandom()));
carList.forEach(car -> OutputView.printCarPosition(car.getCarName(), car.getPosition()));
OutputView.printMessage("\\n");
}
}
...
}
carList.forEach(car -> OutputView.printCarPosition(car.getCarName(), car.getPosition()));
위의 코드와 같이 RaceServiceImpl.racing()에서 CarName, Position은 알지 못하지만, Car 객체를 통해 자동차 이름인 name과 자동차 위치인 idx는 모두 사용할 수 있게 되었다.
😎 결론
디미터 법칙을 외우기 힘들면 그냥 아래의 문장만 기억하자
메서드 체이닝은 한 줄에 한 개만!
'개발 일기' 카테고리의 다른 글
[개발 일기] 2025.02.12 - 스티키 세션 (1) | 2025.02.12 |
---|---|
[개발 일기] 2025.02.11 - SockJS (0) | 2025.02.11 |
[개발 일기] 2025.02.09 - IP보안 (0) | 2025.02.09 |
[개발 일기] 2025.02.08 - 함수형 프로그래밍 (0) | 2025.02.08 |
[개발 일기] 2025.02.07 - CSRF (0) | 2025.02.07 |