- 메소드
임의의 프로그램은 복잡한 제어 흐름이 들어 있는 커다란 루틴 이라고 볼 수 있다.
여기에서의 문제는 이 커다란 루틴에서는 로직상 중요 부분과 중요하지 않은 부분을 구분해서 읽기가 어렵다는 것이다.
또한, 이처럼 거대한 루틴에서는 반복되는 코드가 나올 수 있는데, 하나의 큰 로직에서는 이러한 중복을 제거하기 쉽지 않다.
이를 위해 메소드를 나누는 방식을 사용하면 읽기 쉬운 코드로 나눌 수도 있고, 반복되는 코드를 메소드를 통해 재사용할 수 있는 효과를 거둘 수 있다.
1. 조합 메소드
- 추상화 수준이 비슷한 메소드 호출로 하나의 메소드를 구성하라.
void calculate() {
input();
number = 1+x;
output();
}
(책의 예시와는 조금 다르지만, 형태는 비슷하다.)
위 로직을 보면 중간 number = 부분은 위 아래 메소드 조합과 어울리지도 않고 의도를 한눈에 파악하기가 어렵다.
통일시켜 주는 게 낫다.
더불어 이렇나 조합 메소드를 한번에 구현하기는 어려우므로 작동하는 로직을 한번에 짜고 리팩토링을 통해 메소드를 나누는 방향이 저자의 방법이라고 한다.
2. 의도 제시형 이름
- 메소드 이름을 통해 메소드의 의도를 쉽게 파악할 수 있어야 한다.
but, 메소드 의도만 표시하면 되는 것이지 굳이 세부 구현이 어떻다 하는 것까진 필요 없다.
User.dataBaseSearchById(String id);
이런식으로 메소드를 통해 메소드의 의도를 나타내는 것은 좋으나, 오히려 메소드명을 다 읽을 때까지 한눈에 파악하기가 힘들다.
User.find(String id);
이런식으로 어떤 '의도' 인지만 표현하면 된다. 세부 구현이 궁금한 사람은 해당 메소드를 찾아서 보면 된다.
3. 메소드 가시성
- 네가지 수준이 있는데, 가시성이 높을 수록 사용하는 측에서는 필요 이상으로 많은 작업을 해야 함. 반대로 가시성이 낮을 수록 미래의 유연성이 (미래에 해당 인터페이스를 수정하는 것이 쉬운정도) 높다.
공용 : 패키지 외부에서도 이 메소드가 유용하다는 걸 의미
패키지 : 같은 패키지의 다른 객체에는 유용하나 패키지 외부의 객체에는 공개하지 않겠다는 의미
보호 : 하위 클래스는 접근 가능하다는 의미. (다른 패키지여도 자식(하위) 클래스라면 접근 가능)
전용 : 오직 해당 객체 내에서만 사용 가능.
final : 메소드를 사용하는 것은 자유지만, 해당 메소드를 더 이상 바꿀 수는 없다는 의미.
static : static으로 선언된 메소드는 해당 클래스의 인스턴스에 접근할 수 없을 때에도 접근 가능. (권한있을 때)
인스턴스 생성과 무관하게 사용할 수 있는 메소드라는 의미(정적메소드 - 복잡한 로직에는 적합하지 x)
4. 메소드 객체
- 조금 더 다시 봐야 할 것 같다. 머리로는 이해하는 거 같은데 딱 예시로 쓰려니 막힌다.
5. 오버라이드
- 상위 클래스에서는 공통부분을 구현하고 하위 클래스에서는 특화한 연산을 하라는 것.
말 그대로 다시 써서(엎어써서) 구현을 하위 클래스에서 하는 것이다.
6. 오버로드
- 파라미터 받는 갯수 및 타입에 따라 여러개의 메소드 구현.
'이 메소드를 사용할 수 있는 다양한 포멧이 존재한다.' 라는 의미
메소드 자체의 연산은 동일해야 함. 다를 경우 메소드 명을 다르게 구현해야 함.
7. 메소드 반환 타입
- 반환 타입은 프로시저(void)와 함수(반환타입 존재)를 구별할 수 있게 해준다.
반환 타입을 지정할 때에는 기본형 타입보다는 추상적인 타입을 정하는 것이 추후 반환 클래스의 타입을 유연하게 변경할 수 있다.
8. 메소드 주석
- 코드 이름과 구조를 통해 최대한 많은 정보를 주는 것이 좋으나, 이것만으로 부족할 때에는 주석을 사용.
그러나 자동화된 테스트 작성 등을 통하는 방식이 주석보다 나을 수 있다.
9. 도우미 메소드
- 메소드를 나누다보면, 작은 단위의 도우미 메소드가 필요.
공통으로 계속 반복되는 부분을 도우미 메소드로 구현하면 수정이 필요할 때, 해당 도우미 메소드만 수정하면 되므로 편리하다.
10. 디버그 출력 메소드
- Object 인터페이스 중 하나인 toString()을 통해 해당 객체에 대한 정보를 표현할 수 있다. 이 방식을 사용하면 복잡한 디버깅 과정보다 쉽게 해당 객체에 대한 정보를 얻을 수 있다.
11. 변환
- 객체 A를 가진 상태에서 이후 연산을 위해 객체 B가 필요한 경우 객체를 변환한다. 변환의 방법은 여러가지가 존재한다.
12. 변환 메소드
class Autumn {
Winter asWinter() {
...
}
}
반환타입을 변환하는 객체 타입으로 지정하여 변환한다.
변환 메소드보다는 변환 생성자 사용을 선호.
13. 변환 생성자
File file = new File(String fileName);
원본 객체(위 예시에서는 String) 를 파라미터로 취해서 대상 객체(File)를 반환
14. 생성
- 크기가 큰 프로그램은 규모가 더 작은 프로그램보다 수정이 어렵다. 작게 나눌수록 수정이 쉽다.
객체를 작게(여러개) 생성하여 처리하면 관리하기가 쉽다는 의미(?)
15. 완결 생성자
- 모든 생성자가 동일한 하나의 생성자를 사용해서 모든 초기화를 하도록 함.
16. 공장 메소드
- 복잡한 객체를 생성할 때, 공장 메소드를 사용. but 일반메소드와 차이를 두기 위해 객체 생성 이외의 작업이 포함될 때 공장 메소드를 활용.
17. 내부 공장
- 설명이 더 필요하거나 추후 개선이 필요한 객체 생성의 경우 도우미 메소드로 캡슐화
18. 컬렉션 접근자 메소드
- 컬렉션에 제한적인 접근만 허용한다. 사용자가 컬렉션을 직접 조작하게 하면 안됨.
List<User> getUsers() {
return users;
}
위처럼 하지 말고 아래처럼 처리해야 함.
List<User> getUsers() {
return Collections.unmodifiableList(users);
}
19. 불린 설정 메소드
- 커뮤니케이션에 도움이 된다면, boolean값을 설정하는 두개의 메소드를 각각 제공하는게 낫다.
void valid () {
...
return true;
}
void invalid() {
...
return false;
}
20. 질의(쿼리) 메소드
- 조건문에 들어가는 객체(메소드)는 isXXX 라는 형태의 이름으로 명명한다. (가독성에 좋다.)
21. 동등성 메소드
- 객체의 동일성이 아닌 '동등성' 을 비교해야 하는 경우 equals() 와 hashcode()를 구현하라
22. 취득 메소드
- get으로 시작하는 메소드. 로직이 있는 곳에서 취득 메소드를 사용하지 말고 되도록 데이터가 있는 곳에서 연산 로직을 사용할 수 있도록 구현한다. (취득메소드는 되도록 지양)
23. 설정 메소드
- set으로 시작하는 메소드. 이 또한 취득 메소드와 마찬가지로 외부에서 설정 메소드 사용을 피하는 것이 좋다.
24. 안전한 복사
- 접근자 메소드를 통해 전달하거나 전달되는 인스턴스를 복사해서 앨리어스 문제 회피
'책(독서)' 카테고리의 다른 글
[켄트벡의 구현패턴] ~ 195p 발전하는 프레임워크 (0) | 2020.09.21 |
---|---|
[켄트벡의 구현패턴] ~175p 9. 컬렉션 (0) | 2020.09.17 |
[켄트벡의 구현패턴] ~121p 행위 (0) | 2020.09.15 |
[켄트벡의 구현패턴] ~106p 06. 상태 (0) | 2020.09.06 |
[켄트벡의 구현패턴] ~80p (0) | 2020.09.04 |