|
Q1. STL 시퀸스 컨테이너와 연관 컨테이너 A1. STL 컨테이너는 크게 시퀸스 컨테이너와 연관 컨테이너로 나눕니다. 시퀸스 컨테이너는 vector, list, deque와 같이 순서 있게 자료를 보관합니다. 연관 컨테이너는 어떠한 Key와 짝을 이루어 자료를 보관합니다. 그래서 자료를 넣고, 빼고, 찾을 때는 Key가 필요합니다. 시퀸스 컨테이너는 많지 않은 자료를 보관하고 검색 속도가 중요한 경우에 사용하고, 연관 컨테이너는 대량의 자료를 보관하고 검색을 빠르게 하고 싶을 때 사용합니다. Q2. STL list를 사용해야 하는 경우 A2. 1. 저장할 데이터 개수가 가변적이다. - list는 저장 공간의 크기가 자동으로 변하므로 유연하게 사용 할 수 있다. 2. 중간에 데이터 삽입니다 삭제가 자주 일어난다. 3. 데이터를 랜덤하게 접근하는 경우가 많지 않다. - list는 순차접근만 가능하기 때문에 반복자를 통해서 접근해야 한다. Q3. STL vector를 사용해야 하는 경우 A3. 1. 저장할 데이터 개수가 가변적이다. - 배열과 vector의 가장 큰 차이점은 배열은 크기가 고정이고 vector는 동적으로 변한다는 것이다. 2. 중간에 데이터 삽입니다 삭제가 없다. - vector는 배열처럼 데이터를 순차적으로 저장한다. 중간에 데이터 삭제 및 삽입을 하면 배열과 같은 문제가 발생한다. vector는 가장 뒤에서부터 데이터를 삭제 하거나 삽입하는 경우에 적합하다. 3. 저장할 데이터 개수가 적거나 많은 경우 빈번하게 검색하지 않는다. - 데이터를 순차적으로 저장하므로 많은 데이터를 저장한다면 검색 속도가 빠르지 않다. 검색을 자주한다면 map나 set, hash_map을 사용해야 합니다. 4. 데이터 접근을 랜덤하게 하고 싶다. - vector는 배열 같은 특성이 있어서 랜덤 접근이 가능하다. 특정 데이터가 저장된 위치를 안다면 랜덤 접근을 사용하는 쪽이 성능이 좋고, 사용하기도 간편하다. Q4. vector Vs list? A4. vector와 list의 차이점은 크게 [중간 삽입, 삭제] [랜덤 접근] 2가지이다. 중간 삽입, 삭제가 없고 랜덤 접근을 자주 해야 된다면 vector가 좋고, 중간 삽입, 삭제가 자주 있으며 랜덤 접근이 필요 없으면 list가 좋다. Q5. STL deque를 사용해야 하는 경우 A5. 1. 앞과 뒤에서 삽입, 삭제를 한다. - 이것으 deque를 사용하는 가장 큰 이유이다. 대부분 작업이 데이터를 앞이나 뒤에 삽입, 삭제를 한다면 STL 컨테이너 라이브러리 중에서 deque를 사용할 때 성능이 가장 좋다. 2. 저장할 데이터 개수가 가변적이다. - 저장할 데이터 개수를 미리 알 수 없어도 deque는 크기가 동적으로 변하므로 유연하게 사용 할 수 있습니다. 3. 검색을 거의 하지 않는다. - 많은 데이터를 저장한다면 map, set, hash_map 중 하나를 선택해서 사용하는 편이 좋습니다. 4. vector과 같은 랜덤 접근이 필요할 경우 동일한 방법으로 접근 할 수 있다. Q6. STL map을 사용해야 하는 경우 A6. map의 자료구조는 '트리(tree)'이다. 트리는 자료를 정해진 방식에 따라서 분류하여 저장하기 때문에 시퀸스(일렬로)하게 자료를 저장하는 연결 리스트에 비해서 검색이 빠르지만 정해진 규칙에 따라서 자료를 삽입, 삭제 해야 되기 때문에 삽입과 삭제가 간단하지 않으며 구현이 복잡하다. 1. 정렬해야 한다. 2. 많은 자료를 저장하고, 검색이 빨라야 한다. 3. 빈번하게 삽입, 삭제하지 않는다.
/// 텍스쳐로부터 색상정보가 있는지 없는지 Bit단위로 가져온다. void CGuiControl::GetBitFromTexture( CGuiWindow* pWindow, BYTE *pbyBitInfo ) { if( pWindow ) { QTexture* pTexture = pWindow->GetTextureOrigin(); if( pTexture ) { LPDIRECT3DTEXTURE9 tex = pTexture->GetTexture(); int W=0, H=0, BS=0; if( false == QTexGetInfo( tex, W, H, BS ) ) return; /// 텍스처 가져올 부분의 메모리 유효성 검사 CGuiRect rtWindow; pWindow->GetWindowRect( rtWindow ); SImageInfo* pImage = pWindow->GetTextureOriginInfo(); int nTLeft = pImage->m_nPosX; int nTTop = pImage->m_nPosY; int nTRight = pImage->m_nPosX + pImage->m_nWidth; int nTBottom = pImage->m_nPosY + pImage->m_nHeight; int Level = 0; D3DLOCKED_RECT Rect; tex->LockRect( Level, &Rect, NULL, 0 ); BYTE* byColor = (BYTE*)Rect.pBits; tex->UnlockRect( Level ); ZeroMemory( pbyBitInfo, sizeof(BYTE) * (BS / 8) ); for( int i = 0; i < BS; i += 4 ) { /// 빨강색 있으면 if( byColor[i + 2] ) { if( byColor[i] == 0 && byColor[i + 1] == 0 ) { if( i % 32 > 0 ) pbyBitInfo[i/32] = pbyBitInfo[i/32] << 1; pbyBitInfo[i/32] = pbyBitInfo[i/32] | 0x01; /// 1채우기 } else { if( i % 32 > 0 ) pbyBitInfo[i/32] = pbyBitInfo[i/32] << 1; } } else { pbyBitInfo[i/32] = pbyBitInfo[i/32] << 1; /// 0채우기 } } } } } /// 비트 단위에 있는 그림 정보를 pWindow 에 그려 넣는다. void CGuiControl::SetTextureFromBit( CGuiWindow* pWindow, BYTE *pbyBitInfo ) { if( pWindow ) { QTexture* pTexture = pWindow->GetTextureOrigin(); if( pTexture ) { LPDIRECT3DTEXTURE9 tex = pTexture->GetTexture(); int W=0, H=0, BS=0; if( false == QTexGetInfo( tex, W, H, BS ) ) return; int nBufferSize = BS/32; int Level = 0; D3DLOCKED_RECT Rect; tex->LockRect( Level, &Rect, NULL, 0 ); DWORD* dwLoadColor = (DWORD*)Rect.pBits; for( int i = 0; i < nBufferSize; i++ ) { /// 첫번째 비트 정보 if( pbyBitInfo[i] & 0x80 ) dwLoadColor[i * 8] = D3DCOLOR_RGBA( 255, 0, 0, 255 ); else dwLoadColor[i * 8] = D3DCOLOR_RGBA( 255, 255, 255, 255 ); if( pbyBitInfo[i] & 0x40 ) dwLoadColor[i * 8 + 1] = D3DCOLOR_RGBA( 255, 0, 0, 255 ); else dwLoadColor[i * 8 + 1] = D3DCOLOR_RGBA( 255, 255, 255, 255 ); if( pbyBitInfo[i] & 0x20 ) dwLoadColor[i * 8 + 2] = D3DCOLOR_RGBA( 255, 0, 0, 255 ); else dwLoadColor[i * 8 + 2] = D3DCOLOR_RGBA( 255, 255, 255, 255 ); if( pbyBitInfo[i] & 0x10 ) dwLoadColor[i * 8 + 3] = D3DCOLOR_RGBA( 255, 0, 0, 255 ); else dwLoadColor[i * 8 + 3] = D3DCOLOR_RGBA( 255, 255, 255, 255 ); if( pbyBitInfo[i] & 0x08 ) dwLoadColor[i * 8 + 4] = D3DCOLOR_RGBA( 255, 0, 0, 255 ); else dwLoadColor[i * 8 + 4] = D3DCOLOR_RGBA( 255, 255, 255, 255 ); if( pbyBitInfo[i] & 0x04 ) dwLoadColor[i * 8 + 5] = D3DCOLOR_RGBA( 255, 0, 0, 255 ); else dwLoadColor[i * 8 + 5] = D3DCOLOR_RGBA( 255, 255, 255, 255 ); if( pbyBitInfo[i] & 0x02 ) dwLoadColor[i * 8 + 6] = D3DCOLOR_RGBA( 255, 0, 0, 255 ); else dwLoadColor[i * 8 + 6] = D3DCOLOR_RGBA( 255, 255, 255, 255 ); if( pbyBitInfo[i] & 0x01 ) dwLoadColor[i * 8 + 7] = D3DCOLOR_RGBA( 255, 0, 0, 255 ); else dwLoadColor[i * 8 + 7] = D3DCOLOR_RGBA( 255, 255, 255, 255 ); } tex->UnlockRect( Level ); } } }
1. 선그리기 함수 a. Bresenham 알고리즘 사용 b. (x1, y1) ~ (x2,y2)까지 선을 그린다. c. 어딘가에서 찾은 게임 소스보고 함... void line(int x1, int y1, int x2, int y2, unsigned char color) { int t, distance; int xerr=0, yerr=0, delta_x, delta_y; int incx, incy; delta_x=x2-x1; delta_y=y2-y1; incx=(delta_x>0)? 1 : ((delta_x==0)? 0 : -1 ); incy=(delta_y>0)? 1 : ((delta_y==0)? 0 : -1 ); delta_x=abs( delta_x ); delta_y=abs( delta_y ); distance=(delta_x > delta_y ) ? delta_x:delta_y; for(t=0;t<=distance;t++){ PutPixel(x1,y1,color); xerr+=delta_x; yerr+=delta_y; if(xerr>=distance ){ xerr-=distance; x1+=incx; } if(yerr>=distance){ yerr-=distance; y1+=incy; } } } 2. 원그리기 함수 a. (x, y)를 점에서 radius만한 원이나 타원을 그린다. b. flg=1 이면 타원 flg=0 이면 원 void plot_circle_points(int x_center,int y_center,int x,int y,char *addr,char flg) { framePutPixel(x_center+x, (y_center+y/flg), addr); framePutPixel(x_center-x, (y_center+y/flg), addr); framePutPixel(x_center+x, (y_center-y/flg), addr); framePutPixel(x_center-x, (y_center-y/flg), addr); framePutPixel(x_center+y, (y_center+x/flg), addr); framePutPixel(x_center-y, (y_center+x/flg), addr); framePutPixel(x_center+y, (y_center-x/flg), addr); framePutPixel(x_center-y, (y_center-x/flg), addr); } void circle(int x_center, int y_center, int radius,char *addr,char flg) { int x, y, p; x=0; y=radius; p=3-2*radius; while(x<y){ plot_circle_points(x_center, y_center, x, y, addr,flg); plot_circle_points(x_center+2, y_center, x, y, addr,flg); plot_circle_points(x_center-2, y_center, x, y, addr,flg); if(p<0) p=p+4*x+6; else { p=p+4*(x-y)+10; y=y-1; } x=x+1; } if(x==y) { plot_circle_points(x_center, y_center, x, y, addr,flg); } }
Refactoring -마틴파울러 리펙토링은 외부 동작을 바꾸지 않으면서 내부 구조를 개선하는 방법으로, 소프트웨어 시스템을 변경하는 프로세스이다. 이것은 버그가 끼어 들 가능성을 최소화하면서 코드를 정리하는 정형화된 방법이다. 본질적으로 우리가 리펙토링을 할때, 우리는 코드가 작성된 후에 코드의 디자인을 개선하는 것이다. "코드가 작성된 후에 디자인을 개선한다" 이상한 말이다. 현재 우리가 알고 있는 소프트웨어 개발에서 우리는 디자인을 한 다음 코딩을 하는 것이라 믿고 있다. 좋은 디자인이 먼저이고, 그 다음이 코딩이다. 그러나 시간이 지나면 코드는 수정될 것이고, 시스템 본래의 모습과 디자인을 따른 구조는 점점 사라질 것이다. 코드는 천천히 엔지니어링에서 해킹 수준으로 떨어질 것이다. 리펙토링은 이런 관례와 반대이다. 리펙토링을 사용하면 서투른 디자인을 취해 재작업하여 잘 디자인된 코드로 만들수 있다. 각각의 단계는 아주 간단하다. 필드 하나를 다른 클래스로 옮기고, 메소드에서 코드의 한 부분을 따로 다른메소드로 뽑아내고, 코드의 일부를 수퍼클래스나 서브 클래스로 옮긴다. 그러나 이런 작은 변화가 쌓여서 근본적으로 디자인을 개선하게 된다. 보통의 소프트웨어 쇠퇴 개념과는 정확하게 반대다. 리펙토링을 사용하면 작업의 밸런스가 바뀐다. 모든것을 미리 생각하기 보다는 개발을 하면서 지속적으로 좋은 디자인을 찾는다. 시스템을 구축하면서 어떻게 디자인을 개선할지에 대해 배운다. 이런 작업은 개발이 계속되어도 프로그램의 디자인을 개선할지에 대해 배운다. 이런 작업은 개발이 계속되어도 프로그램의 디자인이 계속 좋은 상태로 남아있게 한다.
출처 : http://blog.acronym.co.kr/33 리펙토링을 하려면 소스 코드 속의 나쁜 냄새를 맡을 수 있어야 한다고 합니다.
"리펙토링"이란 책에서 켄트 벡(Kent Beck)과 마틴 파울러(Martin Fowler)가 이야기한 코드 속의 나쁜 냄새에 대해 정리해 보려고 합니다.
중복된 코드 (Duplicated Code)- 동일한 소스가 여기 저기 사용된다면 반드시 리펙토링을 해야 한다고 합니다. Extract Method (136), Extract Class (179), Pull Up Method (370), Form template Method (393) 긴 메소드 (Long Method)- C 프로그래밍부터 시작해서인지 절차적 프로그래밍에 익숙한 경우, 하나의 메소드에서 모든 일을 처리하는 경우가 종종 있습니다. 긴 메소드는 쪼개야 한다고 하네요~ Extract Method (136), Replace Temp with Query (147), Replace method with Method Object (163), Decompose Conditional (276) 거대한 클래스 (Large Class)- 클래스가 너무 많은 일을 하면 변수도 많아지고, 중복된 코드도 많아질 가능성이 높다고 합니다. Extract Class (179), Extract Subclass (378), Extract Interface (389), Replace Data Value with Object (209) 긴 파라미터 리스트 (Long Parameter List)- 메소드에 전달되는 파라미터가 길면 소스를 이해하는 것이 어렵기 때문에 수정하는 것이 낫다고 합니다. Replace Parameter with Method (335), Introduce Parameter Object (339), Preserve Whole Object (331) 확산적 변경 (Divergent Change)- 말이 좀 어려운데요.. A라는 클래스를 "가" 이유 때문에 3개의 메소드를 수정해야 하고, 또 "나" 이유 때문에 4개의 메소드를 수정해야 한다면.. 차라리 "A가", "A나"와 같이 두개의 객체로 만드는 것이 바람직하다는 것인데요.. 대략적으로는 알겠는데 정말 확 와닿지는 않네요.. Extract Class (179) 산타총 수술 (Shotgun Sugery)- 역시 어려운 말입니다. -.- 자주 수정하는 부분인데.. 한번 수정할 때마다 여러개의 클래스를 수정해야 한다면 이를 하나의 클래스만 수정하도록 변경해야 한다는 겁니다. 요건 가끔 있을 것 같네요.. Move Method (170), Move Field (175), Inline Class (184) 기능에 대한 욕심 (Feature Envy)- 메소드가 자신이 속한 클래스보다 다른 클래스를 더 많이 활용하고 있다면, 옮겨야 겠죠.. Move Method (170), Move Field (175), Extract Method (136) 데이터 덩어리 (Data Clump)- 한 클래스와 관련없는 여려 데이터들이 모여있는 경우입니다. 각각의 역할에 맞는 여러개의 객체로 쪼개야 한다고 하네요.. Extract Class (179), Introduce Parameter Object (339), Preserve Whole Object (331) 기본 타입에 대한 강박관념 (Primitive Obsession)- 기본 타입만을 고집하지 말고 Money 클래스, Range 클래스, 전화번호 클래스, 우편번호 클래스등 특정한 작업을 위한 작은 객체를 충분히 활용하라고 합니다. Replace Data Value with Object (209), Extract Class (179), Introduce Parameter Object (339), Replace Array with Object (220), Replace Type Code with Class (255), Replace Type Code with Subclasses (261), Replace Type Code with State/Strategy (265) Switch 문 (Switch Statements)- Switch 문은 객체지향 코드에서는 다형성을 이용해 수정해 주어야 한다고 합니다. 기본적으로 Switch문이 중복성을 가지고 있기 때문이라고 하네요~ Replace Conditional with Polymorphism (293), Replace type with Subclasses (261), Replace Type Code with State/Strategy (265), Replace Parameter with Explicit Method (327), Introduce Null Object (298) 게으른 클래스 (Lazy Class)- 클래스가 충분한 일을 하지 않을 경우, 제거해야 한다는 겁니다. Inline Class (184), Collapse Hierarchy (392) 추측성 일반화 (Speculative Generality)- 앞으로 필요할 것 같아서 미리 추가한 기능과 관련된 코드중 실제로 사용되지 않는 것은 삭제하라는 거네요 Collapse Hierarchy (392), Inline Class (184), Remove Parameter (318), Rename Method (313) 임시 필드 (Temporary Field)- 객체 안의 인스턴스 변수가 특정 상황에서만 사용된다면, 이해하기 어렵게 되므로 변경해야 한다고 하네요. Extract Class (179), Introduce Null Object (298) 메시지 체인 (Message Chains)- 어떤 객체를 사용하기 위해 다른 객체에 물어보고, 다른 객체는 다시 또 다른 객체에 물어보고, 그 객체는 다시 다른 객체에 물어보고.. 이런 경우가 메시지 체인이라고 하네요~~ Hide Delegate (187) 미들 맨 (Middle Man)- 클래스 메소드의 대부분이 다른 클래스로 위임하고 있다면.. 지나친 캡슐화를 사용한고 있다는 것인데요.. 리펙토링을 적용해야 한다고 하네요~ Remove Middle Man (191), Inline Method (144), Replace Delegation with Inheritance (404) 부적절한 친밀 (Inappropriate Intimacy)- 지나치게 친밀한 클래스는 서로 갈라 놓아야 한다고 하네요.. Move Method (170), Move Field (175), Change Bidirectional Association to Unidirectional (236), Replace Inheritance with Delegation (401), Hide Delegate (187) 다른 인터페이스를 가진 대체 클래스 (Alternative Classes with Different Interface)- 같은 작업을 하지만 다른 시그너처를 가진 메소드에 대해서 리펙토링 하라는 거네요.. Rename Method (313), Move Method (170) 불완전한 라이브러리 클래스 (Incomplete Library Class)- 기존의 라이브러리 클래스가 가지고 있었으면 하는 메소드가 있다면 리펙토링을 통해 보완하라는 겁니다. Introduce Foreign Method (194), Introduce Local Extension (196) 데이터 클래스 (Data Class)- get / set 메소드만 가진 클래스.. 실제로 많이 만드는 데요~ 이런 경우 적절한 리펙토링을 하라는 거네요~ Move Method (170), Encapsulate Field (242), Encapsulate Collection (244) 거부된 유산 (Refused Bequest)- 상속구조가 잘못된 경우, 새로운 형제 클래스를 만들고 리펙토링해야 한다는 이야기 입니다. Replace Inheritance with Delegation (401) 주석 (Comments)- 주석을 써야 할 것 같다면 먼저 코드를 리펙토링 하여 주석이 불필요하도록 하라는 거네요.. Extract Method (136), Introduce Assertion (306) 리펙토링은 최적화의 개념이 아니라.. 소스 코드를 이해하기 쉽게 만드는 겁니다. 소스 코드 속의 나쁜 냄새를 잘 파악할 수 있도록 위의 내용을 참고하시기 바랍니다.
출처 : http://blog.acronym.co.kr/33 리펙토링을 실제로 어떻게 수행하는지.. 마틴 파울러(Martin Fowler)의 리펙토링 책에 나온 내용을 요약합니다.
여기에 정리한 내용은 인덱스 정도로 활용하시고.. 실제 리펙토링을 위한 예제나 자세한 설명은 책을 참고하시기 바랍니다.
1. 메소드 정리 (Composing Methods)
Extract Method (136) 그룹으로 함께 묶을 수 있는 코드 조각이 있으면, 코드의 목적이 잘 드러나도록 메소드의 이름을 지어 별도의 메소드로 뽑아낸다.
Inline Method (144) 메소드 몸체가 메소드의 이름 만큼이나 명확할 때는, 호출하는 곳에 메소드의 몸체를 넣고 메소드를 삭제하라
Inline Temp (146) 간단한 수식의 결과값을 가지는 임시변수가 있고, 그 임시변수가 다른 리펙토링을 하는데 방해가 된다면, 이 임시변수를 참조하는 부분을 모두 원래의 수식으로 바꾸라
Replace Temp with Query (147) 어떤 수식의 결과값을 저장하기 위해서 임시변수를 사용하고 있다면, 수식을 뽑아내서 메소드로 만들고 임시변수를 참조하는 곳을 찾아 모두 메소드 호출로 바꾼다. 새로 만든 메소드는 다른 메소드에서도 사용될 수 있다.
Introduce Explaining Variable (151) 복잡한 수식이 있는 경우에는, 수식의 결과나 또는 수식의 일부에 자신의 목적을 잘 설명하는 이름으로 된 임시변수를 사용하라.
Split Temporary Variable (155) 루프 안에 있는 변수나 collecting temporary variable도 아닌 임시변수에 값을 여러 번 대입하는 경우에는, 각각의 대입에 대해서 따로따로 임시변수를 만들어라.
Remove Assignments to Parameters (159) 파라미터에 값을 대입하는 코드가 있으면, 대신 임시변수를 사용하도록 하라.
Replace Method with Method Object (163) 긴 메소드가 있는데 지역변수 때문에 Extract Method를 적용할 수 없는 경우에는, 메소드를 그 자신을 위한 객체로 바꿔서 모든 지역변수가 그 객체의 필드가 되도록 한다. 이렇게 하면 메소드를 같은 객체 안의 여러 메소드로 분해할 수 있다.
Substitute Algorithm (167) 알고리즘을 보다 명확한 것으로 바꾸고 싶을 때는, 메소드의 몸체를 새로운 알고리즘으로 바꾼다.
2. 객체간의 기능 이동
Move Method (170) 메소드가 자신이 정의된 클래스보다 다른 클래스의 기능을 더 많이 사용하고 있다면, 이 메소드를 가장 많이 사용하고 있는 클래스에 비슷한 몸체를 가진 새로운 메소드를 만들어라. 그리고 이전 메소드는 간단한 위임으로 바꾸거나 완전히 제거하라.
Move Field (175) 필드가 자신이 정의된 클래스보다 다른 클래스에 의해서 더 많이 사용되고 있다면, 타켓 클래스에 새로운 필드를 만들고 기존 필드를 사용하고 있는 모든 부분을 변경하라.
Extract Class (179) 두 개의 클래스가 해야 할 일을 하나의 클래스가 하고 있는 경우, 새로운 클래스를 만들어서 관련 있는 필드와 메소드를 예전 클래스에서 새로운 클래스로 옮겨라.
Inline Class (184) 클래스가 하는 일이 많지 않은 경우에는, 그 클래스에 있는 모든 변수와 메소드를 다른 클래스로 옮기고 그 클래스를 제거하라.
Hide Delegate (187) 클래스가 객체의 위임 클래스를 직접 호출하고 있는 경우, 서버에 메소드를 만들어서 대리객체를 숨겨라.
Remove Middle Man (191) 클래스가 간단한 위임을 너무 많이 하고 있는 경우에는, 클라이언트가 대리객체를 직접 호출하도록 하라.
Introduce Foreign Method (194) 사용하고 있는 서버 클래스에 부가적인 메소드가 필요하지만 클래스를 수정할 수 없는 경우에는, 첫 번째 인자로 서버 클래스의 인스턴스를 받는 메소드를 클라이언트에 만들어라.
Introduce Local Extension (196) 사용하고 있는 서버 클래스에 여러 개의 메소드를 추가할 필요가 있지만 서버 클래스를 수정할 수 없는 경우, 필요한 추가 메소드를 포함하는 새로운 클래스를 만들어라. 이 확장 클래스를 원래 클래스의 서브클래스 또는 래퍼 클래스로 만들어라.
3. 데이터 구성 (Organizing Data)
Self Encapsulate Field (205) 필드에 직접 접근하고 있는 필드에 대한 결합이 이상해지면, 그 필드에 대한 get/set 메소드를 만들고 항상 이 메소드를 사용하여 필드에 접근하라.
Replace Data Value with Object (209) 추가적인 데이터나 동작을 필요로 하는 데이터 아이템이 있을 때는, 데이터 아이템을 객체로 바꾸어라.
Change Value to Reference (213) 동일한 인스턴스를 여러 개 가지고 있는 클래스가 있고 여러 개의 동일한 인스턴스를 하나의 객체로 바꾸고 싶으면, 그 객체를 참조 객체로 바꾸어라.
Change Reference to Value (217) 직고, 불변성(immutable)이고 관리하기 어려운 참조 객체가 있는 경우, 그것을 값 객체로 바꾸어라.
Replace Array with Object (220) 배열의 특정 요소가 다른 뜻을 가지고 있다면, 배열을 각각의 요소에 대한 필드를 가지는 객체로 바꿔라.
Duplicate Observed Data (224) GUI 컨트롤에서만 사용 가능한 도메인 데이터가 있고 도메인 메소드에서 접근이 필요한 경우, 그 데이터를 도메인 객체로 복사하고 옵저버를 두어 두 데이터를 동기화하라.
Change Unidirectional Association to Bidirectional (232) 각각 서로의 기능을 필요로 하는 클래스가 있는데, 링크가 한쪽 방향으로만 되어 있는 경우, 반대 방향으로 포인터를 추가하고 수정자(modifier)가 양쪽 세트를 모두 업데이트 하게 변경하라.
Change Bidirectional Association to Unidirectional (236) 서로 링크를 가지는 두 개의 클래스에서 한 쪽이 다른 한쪽을 더 이상 필요로 하지 않을 때는 불필요한 링크를 제거하라.
Replace Magic Number with Symbolic Constant (240) 특별한 의미를 가지는 숫자 리터럴이 있으면, 상수를 만들고 의미를 잘 나타내도록 이름을 지은 다음 숫자를 상수로 바꾸어라.
Encapsulate Field (242) public 필드가 있는 경우, 그 필드를 private으로 만들고 접근자를 제공하라.
Encapsulate Collection (244) 컬렉션(collection)을 리턴하는 메소드가 있으면, 그 메소드가 읽기 전용 뷰를 리턴하도록 만들고 add/remove 메소드를 제공하라.
Replace Record with Data Class (254) 전통적인 프로그래밍 환경에서 레코드 구조에 대한 인터페이스가 필요한 경우, 그 레코드를 위한 데이터 객체를 만들어라.
Replace Type Code with Class (255) 클래스의 동작에 영향을 미치지 않는 숫자로 된 타입 코드가 있으면 숫자를 클래스로 바꾸어라.
Replace Type Code with Subclass (261) 클래스의 동작에 영향을 미치는 변경 불가능하는 타입코드가 있다면, 타입 코드를 서브클래스로 바꾸어라.
Replace Type Code with State/Strategy (265) 클래스의 동작에 영향을 미치는 타입 코드가 있지만 서브클래싱을 할 수 없을 때는 타입 코드를 스테이트(state) 객체로 바꾸어라.
Replace Subclass with Fields (270) 상수 데이터를 리턴하는 메소드만 다른 서브클래스가 있으면, 그 메소드를 수퍼클래스의 필드로 바꾸고 서브클래스를 제거하라.
4. 조건문의 단순화
Decompos Conditional (276) 복잡한 조건문 (if-then-else)이 있는 경우, 조건 then 부분 그리고 else 부분에서 메소드를 추출하라.
Consolidate Conditional Expression (278) 같은 결과를 초래하는 일련의 조건 테스트가 있는 경우, 그것을 하나의 조건식으로 결합하여 뽑아라.
Consolidate Duplicate Conditional Fragments (281) 동일한 코드 조각이 조건문의 모든 분기 안에 있는 경우, 동일한 코드를 조건문 밖으로 옮겨라.
Remove Control Flag (283) 일련의 boolean 식에서 컨트롤 플래그 역할을하는 변수가 있는 경우, break 또는 return을 대신 사용하라.
Replace Nested Conditional with Guard Clauses (288) 메소드가 정상적인 실행 경로를 불명확하게 하는 조건 동작을 가지고 있는 경우, 모든 특별한 경우에 대해서 보호절을 사용하라.
Replace Conditional with Polymorphism (293) 객체의 타입에 따라 다른 동작을 선택하는 조건문을 가지고 있는 경우, 조건문의 각 부분을 서브클래스에 있는 오버라이딩 메소드로 옮겨라. 그리고 원래 메소드를 abstract로 만들어라.
Introduce Null Object (298) null 체크를 반복적으로 하고 있다면, null 값을 null 객체로 대체하라.
Introduce Assertion (306) 코드의 한 부분이 프로그램의 상태에 대하여 어떤 것을 가정하고 있으면, assertion을 써서 가정을 명시되게 만들어라.
5. 메소드 호출의 단순화
Rename Method (313) 메소드의 이름이 그 목적을 드러내지 못하고 있다면, 메소드의 이름을 바꿔라.
Add Parameter (316) 어떤 메소드가 그 메소드를 호출하는 부분에서 더 많은 정보를 필요로 한다면, 이 정보를 넘길 수 있는 객체에 대한 파라미터를 추가하라.
Remove Parameter (318) 파라미터가 메소드 몸체에서 더 이상 사용되지 않는다면, 그 파라미터를 제거하라.
Separate Query from Modifier (320) 값을 리턴할 뿐만 아니라 객체의 상태도 변경하는 메소드를 가지고 있는 경우, 두 개의 메소드를 만들어서 하나는 값을 리턴하는 역할을 하고, 하나는 객체의 상태를 변경하는 역할을 하게 하라.
Parameterize Method (325) 몇몇 메소드가 메소드 몸체에 다른 값을 포함하고 있는 것을 제외하고는 비슷한 일을 하고 있다면, 다른 값을 파라미터로 넘겨 받는 하나의 메소드를 만들어라.
Replace Parameter with Explicit Methods (327) 파라미터의 값에 따라서 다른 코드를 실행하는 메소드가 있다면, 각각의 파라미터 값에 대한 별도의 메소드를 만들어라.
Preserve Whole Object (331) 어떤 객체에서 여러 개의 값을 얻은 다음 메소드를 호출하면서 파라미터로 넘기고 있다면, 대신 그 객체를 파라미터로 넘겨라.
Replace Parameter with Method (335) 객체가 메소드를 호출한 다음, 결과를 다른 메소드에 대한 파라미터로 넘기고 있다. 수신자 또한 이 메소드를 호출할 수 있다면 그 파라미터를 제거하고 수신자가 그 메소드를 호출하도록 하라.
Introduce Parameter Object (339) 자연스럽게 몰려다니는 파라미터 그룹을 가지고 있다면, 그것들을 객체로 바꾸어라.
Remove Setting Method (344) 어떤 필드가 객체 생성시에 값이 정해지고 그 이후에는 변경되지 않아야 한다면, 그 필드 값을 설정하는 모든 메소드를 제거하라.
Hide Method (348) 메소드가 다른 클래스에서 사용되지 않는다면, 그 메소드를 private로 만들어라.
Replace Constructor with Factory Method (350) 객체를 생성할 때 단순히 생성하는 것 이외에 다른 작업도 하고 있다면, 생성자를 팩토리 메소드로 대체하라.
Encapsulate Downcast (355) 메소드가 그 호출부에서 다운캐스트 될 필요가 있는 객체를 리턴하고 있다면 다운캐스트 하는 것을 메소드 안으로 옮겨라.
Replace Error Code with Exception (357) 메소드가 에러를 나타내는 특별한 코드를 가지고 있다면, 대신 예외를 던져라.
Replace Exception with Test (363) 호출부에서 먼저 검사할 수 있는 조건에 대해 예외를 던지고 있다면, 호출부가 먼저 검사하도록 바꿔라.
6. 일반화 다루기
Pull Up Field (368) 두 서브클래스가 동일한 필드를 가지고 있다면, 그 필드를 수퍼클래스로 옮겨라.
Pull Up Method (370) 동일한 일을 하는 메소드를 여러 서브클래스에서 가지고 있다면, 이 메소드를 수퍼클래스로 옮겨라.
Pull Up Constructor Body (373) 서브크래스들이 대부분 동일한 몸체를 가진 생성자를 가지고 있다면, 수퍼클래스에 생성자를 만들고 서브클래스 메소드에서 이것을 호출하라.
Push Down Method (376) 수퍼클래스에 있는 동작이 서브클래스 중 일부에만 관련되어 있다면, 그 동작을 관련된 서브클래스로 옮겨라.
Push Down Field (377) 어떤 필드가 일부 서브클래스에 의해서만 사용되고 있다면, 그 필드를 관련된 서브클래스로 옮겨라.
Extract Subclass (378) 어떤 클래스가 일부 인스턴스에 의해서만 사용되는 기능을 가지고 있다면, 기능의 부분집합을 담당하는 서브클래스를 만들어라.
Extract Superclass (384) 비슷한 메소드와 필드를 가진 두 개의 클래스가 있다면, 수퍼클래스를 만들어서 공통된 메소드와 필드를 수퍼클래스로 옮겨라.
Extract Interface (389) 여러 클라이언트가 한 클래스 인터페이스와 동일한 부분 집합을 사용하고 있거나, 두 클래스가 공통된 인터페이스를 가지는 부분이 있다면, 그 부분 집합을 인터페이스로 뽑아내라.
Collapse Hierarchy (392) 수퍼클래스와 서브클래스가 별로 다르지 않다면, 그것들을 하나로 합쳐라.
Form Template Method (393) 각각의 서브클래스에 동일한 순서로 비슷한 단계를 행하지만 단계가 완전히 같지는 않은 두 메소드가 있다면, 그 단계를 동일한 시그니처를 가진 메소드로 만들어라. 이렇게 하면 원래의 두 메소드는 서로 같아지므로, 수퍼클래스로 올릴 수 있다.
Replace Inheritance with Delegation (401) 서브클래스가 수퍼클래스 인터페이스의 일부분만 사용하거나 또는 데이터를 상속 받고 싶지 않은 경우, 수퍼클래스를 위한 필드를 만들고 메소드들이 수퍼클래스에 위임하도록 변경한 후 상속 관계를 제거한다.
Replace Delegation with Inheritance (404) 위임을 사용하고 있는데 전체 인터페이스에 대해 간단한 위임을 자주 작성하고 있다면, 위임하는 클래스를 대리객체의 서브클래스로 만들어라.
|

by 사랑합니다
|
|