Upload
jay-park
View
6.975
Download
2
Embed Size (px)
Citation preview
+목차
n 기술 부채
n 설계 악취
n 설계 원칙
n 악취에 대한 사례 연구: 추상화, 캡슐화, 모듈화, 계층화
n 깨끗한 코드
+프로그램이 진화함에 따라 유지보수 작업을 행하거나 복잡도를 줄이지 않는 이상 프로그램의 복잡도는 증가한다.
- 늘어나는 복잡도에 대한 레만의 법칙
+부채
n 부채 n 미래의 부를 현재로 할인해서 당겨오는 행위로 인해 발생 n 부의 확장, 편의성 추구, 생존을 위한 지렛대로 사용할 수 있지만 기본적으로는 최대한 작게 유지(risk!)
n 이자 발생
n 상환 기간
n 복리 n 변동 금리
n 인플레이션
+설계 악취와 기술 부채
n 설계 악취의 정의 n 설계 품질에 부정적인 영향을 미치고 기본 설계 원칙을 위반하는 설계상의 특정 구조
n 설계 구조에서 잠재적인 문제
n 기술 부채의 정의 n 잘못되거나 최적화되지 않은 설계 결정을 의식적이거나 무의식적으로 내릴 경우 축적되는 부채다
n 워드 커닝햄(1992)이 처음으로 사용 n 누적된 이자를 갚지 못하면 파산 à 기술 부채를 해소하지 못하면 제품 파산
n 코드 부채, 설계 부채, 테스트 부채, 문서 부채
+기술 부채가 등장하는 이유?
n 빨라진 제품 주기 n 경쟁사에 비해 더 빨리 더 저렴하게 시장에 신제품 출시 요구 n 설계 결정의 영향력을 적절히 평가할 기회나 시간적 여유 부족
n 지역적인 설계 결정의 집합이 구조적인 품질을 떨어뜨림 à 설계 부채 축적
n 문제는 유지보수!
n 기술 부채가 눈에 보이지 않는 이유? n 결함은 최종 사용자에게 직접 보임 n 기술 부채는 소프트웨어 시스템의 내부 품질에 영향을 미침 à 보이지 않으므로 무시됨
+기술 부채가 미치는 영향
n 기술 부채의 구성 요소 n 원금(꼼수나 지름길)과 이자(상환하지 않을 경우 치뤄야 할 대가)
n 기술 부채에서 복리로 붙는 이자 문제 n 소프트웨어에 가해지는 새로운 변경이 부채에 허덕이는 설계 구조와 결합해 부채를 더 늘이기 때문 à 더 많은 꼼수 à 더 많은 부채
n 변경 비용이 기술 부채에 따라 기하급수적으로 증가함 à 기술 파산
n 개발팀의 사기 저하 n 부채를 짊어진 3류 제품을 개발하고픈 개발자는 아무도 없다!
n 단기간에 부채를 갚기란 쉽지 않기 때문 n 소프트웨어 변경을 시도할 경우 발생하는 불확실성과 위험에 직접 노출
+
+기술 부채를 초래하는 원인
n 관리자, 아키텍트, 개발자들이 내린 엉뚱한 결정의 원인 n 일정 압력: 복사해붙이기 n 좋은/숙달된 설계자 부족: 악화(나쁜 관례)가 양화를 구축
n 설계 원칙의 응용 부족: 경험/인식 부족 n 설계 악취와 리펙터링에 대한 인식 부족
Programming == Googling stackoverflow?
+기술 부채를 관리하는 방법
n 기술 부채에 대한 점진적인 자각
n 기술 부채의 감지와 상환
n 기술 부채의 누적 방지
+설계 악취란?
n 설계가 잘못되었음을 알려주는 징표
n 악취에 신경을 써야 하는 이유? n 소프트웨어 이해 가능성에 치명적인 문제를 일으킴 n 결함 수정과 개선을 위한 변경이 끊이지 않고 유지 보수 시간이 늘어남
n 논리적인 재사용이 불가함 n 안정성과 테스트 가능성이 점점 나빠짐
n 소프트웨어 설계가 소프트웨어 품질에 영향을 미침 n 설계가 잘못되면 악취가 발생!
+악취가 영향을 미치는 품질 속성
n 소프트웨어 비기능 측면 n 이해 가능성 n 변경 가능성
n 확장 가능성 n 재사용 가능성
n 테스트 가능성
n 안정성
n 주의: 비기능은 소프트웨어의 내재된 속성이므로 개발자가 보증해야 한다!
+설계 악취를 일으키는 요인
n 설계 원칙 위반
n 부적절한 패턴 사용
n 언어 제약
n 객체지향에서 절차적인 사고 방식
n 점성
n 우수 관계와 우수 프로세스의 미준수
+설계 악취를 일으키는 요인
n java.util 패키지 일부인 Calendar 클래스 n 실세계의 달력 기능을 추상화 n 날짜와 관련된 기능을 제공
n 하지만 시간 관련 기능도 제공
n 여기서 위반된 설계 원칙 n 추상화는 유일한 책임만 맡아야 함 à 추상화 원칙 위반
n 다면적인 추상화 악취 발생
사례(설계 원칙 위반)
+설계 악취를 일으키는 요인
n JDK의 AbstractQueuedSynchronizer와 AbstractQueuedLongSynchronizer 클래스 n 두 클래스는 AbstractOwnableSynchronizer 에서 파생 n 원시 타입의 차이뿐!
n 여기서 위반된 설계 원칙 n 펙터링이 안 된 설계 계층 악취 n 문제) 자바에서는 제네릭에서 원시 타입을 지원하지 않음! à 필연적인 코드 중첩 발생
사례(언어 제약)
+설계 악취를 일으키는 요인
n 소프트웨어 점성이란? n 올바른 해법을 문제에 적용하는 과정에서 반드시 직면하는 저항(늘어나는 노력과 시간)
n 꼼수는 시간과 노력을 적게 요구(낮은 저항)
n 환경 점성이란? n 우수 관례를 따르기 위해 반드시 극복해야 할 소프트웨어 개발 환경으로 인한 ‘저항’
n 우수 관계를 따를 때 시간이 더 많이 걸리면 나쁜 관례를 선택! n 개발 프로세스, 재사용 프로세스, 조직 요구 사항, 법적인 제약
사례(점성)
+악취 분류에 기반한 설계 원칙
n 악취 맥락에서 위반이 일어난 설계 원칙을 인식할 필요가 있음 n 추상화
n 단순화와 일반화 기법을 사용해 엔티티의 단순화를 옹호함
n 단순화는 불필요한 세부 사항을 제거하며, 일반화는 공통적이며 중요한 특성을 파악하고 명세함
n 캡슐화
n 추상화의 세부 구현과 변형을 숨기는 기법을 사용해 관심사 분리와 정보 은닉을 옹호 n 모듈화
n 지역화와 분해 기법을 사용해 응집력은 높고 결합력은 낮은 추상화 생성을 옹호 n 계층 구조
n 분류, 일반화, 대체, 배치와 같은 기법을 사용해 계층적인 추상화 구조 생성을 옹호
+몇 가지 추가적인 설계 원칙(1)
n OCP(Open/Close Principle) n 모듈은 확장에 대해 열려야 하지만 변경에 대해 닫혀야 마땅하다 n 모듈은 코드 변경 없이 새로운 요구 사항을 지원할 수 있어야 마땅하다
n 단일 책임 원칙(SRP, Single Responsibility Principle) n 클래스가 변경되어야 하는 이유가 결코 둘 이상 있어서는 안 된다 n 각 책임은 변경 축이되므로, 각 변경은 단일 책임에만 영향을 미쳐야 마땅하다
n LSP(LISKOV’S SUBSTITUTION PRINCIPLE) n 상속 받은 클래스는 사용자가 차이점을 알 필요 없이 기초 클래스 인터페이스를 통해 사용 가능해야만 한다
+몇 가지 추가적인 설계 원칙(2)
n KISS(KEEP IT SIMPLE SILLY) 원칙 n 소프트웨어 설계 구성에는 두 가지 방법이 있다. 하나는 아주 단순하게 만들어 명백하게 의존성이 하나도 없게 만드는 방법이며, 다른 하나는 아주 복잡하게 만들어 명백한 의존성이 하나도 없게 만드는 방법이다
n 정보 은닉(Information Hiding) 원칙 n 어렵거나 변경되기 쉬운 설계 결정을 파악하고 적절한 모듈이나 타입을 생성해 다른 모듈이나 타입으로부터 이런 결정을 감추는 방식
n DRY(Don’t Repeat Yourself) 원칙 n 모든 지식은 시스템 내부에서 단일하고 모호하지 않고 권위 있는 표현이 되어야만 한다
n 비순환 의존성 원칙(ADP, Acyclic Dependencies Principle) n 패키지 사이의 의존성은 순환을 형성해서는 안 된다
+
+사례 연구
n 중복된 추상화 악취란? n 이름이 같거나 구현이 동일하거나 이름도 같고 구현도 동일할 때 발생하는 악취
n 동일한 이름: 추상화 이름이 동일(우연히 이름이 동일할 가능성에 주의!)
n 동일한 구현: 의미상으로 동일한 멤버 정의가 있는 경우
n 문제의 원인: DRY, 중복 방지 원칙을 어김(예: 복붙) n 여파: 코드 중복으로 인한 유지보수가 어려워짐
추상화 악취 중 중복된 추상화
+사례 연구
n 예제: JDK에 존재하는 이름이 동일한 클래스 n 자바 7에서 4005개 타입 중 135개 타입이 중복(3.3%)! n 예: java.util.Date와 여기서 파생된 java.sql.Date 클래스
n 컴파일러가 문제 삼지 않는 이유 n 패키지가 다르다
n 사용자 입장에서 문제가 되는 이유 n 두 클래스를 동시에 import할 경우 명시적으로 한정해야 한다 à 모호성 문제를 수동으로 해소
추상화 악취 중 중복된 추상화
+사례 연구
n 리펙터링 제안 n java.sql.Date JavaDoc 문서: “SQL DATE 정의에 순응하기 위해, java.sql.Date 인스턴스가 감싼 밀리초 값은 관련된 인스턴스가 위치한 특정 시간대에 맞춰 시, 분, 초, 밀리초를 0으로 설정하는 방식으로 ‘정규화되어야’만 한다.”
n 더 나은 설계안
n java.sql.Date에서 java.util.Date 인스턴스를 감싸기 n 상속을 위임으로 변환
n 또한 java.sql.SQLDate로 이름 변경
추상화 악취 중 중복된 추상화
+사례 연구
n 누락된 캡슐화 악취란? n 구현 변형을 계층 내부나 추상화 내부에서 캡슐화하지 않을 경우 발생하는 악취
n 문제의 원인: OCP 위반(타입의 행동 양식은 변경이 아니라 확장에 의해 바꿔야 함)
n 여파: 계층에 새로운 변형을 지원하려고 시도할 때마다 불필요한 클래스 ‘폭발’ 발생
캡슐화 악취 중 누락된 캡슐화
+사례 연구
n 예제(1) n 데이터 암호화를 위한 Encryption 클래스: DES (Data Encryption Standard), AES (Advanced Encryption Standard), TDES (Triple Data Encryption Standard)를 포함해 알고리즘에 대한 다양한 선택이 가능
n 초보 개발자는 Encryption 클래스 내부에 EncryptUsingDES(), EncryptiUsingAES(), …와 같은 수많은 메소드를 추가
n 문제점 n Encryption 클래스가 알고리즘 추가에 따라 점점 커짐(한번에 알고리즘을 하나만 사용하더라도 덩치 큰 클래스를 가져와야 함)
n 새로운 알고리즘 추가가 어려움
n 특정 알고리즘 하나만 재사용하기가 불가능
캡슐화 악취 중 누락된 캡슐화
+사례 연구
n 예제(2) n 데이터 암호화를 위한 알고리즘 뿐만 아니라 다양한 유형의 데이터를 암호화
n 내용 유형과 암호화 알고리즘 유형의 두 가지 변형이 존재 n DESImageEncryption, AESTextEncryption, …
n 문제점 n 구현에서 변형이 서로 뒤섞이며, 독자적으로 캡슐화되지 못함
n 클래스 폭발로 마무리
캡슐화 악취 중 누락된 캡슐화
+사례 연구
n 리펙터링 제안 n 예제(1)
n EncryptionAlgorithm 인터페이스를 생성 n DESEncryptionAlgorithm와 AESEncryptionAlgorithm는 EncryptionAlgorithm 인터페이스를 구현하고 각각 DES와 AES 알고리즘을 정의
n Encryption 클래스는 EncryptionAlgorithm 인터페이스에 대한 참조를 유지
n 장점 n Encryption 객체는 실행 시간에 특정 암호화 알고리즘으로 구성 가능 n EncryptionAlgorithm 계층에 정의된 알고리즘은 다른 맥락에서 재사용 가능
n 새로운 알고리즘 추가 용이
캡슐화 악취 중 누락된 캡슐화
+사례 연구 캡슐화 악취 중 누락된 캡슐화
깨진 계층화 악취
다른 맥락에서 알고리즘 재사용 불가
+사례 연구
n 리펙터링 제안 n 예제(2)
n 두 가지 직교적인 관심사에 대해 독자적으로 변형을 캡슐화
캡슐화 악취 중 누락된 캡슐화
+사례 연구
n 불충분한 모듈화 악취란? n 완벽하게 분해되지 않은 추상화 à 추가로 분해할 경우 크기/구현 복잡도 해소 가능
n 부풀어로은 인터페이스: 추상화의 공개 인터페이스에 엄청나게 많은 멤버 존재
n 부풀어오른 구현: 구현에 엄청나게 많은 메소드 존재/구현 복잡도가 과도하게 높은 메소드가 하나 이상 존재
n 문제의 원인: 추상화를 관리 가능한 크기로 유지하지 못함
n ‘연산과 클래스는 조화로운 크기가 되어야 마땅하다. 다시 말해, 크기는 양극단을 피해야 마땅하다.”
n 주의: SRP와 혼동 à 책임이 하나인 추상화면서도 여전히 크고 복잡할 수 있다. 유사하게, 작은 추상화면서도 여전히 책임이 여럿일 수 있다
모듈화 악취 중 불충분한 모듈화
+사례 연구
n 불충분한 모듈화 악취란? n 완벽하게 분해되지 않은 추상화 à 추가로 분해할 경우 크기/구현 복잡도 해소 가능
n 부풀어로은 인터페이스: 추상화의 공개 인터페이스에 엄청나게 많은 멤버 존재
n 부풀어오른 구현: 구현에 엄청나게 많은 메소드 존재/구현 복잡도가 과도하게 높은 메소드가 하나 이상 존재
n 문제의 원인: 추상화를 관리 가능한 크기로 유지하지 못함
n ‘연산과 클래스는 조화로운 크기가 되어야 마땅하다. 다시 말해, 크기는 양극단을 피해야 마땅하다.”
n 주의: SRP와 혼동 à 책임이 하나인 추상화면서도 여전히 크고 복잡할 수 있다. 유사하게, 작은 추상화면서도 여전히 책임이 여럿일 수 있다.
모듈화 악취 중 불충분한 모듈화
+사례 연구
n 예제: JDK에 존재하는 java.awt.Component 클래스 n 메소드가 332개(그 중 259개가 public으로 선언!) n 중첩된 내부 클래스가 11개, 필드가 107개(상수 포함)
n 원시 코드: 10,102행
n 문제점 n 분석, 유지보수, 기능 추가 과정에서 이 클래스를 이해하기가 너무 복잡하다!
추상화 악취 중 중복된 추상화
+사례 연구
n 리펙터링 방안 n 부풀어오른 인터페이스
n 공개 인터페이스의 부분 집합이 밀접히 연관된 (응집력이 높은) 멤버를 포함하면, 부분 집합을 독자적인 추상화로 추출
n 클래스가 멤버로 구성된 부분 집합을 둘 이상 포함하며 각 부분 집합의 응집력이 높으면, 이런 부분 집합을 독자적인 클래스로 나눔
n 클래스 인터페이스가 클라이언트에 밀접한 메소드를 사용해 여러 클라이언트를 서비스하면, 원본 인터페이스를 클라이언트에 밀접한 여러 인터페이스로 분리하기 위해 ISP(Interface Segregation Principle)를 적용
n 부풀어오른 구현 n 메소드 논리가 복잡하면, 해당 메소드에 있는 코드를 단순하게 만들기 위해 사적인 도우미 메소드를 도입
n 불충분한 모듈화 악취와 함께 다면적인 추상화 악취까지 포함하는 추상화라면, 각 책임을 분리된 (새로 만들거나 기존에 존재하는) 추상화에 캡슐화
추상화 악취 중 중복된 추상화
+사례 연구
n 넓은 계층 악취란? n 중간 타입이 빠졌을지도 모른다는 사실을 암시하는 ‘너무’ 넓은 상속 관계가 존재하는 악취
n 문제의 원인: ‘의미있는 일반화 적용’이라는 요소 기술을 위반 n 빠진 중간 타입은 계층에 있는 클라이언트가 하위 타입을 직접 참조하게 강제할지도 모른다. 이런 의존성은 계층의 변경 가능성과 확장 가능성에 영향을 미친다.
n 타입 내부에 불필요한 중복이 있을지도 모른다(중간 타입의 부족으로 인해 공통성을 적절히 추상화할 수 없다)
n 영향: 동일 층에 하위 타입 수가 너무 많으면(9개를 넘길 경우), 계층을 이해하거나 사용하기가 더 어려워짐
계층 악취 중 넓은 계층
+사례 연구
n 예제 n JDK 7에서 바로 아래 하위 클래스 36개를 거느리는 java.util.EventObject 클래스
n 문제점 n 형제자매 클래스가 엄청나게 많아서 이해가 어려움
계층 악취 중 넓은 계층
+사례 연구
n 리펙터링 제안 n 중간 타입을 도입 n 예: TreeEvent
계층 악취 중 넓은 계층
+설계에서 코드로
n 설계 리펙터링은 코드 리펙터링과 밀접한 관련이 있다! n 깨끗한 설계를 유지하는 동시에 깨끗한 코드를 유지해야 한다
+참고 자료
+