캡슐화가 뭔가요? 라고하면 책임들을 하나로 묶고 실제 구현을 외부로 부터 감추는 것이라고 알고있습니다
객체의 책임을 잘 묶으면 응집도가 올라가 자율적인 객체가 됩니다. 자율적인 객체가 되면 단순히 데이터 전달자 역할이 아니라, 자신의 상태를 스스로 처리할 수 있습니다.
하지만, 이 상황에서 은닉화가 잘 이루어지지 않는다면 외부 객체가 은닉화가 지켜지지 않은 객체의 정보를 너무 많이 알게 되고,
깊게 의존하는 코드를 작성하게 될 위험이 존재합니다,
이는 곧 두 객체 모두 높은 결합도와 낮은 응집도를 지닌 수동적인 객체가 됨을 의미합니다
이는, 변경이 힘들게 하여 유지보수가 어렵게 됩니다
은닉화 실수하기 쉬운 케이스
은닉화가 잘 이루어지지 않았다는것 객체가 자신의 데이터 존재나 상태, 그리고 책임을 외부로 공개하고 있다는 것입니다
하지만 생각보다 아래와 같은 케이스는 실수하기 쉬우니 알아두고 주의를 할 필요가 있습니다
case1
public class Movie {
private Money fee;
public Money getFee() {
return this.fee;
}
public void setFee(Money fee) {
this.fee = fee;
}
}
"getFee"라는 메소드명을 통해서 해당 객체의 필드에는 fee가 있다고 만천하에 공개하고 있습니다
이런 코드는 다음과 같은 문제가 있습니다
- fee의 이름이나 타입이 바뀌면 메서드 이름도 다 바꿔줘야하고, 이를 사용하고 있던 외부 객체들의 코드도 모두 바꿔야 한다
- fee를 필드에 가지고 있음을 알 수 있으며, 외부 객체가 setter를 이용해 Movie의 데이터를 조작할 수 있다
때문에, getter, setter는 최대한 자제하고, 데이터를 물어보지 말고 메세지를 통해 결과를 의존 하도록 변경해야합니다
class car {
private int 주행거리;
...
public int get주행거리(){
return this.주행거리;
}
public static void main(String[] args) {
if(car.get주행거리 >= 100) //데이터를 내놔!
System.out.println("100km 이상 주행");
}
}
class car {
private int 주행거리;
...
public int isOverKm(int km){ //메세지에 대한 응답만 할꺼야
return 주행거리>= km;
}
public static void main(String[] args) {
if(isOverKm(100))
System.out.println("100km 이상 주행");
}
case2
public class DiscountCondition {
private DiscountConditionType type;
privateint sequence;
private DayOfWeek dayOfWeek;
private LocalTime startTime;
private LocalTime endTime;
public DiscountConditionType getType() { ... }
public boolean isDiscountable(DayOfWeek dayOfWeek, LocalTime time) { ... }
public boolean isDiscountable(int sequence) { ... }
}
해당 코드는 getter문이 있으나 내부 구현과는 연관이 없고, 메서드 명도 내부 구현을 유추할 수 없도록 하고 있습니다
하지만, isDiscountable 를 보면 파라미터에 해당 객체의 필드를 가지고 있습니다
마찮가지로, DiscountCondition의 필드가 수정되면 해당 메서드는 물론 이를 사용하고 있던 외부 객체들의 코드도 모두 수정해야 합니다
맴버 메서드 파라미터에 스스로의 정보를 담지 않도록 주의해야 합니다
case3
public class Movie {
private String title;
private Duration runningTime;
private Money fee;
private List<DiscountCondition> discountConditions;
private MovieType movieType;
private Money discountAmount;
private double discountPercent;
public MovieType getMovieType() { ... }
public Money calculateAmountDiscountedFee() { ... }
public Money calculatePercentDiscountedFee() { ... }
public Money calculateNoneDiscountedFee() { ... }
}
해당 코드는 내부 구현과 연관 없는 getter문이 있고, 메소드 파라미터의 객체의 속성을 담지도 않았습니다
하지만, 해당 객체에는 3가지 할인 정책이 있다는 것을 외부에 노출하고 있습니다
이 상황에서는 할인 정책이라는 객체를 따로 분리하고 그 안에서 함수형 인터페이스 등을 통해 다형성을 구현하는 것이 적절합니다
은닉화의 본질
자신의 데이터를 자신이 처리하도록 하고 자신의 정보를 외부로 부터 숨기면 됩니다
A 객체가 검증 객체를 의존한다면 검증하다 라는 정보만 알아야지 어떤 검증 로직들을 이용해서 검증을 하는지는 몰라야 한다는 것입니다
또한, A는 검증 객체의 필드에 접근해 데이터를 처리해서도 안됩니다
자신의 책임은 자기 자신만 알고 처리하며, 외부 객체는 해당 책임의 결과가 이거구나만 알고 활용하기만 해야 합니다
인터페이스를 의존하라고 하는 이유도 여기서 기인합니다
Reference
https://ko.wikipedia.org/wiki/캡슐화
https://steady-coding.tistory.com/449
'개발자 준비 > 개발 공부' 카테고리의 다른 글
API 사용성 개선: 사용자 정의 예외를 이용한 API 응답 세분화 및 상세화 (0) | 2022.12.02 |
---|---|
전략 패턴과 캡슐화 개념을 이용한 도메인과 검증 로직 최적 분리 과정 (0) | 2022.12.02 |
JWT 토큰 탈취시 대처법에 대한 고찰 (0) | 2022.12.02 |
동적 주입으로 버전 관리시 DB 연동 정보 숨기기 (3) | 2022.12.02 |
Static 장점 제대로 살리기(feat. 프로그래밍 패러다임은 어우러져야 한다) (0) | 2022.11.22 |