구현 설계
17 Oct 2017 | JAVA DESIGNChapter 5 구현 설계
내용
- 5.1. 설계의 문제점
- 5.2. 설계의 핵심 개념
- 5.3. 빌딩 블록의 설계
- 5.4. 설계 방법
- 5.5. 유명한 방법론에 대한 의견
5.1. 설계의 문제점
설계에는 완벽은 없다 주어진 상황에 맞추어 진행되며 항상 완료될때까지 발견적 학습과정이다.
5.1.1. 설계는 불명확한 문제이다. (문제를 해결해나아가는 과정이다)
5.1.2. 결과는 정돈되었을 지라도, 설계는 엉성한 프로세스이다. (완벽하지는 않다.)
5.1.3. 설계는 트레이드오프와 우선순위에 관한 것이다.
5.1.4. 설계에는 제약이 따른다.
5.1.5. 설계는 다양성을 지닌다.
5.1.6. 설계는 발견적 학습 과정이다.
5.1.7. 설계는 창발적이다.
5.2. 설계의 핵심 개념
훌륭한 설계는 중요한 개념들을 얼마나 잘 이해하는지에 달려있다.
- 이해를 돕기 위해 비유를 사용한다
- 개념을 이해하고 단순화 시킨다.
5.2.1. 소프트웨어의 기본적인 기술적 의무 : 복잡성관리
본질과 비본질
우리가 한번에 한 부분을 제대로 집중할 수 있게 프로그램을 구성하도록 노력해야 한다. (단순화)
소프트웨어 아키텍처 수준에서는 시스템을 서브시스템으로 나누어 문제의 복잡성을 줄인다.
- 한번에 처리해야하는 본질적인 복잡성의 양을 최소화 한다.
- 비본질적인 복잡성이 불필요하게 증가하지 않도록 한다.
5.2.2. 바람직한 설계의 특징
- 복잡성 최소화: 재치있는 설계보다는 간단하고 이해하기 쉬운 설계로 만들어라.
- 유지 관리의 편리함: 유지보수 프로그래머를 위한 설계
- 느슨한 결합: 프로그램의 서로 다른 부분들 간의 연결을 최소화하도록 설계하는 것을 의미한다.
- 클래스 간의 연결이 최대한 적도록 설계하기 위하여 클래스 인터페이스에서의 추상화와 캡슐화, 정보의 은닉과 같은 방법을 사용.
- 연결이 최소화되면 통합이나 테스트, 유지 보수 시에도 작업을 줄일 수 있다.
- 확장성: 예측가능한 변경
- 재사용성: 현재 시스템의 일부를 다른 시스템에서도 사용할 수 있도록 시스템을 설계하는 것
- 높은 팬인(fan-in): 주어진 클래스를 사용하는 클래스의 수가 많다는 것을 의미
- 낮은 팬아웃(fan-out): 주어진 클래스가 다른 클래스를 적게 사용한다는 것을 의미
- 이식성: 시스템을 다른 환경으로 쉽게 이동시킬 수 있도록 설계하는 것을 의미
- 간결성: 추가적인 부분이 없도록 시스템을 설계하는 것을 의미
- 계층화: 시스템을 특정한 수준으로 살펴보고 일관된 뷰를 얻을 수 있도록 분산 계층을 유지하는 것을 의미 한다.
- 표준 기술들: 표준화되고 일반적 접근 방법을 사용.
5.2.3. 설계 수준
- 1수준(소프트웨어 시스템): 전체 시스템
- 2수준(서브시스템이나 패키지로 분할)
- 프로그램을 주요 서브시스템으로 어떻게 분할할 것인지와 각 서브시스템이 다른 서브시스템 어떻게 사용할지를 결정하는 일이다.
- 서브시스템간의 커뮤니케이션은 “알아야 할 필요성”이 있을때에만 가능하도록 한다.
- 연결을 이해하고 쉽게 유지하기 위해서 내부 서브시스템의 관계를 간단하게 만들도록 한다.
- 시스템 수준의 다이어그램은 비순환 그래프이어야 한다.
- 공통적인 서브시스템
- 비즈니스 규칙
- 사용자 인터페이스
- 데이터베이스 접근
- 시스템 의존성 : 이식성 상승한다.
- 3수준(클래스로 분할) 이 수준에서의 설계는 시스템에 있는 모든 클래스들을 규명한다.
- 클래스와 객체: 객체와 클래스간의 차이점 클래스는 프로그램 코드에서 보게 되는 정적인 것이고, 객체는 프로그램이 실행했을 때 보게되는 특정한 값과 특성을 갖는 동작이다.
-
4수준(루틴으로 분할): 클래스의 private 루틴들을 상세히 설계
- 5수준(내부 루틴 설계): 개별적인 루틴의 상세한 기능을 구현
-
설계는 의사코드(pseudo code)를 작성하고, 참고 서적에서 알고리즘을 살펴보고, 루틴 내의 코드 단락을 어떻게 구성할것인지를 결정하고, 프로그래밍 언어로 코드를 작성하는 활동들로 구성.
- 의사코드: 일반적인 언어로 코드를 흉내내어 알고리즘을 써놓은 코드를 말한다. 특정언어로 프로그램을 작성하기 전에 알고리즘 모델을 대략적으로 모델링하는데 쓰인다.
5.3. 빌딩 블록의 설계: 발견적 학습(객체지향 설계 관점)
설계는 결정적이지 않기 때문에, 효율적인 경험들에 대한 훌륭한 응용 분야가 훌륭한 소프웨어 설계에서의 핵심적인 활동이다.
=> 먼말이여 …?
5.3.1. 실세계의 객체를 찾아라.
“처음부터 시스템이 무엇을 하는지 묻지 말고 무엇을 해야 하는지 요구하라!”
- 객체설계 과정
- 객체의 특성(메서드와 데이터)를 규명한다.
- 각 객체가 무엇을 할 수 있는지 결정한다.
- 각 객체가 다른 객체에게 무엇을 허용하는지 결정한다. 객체가 서로에게 할 수 있는 일반적인 두 가지 사항은 포함과 상속이다.
- 각 객체에서 다른 객체에 보여질 부분을 결정한다. 즉, public인 부분과 private인 부분을 결정한다. (정보은닉)
- 각 객체의 public 인터페이스를 정의한다.
5.3.2. 일관성 있는 추상화를 구성하라. 추상화를 이용하면 복잡한 개념을 보다 단순한 관점으로 바라볼 수 있다.!!!!
5.3.3. 세부 사항들을 캡슐화하라 추상화는 객체를 높은 수준에서 볼 수 있도록 하고, 캡슐화는 다른 수준에서는 객체를 볼 수 없도록 한다. 캡슐화는 복잡한 부분을 보지 못하게 함으로써 복잡성을 관리하는데 도움을 준다.
5.3.4. 상속이 설계를 단순화할 수 있을 때 상속하라. 객체들 간의 유사성과 차이점을 정의하는 것을 상속이라 부른다. 상속은 프로그램을 단순화 시킨다.
5.3.5. 비밀을 숨겨라(정보은닉) 객체지향 설계에서 정보 은닉이 캡슐화와 모듈화를 발생케 하였으며, 추상화 개념과 연결된다. 정보 은닉은 복잡성을 감추는데 중점을 두고 있다.
-
비밀과 비밀 보장권 변할 것 같은 영역이나 파일의 포맷, 데이터 타입이 구현되는 방법, 또는 해당 영역에 있는 오류가 가능한 한 프로그램에 입히는 피해가 적도록 프로그램의 다른 부분으로부터 차단될 필요가 있는 영역들이 비밀이 된다. “완전하고 최소한의 클래스 인터페이스를 작성하기 위해 노력하라!!”
- 비밀의 두 종류
- 특별하게 관심이 없는 경우에 머리 속에 다룰 필요가 없도록 복잡성을 감추는 것
-
변경이 발생했을 때, 그 효과가 지역화되도록 변경의 원인을 감추는 것
-
정보 은닉에 대한 장애물 정보의 지나친 배분 순환 참조 클래 데이터를 전역 데이터로 혼동 성능 손해
- 정보 은닉의 가치 무엇을 숨겨야 하는지에 대한 질문은 모든 수준에서 좋은 설계 결정에 도움을 준다.
5.3.6. 변경될 거 같은 영역을 규명하라.
5.3.6.1. 변경될 것처럼 보이는 항목을 규명한다.
5.3.6.2. 병경될 것 같은 항목을 분류한다.
5.3.6.3. 변경될 것처럼 보이는 항목을 고립시킨다.
-변경될거같은 영역
- 비즈니스 규칙
- 하드웨어 의존성
- 입력과 출력
- 비표준 언어 기능
- 어려운 설계와 구현 부분
- 상태변수 불린 변수를 상태 변수처럼 사용하지 말라. 대신 열거 형을 사용하라. 상태 변수에 새로운 상태를 추가하는 일은 흔히 발생하며, 열거 형에 새로운 타입을 추가하면 변수를 검사하는 모든 코드를 검토하는 대신 컴파일만 다시 하면 된다. 변수를 직접적으로 접근하는 대신 접근 루틴을 사용하라. 변수 대신 접근 루틴을 검사함으로써, 보다 정교하게 상태를 검사할 수 있다.
- 데이터 크기 제약
-변경의 정도 예상하기
5.3.7. 느슨한 결합을 유지하라(loose coupling) 결합(coupling)은 클래스나 루틴이 다른 클래스나 루틴과 얼마나 밀접하게 연관되어 있는지를 기술한다. 모듈간의 연결은 단순하게 만들어야 한다.
- 결합의 기준
-
크기: 모듈간의 연결의 수를 의미한다.
-
가시성: 두 모듈 간 연결의 현저함을 의미한다. 매개변수 목록에 데이터를 전달하는 것은 분명한 연결을 하는 것이기 때문에 좋지만, 다른 모듈이 데이터를 사용할 수 있도록 전역 데이터를 수정하는 것은 교묘한 연결이기 때문에 나쁘다. 전역 데이터 연결을 문서화하는 것은 그러한 연결을 분명하게 만들기 때문에 약간 낫다.
-
유연성:모듈 간의 연결을 얼마나 쉽게 변경할 수 있는지를 의미한다. 다른모듈이 어떤 모듈을 쉽게 호출할수록, 결합이 느슨해지며 보다 유연하고 유지 보수가 가능해지기 때문에 더 좋다. 시스템 구조를 생성할 때, 최소한의 상호 연결을 기준으로 프로그램을 나누어라. 만약 프로그램이 나무 한그루라면, 깨알같이 쪼개도록 한다.
-
결함의 종류
-
간단한-데이터-매개변수 결합: 만약 두 모듈 사이에서 전달되는 모든 데이터가 기본 데이터 형이고 모든 데이터가 매개변수 목록을 통해서 전달된다면, 두 모듈은 간단한-데이터-매개변수로 결합되어 있다. 이러한 종류의 결합은 자연스럽고 허용 가능하다.
-
간단한-객체 결합: 만약 모듈이 객체를 인스턴스화한다면, 그 모듈은 객체에 간단한-객체로 결합되어 있다. 이러한 종류의 결합은 훌륭하다.
-
객체-매개변수 결합: 만약 Object1이 Object2에게 Object3을 자신에게 넘겨주도록 요구한다면, 두 모듈은 서로에 대해서 객체-매개변수로 결합되어 있다. 이러한 종류의 결합은 Oject2가 Object3에 대해서 알아야 하기 때문에, Object1이 Object2에게 기본 데이터 형을 자신에게 넘기도록 하는 것보다 결합이 좀 더 강하다.
-
의미론적 결합: 한 모듈이 다른 모듈의 구문론적인 요소로 이용하는 것이 아니라 다른 모듈의 내부 작동에 대한 의미론적인 지식을 사용하고 있을 때, 가장 발견하기 어려운 종류의 결합이 발생한다.
-
KeyPoint: 클래스와 루틴은 복잡성을 줄이기 위해서 제일 먼저 사용되어야 하는 지능적인 도구이다.
5.3.8. 일반적으로 널리 사용되는 설계
설계 패턴은 소프트웨어에서 가장 흔히 발생하는 많은 문제들을 해결하기 위해 사용될 수 있는 이미 만들어진(ready-made) 해결책의 핵심적인 부분을 제공한다.
- 패턴은 이미 만들어진 추상화를 제공함으로써 복잡성을 줄인다.
- 패턴은 일반적으로 널리 사용되는 해결책의 세부 사항등을 규정함으로써 오류를 줄인다.
- 패턴은 설계 대안들을 제안함으로써 발견적 학습의 가치를 제공한다.
-
패턴은 설계 대화를 보다 높은 수준으로 옮김으로써 의사 소통을 원활하게 한다.
- 주의사항
- 패턴을 사용함에 있어서 주의해야 할 부분은, 패턴을 사용하기 위해 코드를 억지로 끼워 맞추는 것이다. 어떤 경우에는 잘 알려진 패턴을 따르기 위해서 코드를 변형하면 코드에 대한 이해를 향상시킬 수 있다. 하지만 코드가 지나치게 변형되어야 한다면, 표준 패턴처럼 보이도록 하려는 작업이 복잡성을증가시킬 수도 있다.
- 패턴이 적절한 설계 방법이라기보다는 패턴을 사용해 보기 위해서 패턴을 사용하는 것이다.
5.8.9. 다른 발견적 학습 (어떤 생각을 갖고 개발할것인가?)
- 강한 응집력을 목표로 세워라. (느슨한 결합 강한응집력!!!)
- 계층을 만들어라.(정보의 계층적 구조화: 추상화- > 상세화) 계층은 개념의 가장 일반적인 또는 추상적인 항목이 계층의 최상위에 포함되고, 점차적으로 상세하고 구체적인 항목이 낮은 수준의 계층에 포함되는 층으로 이루어진 정보 구조이다.
- 클래스 계약을 형식화하라.
- 책임을 할당하라. 객체에 어떻게 책임을 할당할 것인가를 생각
- 테스트를 위해서 설계하라.
- 실패를 피하라. 이전의 성공에 촛점을 맞추기보다는 실패를 피하는 것에 염두를 두고 설계하라.
- 바인딩 시간을 의식적으로 선택하라. 바인딩 시간은 특정한 값이 변수에 결합되는 시간이다.
- 제어의 중심점을 만들어라. 정확한 장소의 법칙-중요한 코드를 찾기 위한 정확한 장소와 유지 보수 변경을 수행하기 위한 정확한 장소가 있어야 한다.
- 주먹구구식 기법의 사용을 고려하라. 의심스러울 때에는 주먹구구식 방법을 사용한다.
- 다이어그램을 그려라. 그림을 그려라.
- 설계의 모듈화를 유지하라. 모듈화의 목표는 각 루틴이나 클래스를 “블랙박스”처럼 만드는 것이다.
설계의 발견적 학습에 대한 요약
- 실세계의 객체를 찾아라.
- 일관성 있는 추상화를 구성하라.
- 세부 사항들을 캡슐화하라.
- 상속이 설계를 단순화할 수 있을 때 상속하라.
- 비밀을 숨겨라(정보은닉)
- 변경될 것 같은 영역을 규명하라.
- 느슨한 결합을 유지하라.
- 일반적으로 널리 사용되는 설계 패턴을 찾아라.
다음은 때때로 유용한 발견적 학습들
- 강한 응집력을 목표로 세워라.
- 계층을 만들어라.
- 클래스 계약을 형식화하라.
- 책임을 할당하라.
- 테스트를 위해서 설계하라
- 실패를 피하라
- 바인딩 시간을 의식적으로 선택하라
- 제어의 중심점을 만들어라
- 주먹구구식 기법의 사용을 고려하라
- 다이어그램을 그려라
- 설계의 모듈화를 유지하라
5.3.9. 발견적 접근을 위한 지침
- 문제의 이해
- 계획의 고안
- 계획의 실행
- 검토
5.4. 설계 방법
-
반복: 설계는 반복적인 프로세스이다. 언제나 A의 위치에서 B의 위치로만 가지는 않는다. 때로는 A에서 B로 가고, 다시 A로 돌아오기도 한다.
-
분할 정복: 프로그램을 서로 다른 관심의 영역으로 나누고, 각 부분을 개별적으로 처리하도록 한다. 만약 어떤 영역에서 막다른 길을 만난다면, 반복하도록 한다. 점증적인 개선은 복잡성을 다루는 강력한 도구이다.
- 하향식(Top down)과 상향식(Botton up) 설계 접근 방법 둘다 해보는것이 좋다.
- 하향식 설계는 높은 추상화 수준에서 시작한다. 기본 클래스나 구체적이지 않은 다른 설계 요소들을 정의한다. 설계를 개발함에 따라, 파생 클래스를 규명하고, 클래스들을 협력하고, 다른 세부적인 설계 요소들로 상세화 수준을 증가시킨다. => 분해전략
- 상향식 설계는 구체적인 것부터 시작해서 일반적인 쪽으로 작업을 한다. 전형적으로 구체적인 객체들을 규명하는 것부터 시작해서 객체들의 집합을 일반화시키고 기본 클래스를 생성한다. => 결합전략
-
실험적인 프로토타이핑(prototyping)
-
협력적인 설계
-
설계를 얼마나 해야 할까?
요소 | 구현하기 전에 설계에서 필요한 상세 수준 | 문서화 형식 - 설계 작업 기록하기
* 설계 문서를 코드 자체에 넣어라. (pseudo code 작성 후 주석 처리)
- 설계에 대한 논의와 결정을 위키(Wiki)에 기록하라.
- 이메일(e-mail)로 요약을 작성하라
- 디지털 카메라를 사용하라.
- 설계 플립 차트를 보관하라.
- CRC(클래스, 책입 , 협력자)카드를 사용하라
- 적절한 상세 수준에서 UML 다이어그램을 작성하라. UML은 표준화되었기 때문에, 설계 개념을 의사 소통하는 데 있어서 공통적인 인식을 지원하고 , 그룹 단위로 작업할 때 설계 대안들을 고려하는 작업을 빠르게 진행 할 수 있게 한다.
[설계 방법을 적용하는 데 있어서 독단적일수록, 해결할 수 있는 문제는 적어진다. 설계를 다루기 힘들고, 부주의하고, 발견적 학습 프로세스로 취급하라. 첫 번째 설계로 정하지 말라. 협력하라. 단순함을 추구하라. 필요할 때에는 프로토타이핑하라. 반복하고, 반복하고, 또 반복하라. 그러면 설계에 만족할 수 있을 것이다.]
요점 정리
- 소프트웨어의 일차적인 기술적 의무는 복잡성을 관리하는 것이다. 이것은 단순함에 촛점을 맞춘 설계의 도움을 크게 받을 수 있다.
- 단순함은 두 가지의 일반적인 방법으로 달성할 수 있다. 누군가가 한번은 처리해야 하는 본질적인 복잡성의 양을 최소화하고 부수적인 복잡성이 불필요하게 증가하지 않도록 하는 것이다.
- 설계는 발견적인 학습이다. 어떠한 단일 방법론에 독단적으로 집착하면 상상력과 프로그램에 해를 입힌다.
- 좋은 설계는 반복이다. 여러 번 시도할수록, 마지막 설계는 더 좋아 질 것이다.
- 정보 은닉은 매우 유용한 개념이다. “무엇을 숨겨야 하는가?”라는 질문이 해결하기 어려운 설계 상의 문제들을 해결해 준다. | Style : Background0, Font0, Size16 |
Comments