안녕하세요. 이번 게시물에선 자바/스프링 개발자를 위한 실용주의 프로그래밍 3장 - 행동을 읽고 정리해보겠습니다.
또한 객체지향사상에 부합하지 않는 java interface의 default 메서드에 대한 고찰도 짤막하게 해보았습니다.
데이터 위주 사고 방식 VS 행동 위주 사고 방식
public class Car { //데이터 위주의 사고 방식
private Frame frame;
private Engine engine;
List<Wheel> wheels;
private float speed;
private float direction;
}
public class Car { //행동 위주의 사고 방식
public void drive(){}
public void changeDirection(float amount){}
public void accelerate(float speed){}
public void decelerate(float speed){}
}
데이터 위주의 사고 방식으로 작성된 코드는 절차지향 언어에서의 구조체와 다를 게 없음.
행동 위주의 사고 방식으로 작성된 코드는 객체가 어떤 동작을 할지 예측이 가능함.
객체지향적 관점에서 객체는 서로 협력을 해야하므로 객체의 행동을 기준으로 코드를 작성하는 것이 좋다.
행동에 집중하여 객체 모델링하기
public class ??? {
private float speed;
private float direction;
}
public class ??? {
public void ride(){
// do something..
}
public void run(){
// do something..
}
public void stop(){
// do something..
}
}
속성을 가지고 객체명을 정하기는 매우 어렵다. -> 자동차, 사람, 자전거, 새.. 등이 될 수 있음
행동으로 객체명을 구하기 -> Vehicle
객체가 갖고 있는 속성이나 세부 구현에 집중하기 보다 행동에 집중할 때 더 자연스러운 모델링이 된다.
데이터로 객체 정의는 어렵지만, 행동으로 객체 정의는 쉽다.
행동을 고민하는 것은 자연스럽게 역할을 고민하게 된다.
객체의 구현을 고민하면 속성이 생긴다 (구현을 미리 고민하지 말기)
public class Car {
private int degree; //자동차의 각도 (0~360) //구현을 고민하니 속성이 생겼다
public drive() {}
public float changeDirection(float amount) {
float result = (degree + amount) % 360;
if (result < 0) {
result += 360;
}
return result;
}
public void accelerate(float speed){}
public void decelerate(float speed){}
}
행동의 구현을 고민했더니 클래스가 어떤 값을 가지고 있어야하는지 고민하게 됐다
-> 메서드를 구현하려 했더니 데이터 위주의 사고 방식으로 돌아감!
행동을 고민하면서 구현이나 알고리즘을 고민하면 안된다.
오롯이 어떤 메시지만 처리할지 고민하기.
인터페이스 작성으로 행동만 고민할 수 있다.
인터페이스에는 오롯이 어떤 행동을 어떻게 시킬지만 선언할 수 있음
-> 개발자는 메시지를 전달하는 방법만 신경 쓸 수 있음
초기 설계 단계에서 상세한 구현은 무시해도 괜찮다.
-> 동료와 객체를 신뢰하는 것. -> 동료끼리 협업, 객체끼리의 협업이 가능하게 된다.
시스템 개발 초기 팀원이 모여 역할과 행동으로 인터페이스를 정의하고 객체들의 어떻게 협력할지 합의함
-> 구현 단계부터 병렬적인 작업이 가능해져 생산성이 높아진다.
인터페이스
인터페이스와 행동은 다르다.
인터페이스는 "나를 조작하고 싶다면 이런 메시지를 보내면 된다"라고 외부에 알려주는 수단
인터페이스는 어떤 행동을 지시하기 위해 사용할 수 있는 행동들의 집합
인터페이스는 협력을 위한 창구, 협력을 위한 객체들은 인터페이스를 통해 메시지를 주고 받는다.
역할
자동차 (실체) VS 탈것 (역할)
실체 혹은 구현에 집중할 때 데이터 위주의 사고를 하게 되고 역할에 집중할 때 행동 위주의 사고를 하게 된다.
클라이언트가 "자동차"를 만들어 주세요. 라고 할 때 "탈 것"을 원하는 게 아닌지 역질문하기
-> 구현에 집착하면 클라이언트가 "자전거", "말"등의 추가 요구사항을 제시할 때 유지보수하기 어려운 코드가 탄생하게 된다..
메서드는 함수가 아니다!
객체는 협력 객체에 메시지를 보내고 협력 객체는 메시지를 수신해서 행동을 한다.
개발자는 어떤 객체가 협력 객체에게 요청을 보낼 때 어떤 특정 메서드나 함수를 실행한다고 생각하지만, 사실은 그렇지 않다.
실제로 인터페이스를 통한 통신에서는 메시지를 보내면서 어떤 메서드가 실행될지 모른다.
객체지향에서는 특정 구현에 의존한 상황을 피하려고 한다.
따라서 협력 객체에 어떤 일을 요청할 때 '함수를 실행한다'가 아닌 '메시지를 전달한다'라고 표현함
- 함수는 같은 입력에 대해 항상 같은 출력을 해야하지만, 객체지향에선 실제 코드가 실행되기 전까지 어떤 메서드가 호출될 지 알 수 없다.
즉, 객체는 협력 객체에 메시지만 보낼 뿐. 실제로 어떤 방법(method)으로 일을 처리할지는 객체가 정함.
메서드는 어떤 메시지를 처리해달라는 요청을 받았을 때 이를 어떻게 처리하는지 방법을 서술하는 것.
-> 책임을 나누고 메시지를 통해 협력 관계를 구축하는 것에 집중하지 않고 구현에 집중하면 함수적인 절차지향 코드가 나온다.
cf) 인터페이스 default 메서드에 대한 고민
default 메서드는 인터페이스에서 구현하는 메서드입니다.
인터페이스에선 역할과 그에 따른 행동을 선언하는 것에 그쳐야하는데,
default 메서드로 행동을 구현하는 것은 인터페이스 목적에 반하는 것이라고 할 수 있습니다.
김영한님 강의에서 default 메서드는 버전 호환성을 위한 것이라 사용을 지양해야한다고 언급했었습니다.
개인적으로 enum과 recode에 공통 기능을 적용하고 싶어 인터페이스의 default 메서드 사용을 고민한 적이 있었습니다.
그 때 인프런에 질문 게시판에 enum과 record가 공통된 메서드를 상속받으려면 어떻게 해야하는지에 대해 질문을 올렸습니다.
답변은 enum과 record는 그렇게 공통 기능을 상속 받게 설계된 클래스가 아니다 라는 것이었습니다.
그럼 정확히 어떤 이유로 상속을 받지 말라는 것일까요? 공통 처리를 하지 말아야 하는 이유가 어떻게 될까요?
Also, the enums are by definition constants, and a constant should not be extended nor modified, and IMO this is a stronger reason to have enums
https://stackoverflow.com/questions/19433364/why-cant-a-class-extend-an-enum
스택오버플로우에서 찾은 enum에 대한 간단하지만 정확한 설명을 찾았습니다.
"enum은 상수들의 집합인데, 상수는 확장되거나 수정될 수 없다."
저번 장에서 읽었던 신뢰할 수 있는 객체를 얻기위한 조건 중 하나인 "불변성"에 대한 속성으로 이해해볼 수 있습니다.
enum은 상수이므로 불변성을 갖기 때문에 상속을 받을 수 없다는 것입니다.
record 또한 같은 원리로 생각해볼 수 있습니다. record는 값 객체이므로 "불변성"을 고려했을 때 상속을 받을 수 없는 게 당연합니다.
따라서 이렇게 정리를 해보면 될까요?
객체의 신뢰성을 확보하기 위해 불변성을 가진 enum과 record를 사용하는 것이다.
그런 enum과 record가 상속을 받거나 상속을 하면 불변성이 깨지게 된다.
따라서 enum과 record는 상속을 받거나 할 수 없다.
그렇다면 enum과 record로 공통의 기능을 갖게 하려면 어떻게 해야할까요?
이 질문을 좀 더 파고 들면 이렇게 생각해볼 수 있습니다.
A : 공통의 기능이라..? 혹시 객체에 특정 역할을 부여하고 싶은 거니?
Q : 역할? 아니.. (그런거 모르겠고) enum과 record가 공통의 기능을 갖게 하고 싶을 뿐이야.
A : 공통의 기능을 갖는다라.. 공통의 기능을 갖는다는 것은 하나의 역할을 갖고 있다는 것인데?
... 이어서
-> 프로세서 사용하는 것으로 결론 (결국 단순히 공통의 함수를 호출하고 싶다는 것이므로 이것은 객체의 협력으로 유도해야한다)
'컴퓨터 과학 > [책] 자바스프링 개발자를 위한 실용주의 프로그래밍' 카테고리의 다른 글
자바/스프링 개발자를 위한 실용주의 프로그래밍 7장 - 서비스 (0) | 2024.10.07 |
---|---|
자바/스프링 개발자를 위한 실용주의 프로그래밍 5장 - 순환참조 (1) | 2024.09.24 |
자바/스프링 개발자를 위한 실용주의 프로그래밍 4장 - SOLID (0) | 2024.07.19 |
자바/스프링 개발자를 위한 실용주의 프로그래밍 2장 - 객체의 종류 (VO, DTO, DAO, 엔티티) (0) | 2024.07.08 |
자바/스프링 개발자를 위한 실용주의 프로그래밍 1장 - 절차지향과 비교하기 (0) | 2024.07.08 |