리팩토링이란 소프트웨어를 보다 쉽게 이해할수 있고 적은 비용으로 수정할수 있도록 같으로 보이는 동작의 변화 없이 내부 구조를 변경한 것 이라고 Martin Flowler는 정의했다.

 

리팩토링을 하는 이유

  • 코드가 중복되어 있다.
  • 루틴이 너무 길다
  • 루프가 너무 길거나 깊게 중첩되어 있다
  • 클래스의 응집력이 약하다
  • 클래스 인터페이스가 일관된 추상화 수준을 제공하지 않는다
  • 매개변수가 너무 많다
  • 클래스 내에서의 변경 사항이 상호관계를 고려하지 않고 구분되는 경향이 있다.
  • 변경을 할 때 여러 개의 클래스를 동시에 수정해야 한다
  • 상속 계층 구조가 병렬로 변경되어야 한다
  • case 문이 병렬로 변경되어야 한다
  • 함께 사용되는 연관된 데이터 항목이 클래스로 구성되지 않았다
  • 루틴이 자신이 포함된 클래스보다 다른 클래스의 기능을 더 많이 사용한다
  • 기본 데이터 형이 오버도르되었다
  • 클래스가 많은 일을 수행하지 않는다
  • 일련의 루틴들이 뜨내기 데이터(tramp data)를 전달하고 있다.
  • 미들 맨(middle man)객체가 아무것도 하지 않는다
  • 한 클래스가 지나치게 다른 클래스를 참견한다
  • 루틴의 이름이 엉성하다
  • 데이터 멤버가 public이다
  • 서브 클래스는 부모 클래스가 제공하는 그히 일부만 사용한다
  • 주석이 어려운 코드를 설명하기 위하여 사용되었다.
  • 전역 변수가 사용되었다

 

리팩토링을 하면 안되는 이유 - 일반적으로 리팩토링은 결함을 수정하고 기능을 추가하고 설계를 변경하는 것을 가르키는 것처럼 부정확하게 사용된다. 본질적으로 코드를 변경하는 것과 동의어로 사용된다. 이 용어의 의미를 이렇게 희석시키는 것을 불행한 일이다. 변경 그 자체는 미덕이 아니지만 원칙이 적용된 목적이 있는 변경은 유지 보수중에 프로그램의 품질을 꾸준히 향상 시키고 너무나도 잘 알고 있는  SW 엔트로피라는 죽음의 소용돌이를 예방하기 위한 중요한 전략일수 있다.

 

구체적인 리팩토링

데이터 수준 리팩토링

  • 매직 넘버를 명명된 상수로 대체한다
  • 변수이름을 보다 분명하고 많은 정보를 제공하는 이름으로 다시 명명명한다
  • 표현식을 인라인으로 이동시킨다
  • 표현식을 루틴으로 대체한다
  • 중간 변수를 사용한다 - 표현식의 목적을 잘 요약하는 이름을 갖는 중간 변수에 표현식을 할당한다
  • 여러 목적으로 사용되는 변수를 여러개의 단일 목적을 갖는 변수로 변환한다
  • 로컬에서 사용하기 위한 목적이라면 매개변수 대신 지역변수를 사용한다
  • 기본 데이터를 클래스로 변환한다
  • 타입 코드 집합을 클래스나 열거로 변환한다
  • 타입 코드 집합을 서브 클래스를 갖는 클래스로 변환한다
  • 배열을 객체로 변경한다- 만약 각각의 오소들이 형식이 서로 다른 배열을 사용하고 있다면, 배열의 각 요소들에 대한 필드를 갖는 객체를 생성한다
  • 컬렉션을 캡슐화한다
  • 전형적인 레코드를 데이터 클래스로 대체한다 - 레코드의 멤버를 포함하는 ㅋ르래스를 생성한다. 클래스를 생성하면 오류 검사, 지속성, 그리고 레코드와 관련된 다른 연산들을 집중시킬수 있다

 

명령문 수준 리팩토링

  • 불린 표현식을 분해한다
  • 복잡한 불린 표현식을 잘 명명된 불린 함수로 이동시킨다
  • 서로 다른 조건문 내에 중복되어 있는 코드들을 결합한다
  • 루프제어 변수 대신 break나 return을 사용한다
  • 중첩된 if-then-else 명령문 내에서 리턴 값을 할당하는 대신, 답을 알았을떄 곧바로 리턴한다
  • 조건문(특히, 반복되는 case문)을 다형성으로 대체한다
  • 널 갑3ㅅ을 테스트하는 대신, 널 객체를 생성하여 사용한다

 

루틴 수준 리팩토링

  • 루틴을 추출한다. 메소드를 추출한다
  • 루틴의 코드를 인라인으로 이동시킨다
  • 긴 루틴을 클래스로 변환한다
  • 복잡한 알고리즘 대신 간단한 알고리즘을 사용한다
  • 매개 변수를 추가한다
  • 매개 변수를 제거한다
  • 변경 연산으로부터 쿼리 연산을 분히한다
  • 매개 변수를 이용하여 유사한 루틴을 결합한다
  • 전달되는 매개변수에 따라서 행동하는 루틴을 분리한다
  • 특정한 필드 대신 전체 객체를 전달한다
  • 다운 캐스팅(downcasting)을 캡슐화한다 - 만약 루틴이 객체를 리턴하고 있다면, 일반적으로 루틴이 알고 있는 가장 구체적인 객체의 타입으로 리턴해야한다

 

클래스 구현 리팩토링

  • 값 객체를 참조 객체로 변경한다
  • 참조 객체를 값 객체로 변경한다
  • 가상 루틴들을 데이터 초기화로 대체한다
  • 멤버 루틴이라 데이터의 위치를 변경한다
  • 특화된 코드를 서브 클래스로 추출한다
  • 유사한 코드를 슈퍼 클래스로 결합한다

 

클래스 인터페이스 리팩토링

  • 루틴을 다른 클래스로 이동시킨다
  • 한 클래스를 두개로 변환한다
  • 클래스를 제거한다
  • 위임(delegate)을 숨긴다
  • 미들맨들 제거한다
  • 상속을 위임으로 대체한다 - 만약 클래스가 다른 클래스를 사용해야 하지만, 클래스의 인터페이스를 제어하고 싶다면, 슈퍼클래스를 서브 클래스의 필드로 만들고 응집력있는 추상화를 제공하는 일련의 루틴들을 노출시킨다
  • 위임을 상속으로 대체한다. - 만약 클래스가 위임클래스(멤버 클래스)의 모든 public 루틴들을 노출하고 있다면, 클래스를 사용하는 대신 위임 크래로부터 상속을 받도록 한다
  • 외부 루틴을 소개한다
  • 확장 클래스를 소개한다
  • 노툴된 멤버 변수를 캡슐화한다
  • 변할수 없는 필드에 대한 set()루틴을 제거한다
  • 클래스 외부에서 사용되기 위한 것이 아닌 루틴을 감춘다
  • 사용되지 않는 루틴을 캡슐화한다
  • 슈퍼 클래스와 서브 클래스의 구현이 매우 유사하다면, 이 둘을 결합한다

 

시스템 수준 리팩토링

  • 제어할수 없는 데이터를 위해서 명확한 참조 소스를 생산한다
  • 단방향 클래스 관계를 양방향 클래스 관계로 바꾼다
  • 양방향 클래스 관계를 단방향 클래스 관계로 바꾼다
  • 간단한 생성자 대신 팩토리(factory)메소드를 제공한다 - 타입 코드를 기반으로 객체를 생성해야 하거나 값 객체가 아닌 참조 객체를 다루어야 할때, 팩토리 메소드(루틴)을 사용한다
  • 오류코드를 예외로 대체하거나 그 반대로 한다

 

안전한 리팩토링

  • 리팩토링을 시작하는 코드를 저장한다 - 시작 전에 백업하기
  • 리팩토링을 작게 유지한다
  • 리팩토링은 한번에 하나만 수행한다
  • 여더분이 취할 단계에 대한 목록을 만든다
  • 주차장을 만든다 - 장차 리팩토링을 할 필요가 있는 것의 목록을 만든다
  • 빈번하게 체크 포인트를 설정한다
  • 컴파일러의 경고를 활용한다
  • 다시 테스트한다 - 변경한 코드에 대한 무결성 유지
  • 테스트 케이스를 추가한다
  • 변경 사항을 검토한다
  • 리팩토링의 위험 수준에 따라서 접근 방법을 조절한다

 

리팩토링에 좋지 않은 시기

  • 코드를 작성하고 수정하는 것을 감추는 용도로 리팩토링을 사용하지 않는다 - 제대로 동작하지 않는 코드를 수정하는 것은 리팩토링이 아니다. 개념을 혼동한 것이다.
  • 코드를 재성하는 대신 리팩토링을 하지 않는다 - 리팩토링보다 처음부터 새롭게 시작해야 할때는 리팩토링을 하지 않아야 한다

 

리팩토링 전략

  • 루틴을 추가할때 리팩토링한다
  • 클래스를 추가할때 리팩토링한다
  • 결함을 수정할때 리팩토링한다
  • 오류를 유발한 가능성이 있는 모듈을 대상으로 삼는다
  • 복잡도가 높은 모듈을 대상으로 한다
  • 유지 보수 환경에서는 여러분이 많은 부분을 개선한다
  • 정돈된 코드와 엉성한 코드 간의 인터페이스를 정의한후, 인터페이스를 통해서 코드를 이동시킨다.

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 2. 11. 11:51

이 글은 bmwe3님의 2008년 2월 7일의 미투데이 내용입니다.

by 무위자연 2008. 2. 8. 04:34

이 글은 bmwe3님의 2008년 1월 30일의 미투데이 내용입니다.

by 무위자연 2008. 1. 31. 04:34

테이블을 활용하는 기법은 어떠한 정보를 이해하기 위해서 논리적인 명령문(if, else)들을 사용하지 않고 테이블에 있는 정보를 검색하는 방식이다.

 

테이블을 활용할때 일반적으로 고려해야 할 사항들

테이블이 있다면, 복잡한 if와 비교가 난무하지 않고 charType = charTypeTable[inputChar];로 대신할수 있다.

테이블을 활용한 기법의 두가지 문제점.

  1. 테이블에 있는 엔트리들을 어떻게 참조할 것인가. 직접 접근 방식/ 인덱스 접근 방식/단계적 접근 방식
  2. 테이블을 활용한 기법을 사용하고 있을때 테이블에 무엇을 저장해야 하는지 설명해야 한다

 

직접접근방식 - 직접 접근 방식은 복잡한 논리 제어구조를 대체한다. 데이타의 엔트리가 있고 해당하는 것에 직접 접근 한다.

ex private var monthArray:Array = ["January","February","March","April","May","June","July","August" ,"September","October","November","December"];  식의 테이블이 있고

text = monthArray[monthValue]; 이런 식으로 사용하면 복잡한 if else를 대체할수 있다

이런식의 테이블 참조를 위해서 참조키를 조작한다 - 테이블의 인덱스와 사용하는 값이 1:1 대응 혹은 복잡한 수식을 통해서 대응될수 있다. 그래서

  • 키 값을 곧바로 사용할수 있도록 정보를 복사한다
  • 곧바로 사용할수 있도록 키 값을 반환한다.
  • 키 변환 기능을 루틴으로 작성하라 - 함수를 빼는 것과 같은 이치.!

 

인덱스 접근 방식 - 간단한 수학적인 변환만으로 충분하지 않은 경우에는 인덱스접근방식이 좋다. 인덱스를 사용할때에는 인덱스 테이블에 있는 키를 참조하기 위해서 원본데이터를 사용하고,

여러분이 참조하고자 하는 실제 데이터를 참조하기 위해서 인덱스 테이블에 있는 값을 사용한다.

인덱스 접근 방식의 장점

-만약 주 참조테이블(실제 데이터가 저장된 테이블)에 있는 각 엔트리들이 크다면, 많은 공간이 낭비되는  주 검색 테이블을 만드는 대신, 많은 공간을 낭비하는 인덱스 배열(주 검색 테이블의 위치를

저장하는 테이블)을 만들기때문에 공간이 더 적게 사용된다.

-인덱스를 사용함으로 공간을 절약할수 없다고 하더라도 메인 테이블에 있는 엔트리보다 인덱스에 있는 엔트리를 다루는 것이 더 간단한 경우가 있다.

 

단계적 접근 방식 - 인덱스 구조처럼 간접적으로 데이터에 접근하며, 데이터 공간의 낭비가 심하지 않다. 테이블에 있는 엔트리들이 특정한 데이터 위치가 아니라 데이터 영역을 나타내는 것이다.

다른 테이블 활용법 보다 불규칙한 데이터를 잘 다룬다. 그리고 유연성과 수정가능성을 갖는다. 주의할 사항은 다음과 같다

  • 종결점을 확인해라 - 각 단계의 최대값에 해당하는 경우를 처리했는지, '<' '<='를 혼동하지 않았는지 확인한다
  • 순차 검색법 대신 이진 검색법 사용을 고려해라
  • 단계적 접근 방식 대신 인덱스 접근 방식을 고려해라 - 상황에 따라 검색량과 효율이 인덱스 접근방식에 비해 떨어질수 있다.

 

 

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 1. 30. 09:24

boolean 표현식

boolean 테스트에서 true/ false 사용하기

0과 1보다는 코드의 가독성을 높이기 위해서 true/false를 사용하도록 한다. 언어가 지원이 안되면 매크로를 써서라도 쓴다

복잡한 표현식을 단순하게 만들기

  • 새로운 불린 변수를 사용하여 복잡한 테스트를 부분적인 테스트로 만들어라
  • 복잡한 표현식을 불린 함수로 이동시켜라
  • 복잡한 조건을 대체하기 위해서 결정 테이블을 사용해라

 

불린 표현식을 긍정적으로 작성하기.

  • if문에서는 부정문을 긍정문으로 변환한 다음 if절과 else절에 있는 코드를 바꾼다. if(!statusOK) 이런 식의 표현식은 가독성을 떨어뜨린다
  • 부정 불린 테스트를 단순화하기 위해서 드모르간 법칙을 적용하라. - 드모르간 법칙을 이용해서 동일한 의미를 가지면서 가독성이 높은 코드를 만든다

ex 자바로 작성한 부정 테스트에 대한 예제

if(!displayOK || !printerOK)

ex 위 예제에 드모르간 법칙을 적용한 것

if(!(displayOK && printerOK))

 

원본 표현식 동일한 표현식
not A and not B not (A or B)
Not A and B not (A or not B)
A and not B not (not A or B)
A and B not(not A or not B)
not A or notB not( A and B)
not A or B not (A and not B)
A or not B not (not A and B)
A or B not (not A and not B)

 

괄호를 사용하여 이해하기 쉬운 불린 표현식 만들기

  • 괄호의 균형을 유지하기 위해서 간단한 계산 기법을 사용해라.

ex 괄호의 균형이 유지된 자바 예제

if( ( ( ( a < b ) == ( c == d) ) && !done)...- 이 코드를 읽는다

0 1 2 3          2     3         2 1             0   - 0으로 시작해서 열린 괄호를 만나면 1증가 닫힌 괄호를 만다면 1감소 식으로 표기하면 괄호의 균형을 쉽게 맞출수 있다

  • 논리 표현식에는 충분할 정도로 괄호를 사용한다

          

 

0을 비교하는 방법

0은 다양하게 쓰인다. 숫자값 그자체로 쓰고, 문자열의  널종결자일수 있고 널포인터값이나 배열의 첫번째 인덱스일수도 있다. 그래서 사용할떄는 0의 목적을 명확하게 드러나게 코딩한다

  • 숫자는 0에 명시적으로 비교하라 ex while(balance).. 보다는 while(balance != 0) 이 더 좋다
  • 논리적인 변수를 암시적으로 비교해라 ex while(!done) ...
  • C언어에서는 문자를 널 종결자('\0')에 명시적으로 비교하라 ex while(*charPtr) ... 보다는 while(*charPtr != '\0')로 처리한다
  • 포인터를 NULL에 비교해라 - ex while(bufferPtr) 보다는 while( bufferPtr != NULL)...

 

불린 표현식과 관련된 일반적인 문제들

  • C에서 파생된 언어에서는 상수를 비교문의 왼쪽에 놓아라
  • C++에서는 &&, ||, ==을 대체하기 위한 전처리기 매크로 작성에 대해서 고려해 본다- 어쩔수 없는 경우에만)
  • 자바에서는 a== b와 a.equals(b)의 차이점을 이해해라 - 앞은 같은 곳을 참조하는지에 대한 검사이고 후자는 객체가 가지는 논리적인 값에 대한 검사이다.

 

복합문(블록)

복합문 혹은 블록은 프로그램의 흐름을 제어하기 위해서 단일 명령문으로 취급되는 명령문의 집합이다.

  • 중괄호 쌍을 함께 작성하라
  • 중괄호를 사용하여 조건문을 분명히 하라

 

 

널 명령문 - ";" 하나로 작성할수 있다. 단, 눈에 뛰게 해야 한다. 찾기 어려우니까

 

지나치게 깊은 중첩 구조의 처리 - 3수준 이상의 중첩은 좋지 않다

  • 조건의 일부분을 다시 테스트하여 중첩된 if문을 단순화해라
  • break 블록을 사용하여 중첩된 if문을 단순화해라
  • 중첩된 if문을 if-then-else문으로 변환하라
  • 중첩된 if문을 case문으로 변환해라
  • 중첩구조가 깊은 코드를 루틴으로 작성해라.
  • 보다 객체 지향적인 방법으로 접근하라.
  • 깊에 중첩된 코드는 재 설계하라

 

프로그래밍의 기초 - 구조적 프로그래밍

구조적 프로그래밍의 핵심은 오직 하나의 입구와 출구만이 있는 제어구조이다.(단일 진입점과 단일 탈출점 제어구조라고도 한다)

구조적 프로그래밍의 세가지 요소

  1. 순서(sequnce) - 순서는 순서대로 실행되는 명령문들의 집합이다
  2. 선택(selection) - 선택은 명령문을 선택적으로 수행하는 제어구조이다.
  3. 반복(iteration) -반복은 명령문 그룹을 여러번 실행하는 제어구조이다.

이와 같은 분류를 하는 것은 복잡도에 대한 가늠을 하기 위한 것이고 복잡도를 최소화하는 것이 고급 코드를 작성하는 지름길이다.

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 1. 30. 09:24

SW 품질의 외적 특성을 측정하기 위한 잣대

  • 정확성
  • 유용성
  • 효율성
  • 신뢰성
  • 무결성
  • 적응성
  • 정밀성
  • 견고성

 

SW품질의 내적 특성을 측정하기 위한 잣대

  • 유지보수성(!!!)
  • 유연ㅅ넝
  • 이식성
  • 재사용성
  • 가독성
  • 테스트 용이성
  • 이해성

 

SW의 품질을 향상시키기 위한 방법들

  • SW  품질의 목표
  • 명확한 품질보증 활동
  • 테스트 전략
  • SW공학 지침
  • 비형식적인 기술검토 - 동료들과 토론 포함
  • 형식적인 기술적 검토 SW
  • 외부 감사

 

저자의 요점 정리

  • 품질은 결과적으로 무료지만 결함을 비싸게 고치는 대신 싸게 예방하기 위해서 자원의 재분배가 요구된다
  • 모든 품질 보증의 목표들을 동시에 달성할수 없다. 달성하고자 하는 목표를 분명히 결정하고 결정된 목표를 팀우너들과 공유하라- agile개발론과 유사하다
  • 어떠한 단일 결함 감지 기법도 그 자체로 완벽하지 않다. 다양한 기법을 사용해야 한다
  • SW의 품질보증은 프로세스 지향적이다. SW개발은 제조업과 같은 최종 제품에 영향을 미치는 반복적인 단계가 없다. 따라서 결과물의 품질은 SW를 개발하기 위해서 사용되는 프로세스에 의해 관리되어야 한다.

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 1. 30. 09:24

여러 곳에서 리턴하는 루틴

  • 보다 읽기 쉬운 코드를 만들기 위해서 return을 사용해라. - 답을 구했거나 혹은 오류를 발견해서 return을 하면 나머지 루틴을 수행하면서 발생할수 있는 오류에 대한 처리가 필요없어진다.
  • 복잡한 오류 처리를 단순화하기 위해서 보호절(루틴 중간에 사용되는  return이나 exit)을 사용해라
  • 한 루틴에 있는 return의 수를 최소화해라.

 

재귀적 용법

재귀적 용법에는 한 루틴이 문제의 작은 부분을 해결하고 그 문제를 더 작은 부분으로 나눈 다음, 더 작은 부분의 각각을 해결하기 위해서 자신을 호출한다.

사용팁

  • 재귀 호출이 중단되는지 확인해라
  • 무한 재귀 호출을 막기 위해서 안전한 카운터를 사용해라.
  • 한 루틴으로 재귀 호출을 제한하라
  • 스택을 감시하라
  • 팩토리얼이나 피보나치 수열을 계산하기 위해서 재귀적 용법을 사용하지 않는다 - 재귀적 용법은 강력하나 이런 경우에는 쓰는것은 해고감이다. - 라네요 ㅋ - 이런 경우 재귀적용법보다는 다른 대안이나 스택과 반복문으로 처리할수 도 있는 문제인데 재귀적 용법은 좋지 않다.

 

goto문 - 사용해야하는 100가지를 충족시킬수 없다면 쓰지 마라. - 간단히 말해서- 즉 최후의 수단이다.

이 글은 스프링노트에서 작성되었습니다.

by 무위자연 2008. 1. 28. 08:52

이 글은 bmwe3님의 2008년 1월 24일의 미투데이 내용입니다.

by 무위자연 2008. 1. 25. 04:34
  • "gadget이란 단어를 찾다가 확 민망해졌쓰" (해리포터 소서러 필로소퍼) 2008-01-22 08:36:45
  • 지누션 4집 노라보세의 보이네가 리메이크 인줄은 동현이는 정말 몰랐네 ㅋ 나미씨의 노래구나! ㅋ (보이네 지누션 노라보세 리메이크 나미) 2008-01-22 12:23:10

이 글은 bmwe3님의 2008년 1월 22일의 미투데이 내용입니다.

by 무위자연 2008. 1. 23. 04:36
  • 늦잠자고 나왔더니 왠 눈발이 이리도 날리냐-_- 2008-01-21 08:26:45
  • What's your regular commute? 보통 뭐타고 출퇴근해요? (영어한마디) 2008-01-21 17:42:26
  • DateUtil.compareDates(tmpDate, dataStartDate) 왼쪽 날짜가 크면 -1을 오늘쪽 날짜가 크면 1을 같으면 0을 리턴한다. 이게 왜이리 헷갈리는 것일까나-_- 좌절이얌 2008-01-21 17:46:53

이 글은 bmwe3님의 2008년 1월 21일의 미투데이 내용입니다.

by 무위자연 2008. 1. 22. 04:35