* UML (Unified Modeling Language)
> 시스템 시가고하나 사양 및 설계를 문서화할때 사용하는 표현 방법
> 이탤릭체 (interface, abstract)
- 추상 클래스, 인터페이스, 추상 메서드
> 밑줄 (static)
- 클래스 필드, 클래스 메서드
> Realization (실체화, -----▷)
- concrete class 에서 interface 방향으로 긋는다
> Inheritance (상속, ──▷)
- child class 에서 parent class 방향으로 긋는다
> Aggregation (집합, ◇──>)
- 객체를 포함하는 쪽에서 포함되는 쪽으로 긋는다
- 필드에 포함되어 있음을 강조할 때 사용
> Directed Association (직접 연관, ──>)
- class A 는 class B 를 알고 있음
- 필드에 들어 있음을 강조할 때 사용
> Dependency (의존, ----->)
- class A는 class B 를 사용함
- 매개변수나 임시 변수 등으로 사용할 때 사용
> 화살표의 방향은 의존하는 것을 의미하기 때문에 매우 중요하다
* Sequence Diagram
> 프로그램 동작 순서를 표현
> Method Call (──▶)
> Method Return (<-----)
* Refactoring
> 외부에서 보는 프로그램 동작은 바꾸지 않고 프로그램의 내부 구조를 개선
- 리팩토링해도 외부에서 보는 프로그램 동작은 변하지 않는다
- 리팩토링하면 프로그램의 내부 구조가 개선된다
* Refactoring 의 장점
> 버그를 발견하기 쉽게 만든다
> 기능을 추가하기 쉽게 만든다
> 리뷰하기 쉽게 만든다
* Refactoring 의 한계
> 프로그램이 아직 동작하지 않을 때는 불가능하다
> 시간이 너무 촉박할 때 하는 건 현명하지 않다
* 22가지 Code Smell
1. 중복코드
2. 너무 긴 메서드
3. 방대한 클래스
4. 과다한 매개변수
5. 변경 발산
> 사양 변경이 있을 때 수정해야 할 내용이 곳곳에 흝어져있다
6. 변경 분산
> 어떤 클래스를 수정하면 다른 클래스도 수정해야 한다
7. 속성, 조작 끼어들기
> 언제나 다른 클래스 내용을 수정하는 클래스가 있다
8. 데이터 뭉치
> 합쳐서 다뤄야 할 데이터가 한 클래스에 모여 있지 않다
9. 기본 타입 집착
> 클래스를 만들지 않고 int 같은 기본 타입만 사용한다
10. 스위치 문
> switch 문이나 if 문으로 동작을 나눈다
11. 평행상속
> 하위 클래스를 만들면 클래스 계층의 다른 곳에서도 하위 클래스를 만들어야 한다
12. 게으른 클래스
> 클래스가 별로 하는게 없다
13. 의심스러운 일반화
> '언젠가 이런 확장을 하겠지'라고 너무 일반화한다
14. 임시 속성
> 임시로만 쓰는 필드가 있다
15. 메시지 연쇄
> 메서드 호출 연쇄가 너무 많다
16. 중개자
> 맡기기만 하고 자신은 일하지 않는 클래스가 있다
17. 부적절한 관계
> 그럴 필요가 없는데도 양방향 링크를 걸거나 Is-A 관계가 없는데 상속을 사용한다
18. 클래스 인터페이스 불일치
> 클래스 인터페이스가 적절하지 않다
19. 불완전한 라이브러리 클래스
> 기존 라이브러리 클래스를 사용하기 어렵다
20. 데이터 클래스
> 필드와 getter, setter 뿐인 클래스가 있다
21. 상속 거부
> 상속한 메서드인데 호출하면 문제가 발생한다
22. 주석
> 코드의 부족한 부분을 설명하기 위해 자세한 주석이 붙어있다
* Replace Magic Number with Symbolic Constant (매직 넘버를 기호 상수로 치환)
> 문제점
- Magic Number 를 사용하는 경우, 해당 값이 무엇을 뜻하는지 알기 어렵다
- 여러 곳에 있으면 변경하기도 어렵다
> 해법
- 매직 넘버를 기호 상수로 치환
> 결과
- 상수의 의미를 알기 쉬워짐
- 값 변경 시, 상수를 사용하는 모든 곳의 값이 변경됨
- 이해하기 어려운 이름을 사용하면 오히려 오해가 생길 수도 있음
* Remove Control Flag (제어 플래그 삭제)
> 문제점
- 처리 흐름을 제어하는 플래그 때문에 코드가 복잡해진다
> 해법
- 제어 플래그를 삭제하고 break, continue, return 을 사용
> 결과
- 조건 의미와 제어 흐름이 명확해짐 (이후에 오는 코드를 읽지 않아도 됨)
- 단순 반복에도 무리하게 적용하면 코드가 부자연스러워짐
* Introduce Assertion (어서션 도입)
> 문제점
- 주석으로 '어떤 조건이 성립한다'라고 적어도 프로그램 실행시 확인되지 않음
> 해법
- Assetion 도입
> 결과
- 해당 부분에서 성립해야 할 조건이 명확해지고 소스 코드가 읽기 좋아짐
- 버그를 빨리 발견 가능함
* Introduce Null Object (널 객체 도입)
> 문제점
- null 확인이 너무 많음
> 해법
- null 을 나타내는 특별한 객체를 도입해 '아무것도 안 함' 이라는 처리를 함
> 결과
- null 확인이 줄어듦
- 널 객체 종류 만큼 클래스가 늘어날 수 있음
* Extract Method (메서드 추출)
> 문제점
- 메서드 하나가 너무 긺
> 해법
- 기존 메서드에서 묶을 수 있는 코드를 추출해 새로운 메서드를 작성함
> 결과
- 각 메서드가 짧아짐
- 메서드 개수가 늘어남
* Extract Class (클래스 추출)
> 문제점
- 한 클래스가 너무 많은 책임을 지고 있음
> 해법
- 묶을 수 있는 필드와 메서드를 찾아 새로운 클래스로 추출
> 결과
- 클래스가 작아짐
- 클래스의 책임이 명확해짐
- 클래스 개수가 늘어남
* Replace Type Code with Class (분류 코드를 클래스로 치환)
> 문제점
- 타입 판별이 안 됨
> 해법
- 분류 코드를 나타내는 새로운 클래스를 작성
> 결과
- 분류 코드의 타입 판별이 가능해짐
- 클래스 개수가 늘어남
* Replace Type code with Subclasses (분류 코드를 하위 클래스로 치환)
> 문제점
- Switch 문을 써서 동작을 구분함
> 해법
- 분류 코드를 하위 클래스로 치환해서 다형적 메서드를 작성
> 결과
- 동작이 클래스별로 나뉨
- 클래스 개수가 늘어남
* Replace Type Code with State/Strategy (분류 코드를 상태/전략 패턴으로 치환)
> 문제점
- 동작을 switch 문으로 나누고 있지만 분류 코드가 동적으로 변하므로 분류 코드를 하위 클래스로 치환은 불가
> 해법
- 분류 코드를 나타내는 새로운 클래스를 작성해서 상태/전략 패턴을 사용함
> 결과
- 분류 코드 타입 판별이 가능해짐
- 분류 코드에 따른 클래스 동작을 다형성으로 해결 가능
- 클래스 개수가 늘어남
* Replace Error Code with Exception (에러 코드를 예외로 치환)
> 문제점
- 정상 처리와 예외 처리가 혼재함
- 에러 코드 전파 처리가 넓은 범위에 있음
> 해법
- 에러 코드 대신에 예외를 사용함
> 결과
- 정상 처리와 에러 처리를 명확하게 분리 가능
- 에러 코드를 반환해서 전파하지 않아도 됨
- 에러 관련 정보를 예외 객체에 저장 가능
- 에러 발생 부분과 에러 처리 부분이 분리되기 때문에 알기 어려워지는 경우도 있음
* Replace Constructor with Factory Method (생성자를 팩토리 메서드로 치환)
> 문제점
- 생성하고 싶은 인스턴스가 속한 실제 클래스를 클라이언트에는 숨기고 싶음
> 해법
- 생성자를 팩토리 메서드로 치환함
> 결과
- 어느 클래스 인스턴스를 생성할지를 팩토리 메서드안에서 정할 수 있음
- 생성한 인스턴스를 변경해도 클라이언트 쪽은 변경하지 않아도 됨
- 추상도가 너무 올라가면 코드가 오히려 어려워짐
* Duplicate Observed Data (관측 데이터 복제)
> 문제점
- 모델과 뷰가 한 클래스 안에 뒤섞여 있음
> 해법
- 양쪽을 분리하고 관찰자 패턴 또는 이벤트 리스너로 동기화함
> 결과
- 클래스 역할이 확실해짐
- 여러 뷰를 가지거나 뷰를 전환하기 쉬워짐
- 클래스 숫자가 늘어남
- 주의하지 않으면 동기화 이벤트가 무한히 발생할 수도 있음
* Replace Inheritance with Delegation (상속을 위임으로 치환)
> 문제점
- 하위 클래스가 상위 클래스 기능의 일부만 사용함 (상속 거부)
- 하위 클래스가 상속 클래스와 IS-A 관계가 아님
- 리스코프 치환 원칙 위반
- 계약을 지키지 않음
> 해법
- 위임을 사용해서 상속을 치환함
> 결과
- 부적절한 상속 관계를 해소 가능함
- 클래스에 필요한 기능이 명확해짐
- 클래스 개선, 기능 추가가 편해짐
- 위임하는 메서드를 작성해야함
* Hide Delegate (대리자 은폐)
> 문제점
- 클라이언트 클래스가 서버 클래스뿐만 아니라 대리 클래스까지 이용함
> 해법
- 서버 클래스에 위임 메서드를 추가해서 클라이언트 클래스로부터 대리 클래스를 은폐
> 결과
- 클래스 사이의 불필요한 관계가 줄고 코드 수정이 쉬워짐
- 서버 클래스의 책임이 늘어남
* Tease Apart Inheritance (상속 구조 정리)
> 문제점
- 클래스 계층 하나에서 다양한 작업을 함
> 해법
- 상속을 분할하고 필요한 작업은 위임을 사용해 이용함
> 결과
- 부적절한 상속 관계를 해소 가능
- 클래스 개선, 기능 추가가 편해짐
- 클래스 개수가 늘기도 함
'SW 공학 > SE 서적' 카테고리의 다른 글
/////★ GoF의 디자인 패턴 / 에릭감마, 리처드 헬름, 랄프 존슨, 존 블리시디스 / 프로텍 미디어 (0) | 2021.09.20 |
---|---|
☆ 거꾸로 배우는 소프트웨어 개발 / 이호종 / 로드북 (0) | 2021.07.18 |
///실용주의 프로그래머 (0) | 2020.10.16 |
★클린 아키텍처 / 로버트 C.마틴 / 인사이트 (0) | 2020.10.02 |
★ 자바와 JUnit을 활용한 실용주의 단위테스트 / 제프 랭어, 앤디 헌트, 데이브 토마스 / 길벗 (0) | 2020.09.20 |