초록
              좋은 설계와 개발 과정에 대한 많은 노력이 기울여져
              왔다. 불행히도 이러한 노력은 프로젝트가 아무 것도 없
              는 단계에서 시작하는 것을 간주했다.

              하지만 대부분의 프로젝트는 레거시 코드를 수반한다.
              레거시 코드는 신규 코드보다 100:1의 비율로 더 많다.
              신규 코드로 작업하는 것 보다 레거시 코드로 작업하는
              것은 더 많은 노력을 요한다.

              이 발표에서는 레거시 코드에 기능을 추가, 수정하는 전
              략과 몇가지 리팩토링, TDD 기법을 사례 중심으로 알아
              보도록 한다.


아무리 좋은 설계로 시작한 프로젝트라도 테스트가 없으면 변경이 두려워 리팩토링, 개선을 못하게 되고 점진적으로는 코드가 부폐하게 된다.
아무리 나쁜 설계로 시작한 프로젝트라도 포괄적인 테스트가 있다면 변경에 대한 두려움이 없어서 점진적으로 리팩토링, 개선을 하여 좋은 구조를 갖게된다.
효과적으로
레거시 코드 다루기
  서비스플랫폼개발팀
     백명석
Contents


               The Way We Work

               The Way We Have To Do

               Working Effectively with Legacy Code

               Case Studies




우리가   어떻게 일해 왔는지,
우리가   일해야 하는지,
그리고   테스트가 없는 레거시 코드를 효과적으로 다루는 전략과
사례에   대해서 말씀드리겠습니다.
The Way We Work




                     “Quick & Dirty” ➜ “Slow & Dirty”

그린필드 프로젝트라고 불리는 신규 프로젝트는 3개월 정도의 짧은 시간에 개발됩니다. 시간이 없이니 테스트, 리팩토링 없이 개발합니다. 사전 작업에 대한 제약이 없으므로
이때는 아무런 문제가 없습니다.
서비스가 런칭되면 대개 3년 이상 운영됩니다. 신규 요구사항은 넘치고, 버그 수정에 대부분의 시간을 보내게됩니다. 테스트가 없는 dirty 코드는 테스트, 리팩토링이 어려워
많은 비용을 유발하고, 기존 기능에 버그를 유발할 수도 있기에 Quick & dirty는 결국 slow & dirty됨
The Way We Have To Do



              We Already Have Dirty Working Code

              Work Effectively with Legacy Code




그럼 어떻게 해야 할까요 ? 이미 동작하고 있는 지저분한 코드가 있는데.
우리는 테스트가 없는 레거시 코드를 효과적으로 다룰 수 있어야 합니다.
Working Effectively with
             Legacy Code

             Wait Until A New Feature is Needed

                 Change a little bit of design

                 Add few tests in the affected area

                 Eventually spread the test throughout the
                 legacy code



레거시에 코드를 다루는 기본적인 전략은 레거시에 테스트 추가하기 위해 특공대를 투입해서 거대한 새로운 프로젝트를 하는 것이 아니라 “새로운 기능이 필요할
때까지 기다리는 것입니다”
그리고 설계를 조금씩 개선하고, 영향받는 코드들에 대한 테스트를 조금씩 추가해서 최종적으로는 레거시 코드 전체에 테스트를 확산시키는 것입니다.
One More:
                              Automation Tools




또 하나의 전략으로 자동화 툴이 있습니다.
MoreUnit, EclEmma와 같은 eclipse plugin,
eclipse의 hot fix, content assist, refactoring와 같은 자동화 기능
mockito와 같은 유닛 테스트 작성을 위한 라이브러리
등을 활용하는 것이 도움이 됩니다.
Case Studies


             Sprout Method

             Characterization Test

             Subclass and Override Method

             Extract Method Object & Extract Method




이제부터는 레거시 코드 활용의 사례에 대해서 말씀드리겠습니다.
Have to Change
              But Not Enough Time




              Not Enough Time to Break Dependency and
              Add Tests



기존 코드에 새로운 기능을 추가해야 하는데 의존성을 제거하고 테스트를 추가하기에는 시간이 너무 부족한 경우를 생각해 보겠습니다.
필요한 새로운 메소드를 기존 클래스에 10분이면 추가할 수 있는데, 이 클래스에 대한 테스트를 만드려면 2시간은 걸릴 것 같은 상황이 있을 수 있습니다.
Sprout Method

              You Need to Add a Feature to a System

                  Can be formulated completely as new code

                  Write the code in a new method

                  Call it where the new functionality is
                  supposed to be



시스템에 새로운 기능을 추가해야 하는데 새로운 기능이 완전히 분리된 새로운 메소드로 구현할 수 있다면
새로운 기능을 새로운 메소드로 작성하고 새로운 메소드를 해당 기능이 필요한 기존 코드에서 호출하는 방법이 sprout method의 개념입니다.
Steps

                 Identify where you need to make your code
                 change.

                 Write down a call for a new method then
                 comment it out.

                 Develop the sprout method using TDD

                 Remove the comment to enable the call.



1.   변경이 필요한 곳을 식별
2.   새로운 메소드 호출을 추가하고, 커멘트 처리
3.   새로운 메소드를 TDD로 구현
4.   커멘트를 제거하여 메소드 호출이 일어나도록 한다.
Sprout Method Example


                Add Synchronized Token Pattern to
                EditArticleController

                     for prohibiting abusing

                     compare session token and request token




게시글 수정 컨트롤러에 어뷰징 방지를 위해 synchronized token pattern을 적용하는 예제
Need to Make Change
         But Don’t Know What Tests to Write




이와 같이 복잡한 상속구조를 가지고 있는 SearchEngineCreateArticleRequestFactory라는 클래스를 변경해야 하는데 어떻게 테스트를 작성할 지 모르는 경우
를 생각보겠습니다.
Need to Make Change
        But Don’t Know What Tests to Write



               Need to Know What the S/W Supposed To Do

               Write Tests Based on Those Ideas

               Dig up Old Requirements Documents and
               Project Memos ???




- 변경을 가하기 전에 시스템이 어떻게 동작하도록 되어 있는지 알아내야 할 필요가 있다.
- 이러한 요구사항에 기반하여 테스트를 작성할 수 있다.
- 어떻게 해야 하나 ? 오래된 요구 사항 문서를 조사해야 하나 ? 프로젝트 메모들을 뒤져야 하나 ?
Characterization Test

             What the systems does is more important
             than what it is supposed to do.

             We want preserve behavior

             Test that characterizes the actual behavior
             of a piece of code.




- 시스템이 어떻게 동작하기로 약속되었는지 보다 실제로 어떻게 동작하는지가 훨씬 중요하다.
- 비록 코드에 잘못이 있더라도 그 행위를 보존하기를 원합니다.
- 코드가 실제로 어떤 행동을 하는지를 나타낼 수 있는 테스트를 작성하는 것.
이러한 기법을 Characterization Test라고 한다.
Steps


               Write an assertion that you know will fail

               Let the failure tell you what the behavior is

               Change the test so that it expects the
               behavior that the code produces




절차
1. 실패할 것이라고 알고 있는 assert 문장을 작성한다.
2. 이 실패를 통해 실제 행위가 무엇인지 발견한다.
3. 코드가 생산하는 행위를 기대하도록 테스트를 변경한다.
Characterization Test
                   Example




XML 요청을 만드는 팩토리에 대한 테스트
이 테스트는 다소 복잡해서 설명의 편의를 위해 미리 슈퍼 클래스에 복잡한 부분을 구현해 놓았습니다.
Hard to Add Test
         Because of Unwanted Dependency




static funciton등과 같은 원치 않는 의존성으로 인해 테스트를 작성하기 어려운 경우가 있습니다.
Subclass and Override
                     Method
                 A core technique for breaking dependencies
                 in OOP

                 Use inheritance in the context of a test

                      to nullify behavior that you don’t care
                      about

                      to get access to behavior that you do care
                      about


subclass and override method는 OOP에서 의존성 제거를 위한 핵심 기능이다.
테스트에서 subclassing을 아래와 같은 목적으로 사용한다.
1. 원치 않는 행위를 무효화하기 위해
2. 관심있는 행위에 접근하기 위해
Steps

                Identify the dependencies that you want to
                separate

                Make each method overridable(adjust the
                visibility of the methods)

                Create a subclass that overrides the
                methods.



분리하고 하는 의존성을 식별한다.
메소드의 가시성을 protected 등으로 변경하여 메소드가 override 가능하도록 한다.
해당 메소드들을 override할 서브클래스를 생성한다.
Subclass and Override
              Method Example




static 함수는 powermock을 사용해서 mocking이 가능하다.
하지만 static 함수 자체가 싱글톤과 마찬가지로 전역변수를 사용하는 것과 같은 잘못된 습관이다.
그러므로 Static 함수로 인한 테스트 어려움을 제거하는 방법을 알아보자.
Big Function




쉽게 이해하기 어려운 큰 함수를 개선하는 경우를 생각해 보자.
Big function hides
                        classes




큰 함수는 (인자, 로컬 변수로 표현되는 일련의 변수들)과 (이 변수들에 동작하는 들여쓰기로 구분되는 기능들)을 가지고 있다.
클래스 = (일련의 변수(필드)들_ + (이 변수들에 동작하는 함수들)의 집합이다.
그리므로 큰 함수는 클래스로 추출되어야 한다.
Extract Method Object
                    Steps

                        Extract Method(invoke)

                        Create New Inner Class

                        Convert Arg. to Field

                        Convert Local Var. to Field

                        Move Initialization Logic to constructor


1.   큰 메소드를 invoke라는 이름의 메소드로 추출한다.
2.   invoke 메소드를 갖는 inner 클래스를 생성한다. - 함수의 인자는 클래스의 생성자에 인자로 전달하고, invoke 메소드에서는 인자를 제거합니다.
3.   생성자에 전달된 인자를 필드로 변경한다.
4.   함수의 로컬 변수를 필드로 변경한다. 이러한 변경은 클래스 내부에서 메소드 호출시 불필요한 인자를 없애준다.
5.   변수들의 initialization 로직은 constructor로 이동한다.
Extract Method Object
                                                                        Parameterize
                 Steps                                                   Differences
                     Extract Methods Till You Drop

                     Move Type to New File

                     Rename class / method

                     Method Ordering(Top-Down, To-paragraph)



6. 이제 클래스에 더 이상 extract할 메소드가 없을 때 까지 extract method를 수행한다. 이때 반복적으로 나타나는 중복 문장들에서 약간씩 다른 점들이 존재한다면 이를 파
라미터로 처리합니다.
7. inner 클래스를 일반 클래스로 변경
8. 클래스 이름과 메소드 이름을 의미있게 변경한다.
9. 메소드 순서를 변경해서 top-down(to paragraph)으로 읽기 쉽게 변경한다.
Extract Method Object
                   Example


             Fitness




이 예제에서는 큰 함수를 extract method object 기법을 통해 새로운 클래스로 추출하고,
extract method를 수행하는 예를 보이도록 하겠다.
Usage of
           Extract Method Object




Extract Method Object는 TDD에서도 유용하게 사용될 수 있다. 이 예제는 켄트벡이 TDD 스크린 캐스트에서 사용한 예제인데, key-value 스토어의 일종이
tyrant의 클라이언트를 만드는 예제이다.
1. 이와 같이 클라이언트를 생성하고, put하고 get하여 동일한 값인지 확인하는 테스트로 시작한다. 이 기능을 구현하려면 많은 시간이 소요된다. 켄트벡 자신이
2시간 가량 걸릴 것이라고 했으니.
Usage of
          Extract Method Object




이 보다는 본래의 의도는 커멘트 처리하고 소켓을 생성해서 서버에 연결이 되는지 부터 확인하는 것이 쉽다.
디버깅 필요 없이 바로 실행이 되니
Usage of
          Extract Method Object




이렇게 테스트 코드에서 실제 구현을 다 해보는 것이다. 바로 수행해 볼 수 있으니 매우 편리하고 스피디하게 진행된다.
Usage of
             Extract Method Object




기능이 다 구현되면 extract method object 기법을 이용해서 production class로 추출한다.
Usage of
          Extract Method Object




약간의 리팩토리을 통해 상수를 제거하고, open, close, get 메소드를 추출하면 이와 같이 된다.
테스트에서 시작해서 production code을 추출했기 때문에 대한 테스트가 자연적으로 생기는 것을 알 수 있다.
항상 테스트부터 시작함으로써 테스트가 없는 레거시를 만들지 않고 항상 설계 개선을 위해 리팩토링을 할 수 있는 구조가 된다.
Quick & Dirty is OK

                      If you had tests

                      It’ll remove Fear to clean it




TDD

  debugging time / low level documentation / decoupling
Reference


Working Effectively with Legacy Code

Clean Coders Code Cast by Uncle Bob

TDD Code Cast by Kent beck

토비의 스프링 3
Q&A



             msbaek@daumcorp.com

             http://twitter.com/ctemplate




. Grady Booch: Clean code should read like well written prose.
. Martin Fowler: 어떤 바보도 컴퓨터가 이해할 수 있는 코드를 작성할 수 있다. 하지만 사람이 이해할 수 있는 코드를 작성하는 것은 좋은 프로그래머를 요한다.
End
Etc.


              Adding New Feature

                  TDD / Programming by Difference

              Reuse What

              Study Test




Programming by Difference
  OO에서 새로운 기능을 추가하는 방법 중 하나
  기존 클래스를 직접 수정하지 않고 새로운 기능을 추가하기 위해 상속을 사용
  새로운 기능을 추가한 후에 어떻게 기능이 통합되어지길 원하는지 정확히 파악할 수 있다.
  상속을 후에 위임으로 변경 가능
상속의 문제점
  다중 상속 불가(2개 이상의 클래스에 구현된 기능을 상속을 통해 사용할 수 없다).
  상위 클래스의 기능을 하위 클래스에서 재사용(???) 한다면 상위 클래스 변경시 많은 하위 클래스의 변경이 유발됨
상위 클래스에 로직을 구성하고 변경 가능한 부분은 인터페이스로 정의하고 필요에 따라 구현 클래스를 추가해야함.
객체지향은 새로운 타입 추가에 열려있음. 새로운 함수 추가가 아니라.
우리가 재사용하고자 하는 것은 상위 레벨의 로직이지 하위 레벨의 API가 아님

    Independent Deployable(Development)

    Layered Architecture
Don’t Start Big Project

                 New Project for Refactoring or Testing

                     Everything is fine at start

                     Doing New design

                     Cause Double-duty

                     Impossible to replace the old system



-   처음엔 좋다. 모든 것을 원하는 대로 할 수 있다.
-   좋은 디자인을 위해서도 시간을 좀 보낸다.
-   기존 시스템에 버그 수정, 기능 추가 요청이 들어온다. PM이 신 시스템이 나올때까지 요구사항 반영을 미뤄보려 하지만 고객은 기다리지 않는다.
-   신규 프로젝트 팀은 기존 기능을 개선해서 구현하는 것 외에 새로운 요구사항도 구현해야 하는 이중고에 시달린다.
-   더 이상 신규 프로젝트를 완료해서 신 시스템이 기존 시스템을 교체할 수 있다고 믿지 못한다.
-   이 접근법은 항상 실패한다.
Wait Until New Feature
      is Needed
 Change little bit of design

 Add a few tests in the affected area

 Eventually spread the test through out the
 legacy code

 Not all but important/vital bits are get
 tested
Always Prefer Test First


 For all new code you write

   write test first

 Option either modify or add a new module

   write a new module with TFD
Have to Understand but
           Function is Too Big

              Function Size(Screenful)

              Function Should Do One Thing

                  Extract Major Sections into Function

                  Extract Different levels of abstract




함수 내에서 주요 섹션을 새로운 함수로 추출하는 것은 쉽다.
하지만 추상화 수준이 다른 것을 모두 추출하는 것은 모호하다.
Extract Different Levels
                of Abstraction


                 Extract Till You Drop

                      Until simply restates the code without
                      changing abstraction level

                      Most basic refactoring technique

                      Safe with tool support

- 이 코드가 한가지 일을 하는 것인가 ? 세가지 일을 하는 것인가 ? 이 코드를 한가지로 줄인다면
includeSetupsAndTeardownsIfTestPage로 extract하면 추상화 수준의 변경 없이 단순히 코드를 재인용하는 것 밖에는 안된다. 이런 경우는 더 이상 extract할
수 없는 것이다.
- 리팩토링의 가장 기본 기법
- 이클립스와 같은 툴의 지원으로 테스트 없이도 안전하게 수행할 수 있음.
As a Result
Composed Method Pattern

  To Paragraphs

All class’s methods are less than 4 lines

{} in if, while stmt removed

Comment removed

Easy to read in top-down way

Devon 2011-b-5 효과적인 레거시 코드 다루기

  • 1.
    초록 좋은 설계와 개발 과정에 대한 많은 노력이 기울여져 왔다. 불행히도 이러한 노력은 프로젝트가 아무 것도 없 는 단계에서 시작하는 것을 간주했다. 하지만 대부분의 프로젝트는 레거시 코드를 수반한다. 레거시 코드는 신규 코드보다 100:1의 비율로 더 많다. 신규 코드로 작업하는 것 보다 레거시 코드로 작업하는 것은 더 많은 노력을 요한다. 이 발표에서는 레거시 코드에 기능을 추가, 수정하는 전 략과 몇가지 리팩토링, TDD 기법을 사례 중심으로 알아 보도록 한다. 아무리 좋은 설계로 시작한 프로젝트라도 테스트가 없으면 변경이 두려워 리팩토링, 개선을 못하게 되고 점진적으로는 코드가 부폐하게 된다. 아무리 나쁜 설계로 시작한 프로젝트라도 포괄적인 테스트가 있다면 변경에 대한 두려움이 없어서 점진적으로 리팩토링, 개선을 하여 좋은 구조를 갖게된다.
  • 2.
    효과적으로 레거시 코드 다루기 서비스플랫폼개발팀 백명석
  • 3.
    Contents The Way We Work The Way We Have To Do Working Effectively with Legacy Code Case Studies 우리가 어떻게 일해 왔는지, 우리가 일해야 하는지, 그리고 테스트가 없는 레거시 코드를 효과적으로 다루는 전략과 사례에 대해서 말씀드리겠습니다.
  • 4.
    The Way WeWork “Quick & Dirty” ➜ “Slow & Dirty” 그린필드 프로젝트라고 불리는 신규 프로젝트는 3개월 정도의 짧은 시간에 개발됩니다. 시간이 없이니 테스트, 리팩토링 없이 개발합니다. 사전 작업에 대한 제약이 없으므로 이때는 아무런 문제가 없습니다. 서비스가 런칭되면 대개 3년 이상 운영됩니다. 신규 요구사항은 넘치고, 버그 수정에 대부분의 시간을 보내게됩니다. 테스트가 없는 dirty 코드는 테스트, 리팩토링이 어려워 많은 비용을 유발하고, 기존 기능에 버그를 유발할 수도 있기에 Quick & dirty는 결국 slow & dirty됨
  • 5.
    The Way WeHave To Do We Already Have Dirty Working Code Work Effectively with Legacy Code 그럼 어떻게 해야 할까요 ? 이미 동작하고 있는 지저분한 코드가 있는데. 우리는 테스트가 없는 레거시 코드를 효과적으로 다룰 수 있어야 합니다.
  • 6.
    Working Effectively with Legacy Code Wait Until A New Feature is Needed Change a little bit of design Add few tests in the affected area Eventually spread the test throughout the legacy code 레거시에 코드를 다루는 기본적인 전략은 레거시에 테스트 추가하기 위해 특공대를 투입해서 거대한 새로운 프로젝트를 하는 것이 아니라 “새로운 기능이 필요할 때까지 기다리는 것입니다” 그리고 설계를 조금씩 개선하고, 영향받는 코드들에 대한 테스트를 조금씩 추가해서 최종적으로는 레거시 코드 전체에 테스트를 확산시키는 것입니다.
  • 7.
    One More: Automation Tools 또 하나의 전략으로 자동화 툴이 있습니다. MoreUnit, EclEmma와 같은 eclipse plugin, eclipse의 hot fix, content assist, refactoring와 같은 자동화 기능 mockito와 같은 유닛 테스트 작성을 위한 라이브러리 등을 활용하는 것이 도움이 됩니다.
  • 8.
    Case Studies Sprout Method Characterization Test Subclass and Override Method Extract Method Object & Extract Method 이제부터는 레거시 코드 활용의 사례에 대해서 말씀드리겠습니다.
  • 9.
    Have to Change But Not Enough Time Not Enough Time to Break Dependency and Add Tests 기존 코드에 새로운 기능을 추가해야 하는데 의존성을 제거하고 테스트를 추가하기에는 시간이 너무 부족한 경우를 생각해 보겠습니다. 필요한 새로운 메소드를 기존 클래스에 10분이면 추가할 수 있는데, 이 클래스에 대한 테스트를 만드려면 2시간은 걸릴 것 같은 상황이 있을 수 있습니다.
  • 10.
    Sprout Method You Need to Add a Feature to a System Can be formulated completely as new code Write the code in a new method Call it where the new functionality is supposed to be 시스템에 새로운 기능을 추가해야 하는데 새로운 기능이 완전히 분리된 새로운 메소드로 구현할 수 있다면 새로운 기능을 새로운 메소드로 작성하고 새로운 메소드를 해당 기능이 필요한 기존 코드에서 호출하는 방법이 sprout method의 개념입니다.
  • 11.
    Steps Identify where you need to make your code change. Write down a call for a new method then comment it out. Develop the sprout method using TDD Remove the comment to enable the call. 1. 변경이 필요한 곳을 식별 2. 새로운 메소드 호출을 추가하고, 커멘트 처리 3. 새로운 메소드를 TDD로 구현 4. 커멘트를 제거하여 메소드 호출이 일어나도록 한다.
  • 12.
    Sprout Method Example Add Synchronized Token Pattern to EditArticleController for prohibiting abusing compare session token and request token 게시글 수정 컨트롤러에 어뷰징 방지를 위해 synchronized token pattern을 적용하는 예제
  • 13.
    Need to MakeChange But Don’t Know What Tests to Write 이와 같이 복잡한 상속구조를 가지고 있는 SearchEngineCreateArticleRequestFactory라는 클래스를 변경해야 하는데 어떻게 테스트를 작성할 지 모르는 경우 를 생각보겠습니다.
  • 14.
    Need to MakeChange But Don’t Know What Tests to Write Need to Know What the S/W Supposed To Do Write Tests Based on Those Ideas Dig up Old Requirements Documents and Project Memos ??? - 변경을 가하기 전에 시스템이 어떻게 동작하도록 되어 있는지 알아내야 할 필요가 있다. - 이러한 요구사항에 기반하여 테스트를 작성할 수 있다. - 어떻게 해야 하나 ? 오래된 요구 사항 문서를 조사해야 하나 ? 프로젝트 메모들을 뒤져야 하나 ?
  • 15.
    Characterization Test What the systems does is more important than what it is supposed to do. We want preserve behavior Test that characterizes the actual behavior of a piece of code. - 시스템이 어떻게 동작하기로 약속되었는지 보다 실제로 어떻게 동작하는지가 훨씬 중요하다. - 비록 코드에 잘못이 있더라도 그 행위를 보존하기를 원합니다. - 코드가 실제로 어떤 행동을 하는지를 나타낼 수 있는 테스트를 작성하는 것. 이러한 기법을 Characterization Test라고 한다.
  • 16.
    Steps Write an assertion that you know will fail Let the failure tell you what the behavior is Change the test so that it expects the behavior that the code produces 절차 1. 실패할 것이라고 알고 있는 assert 문장을 작성한다. 2. 이 실패를 통해 실제 행위가 무엇인지 발견한다. 3. 코드가 생산하는 행위를 기대하도록 테스트를 변경한다.
  • 17.
    Characterization Test Example XML 요청을 만드는 팩토리에 대한 테스트 이 테스트는 다소 복잡해서 설명의 편의를 위해 미리 슈퍼 클래스에 복잡한 부분을 구현해 놓았습니다.
  • 18.
    Hard to AddTest Because of Unwanted Dependency static funciton등과 같은 원치 않는 의존성으로 인해 테스트를 작성하기 어려운 경우가 있습니다.
  • 19.
    Subclass and Override Method A core technique for breaking dependencies in OOP Use inheritance in the context of a test to nullify behavior that you don’t care about to get access to behavior that you do care about subclass and override method는 OOP에서 의존성 제거를 위한 핵심 기능이다. 테스트에서 subclassing을 아래와 같은 목적으로 사용한다. 1. 원치 않는 행위를 무효화하기 위해 2. 관심있는 행위에 접근하기 위해
  • 20.
    Steps Identify the dependencies that you want to separate Make each method overridable(adjust the visibility of the methods) Create a subclass that overrides the methods. 분리하고 하는 의존성을 식별한다. 메소드의 가시성을 protected 등으로 변경하여 메소드가 override 가능하도록 한다. 해당 메소드들을 override할 서브클래스를 생성한다.
  • 21.
    Subclass and Override Method Example static 함수는 powermock을 사용해서 mocking이 가능하다. 하지만 static 함수 자체가 싱글톤과 마찬가지로 전역변수를 사용하는 것과 같은 잘못된 습관이다. 그러므로 Static 함수로 인한 테스트 어려움을 제거하는 방법을 알아보자.
  • 22.
    Big Function 쉽게 이해하기어려운 큰 함수를 개선하는 경우를 생각해 보자.
  • 23.
    Big function hides classes 큰 함수는 (인자, 로컬 변수로 표현되는 일련의 변수들)과 (이 변수들에 동작하는 들여쓰기로 구분되는 기능들)을 가지고 있다. 클래스 = (일련의 변수(필드)들_ + (이 변수들에 동작하는 함수들)의 집합이다. 그리므로 큰 함수는 클래스로 추출되어야 한다.
  • 24.
    Extract Method Object Steps Extract Method(invoke) Create New Inner Class Convert Arg. to Field Convert Local Var. to Field Move Initialization Logic to constructor 1. 큰 메소드를 invoke라는 이름의 메소드로 추출한다. 2. invoke 메소드를 갖는 inner 클래스를 생성한다. - 함수의 인자는 클래스의 생성자에 인자로 전달하고, invoke 메소드에서는 인자를 제거합니다. 3. 생성자에 전달된 인자를 필드로 변경한다. 4. 함수의 로컬 변수를 필드로 변경한다. 이러한 변경은 클래스 내부에서 메소드 호출시 불필요한 인자를 없애준다. 5. 변수들의 initialization 로직은 constructor로 이동한다.
  • 25.
    Extract Method Object Parameterize Steps Differences Extract Methods Till You Drop Move Type to New File Rename class / method Method Ordering(Top-Down, To-paragraph) 6. 이제 클래스에 더 이상 extract할 메소드가 없을 때 까지 extract method를 수행한다. 이때 반복적으로 나타나는 중복 문장들에서 약간씩 다른 점들이 존재한다면 이를 파 라미터로 처리합니다. 7. inner 클래스를 일반 클래스로 변경 8. 클래스 이름과 메소드 이름을 의미있게 변경한다. 9. 메소드 순서를 변경해서 top-down(to paragraph)으로 읽기 쉽게 변경한다.
  • 26.
    Extract Method Object Example Fitness 이 예제에서는 큰 함수를 extract method object 기법을 통해 새로운 클래스로 추출하고, extract method를 수행하는 예를 보이도록 하겠다.
  • 27.
    Usage of Extract Method Object Extract Method Object는 TDD에서도 유용하게 사용될 수 있다. 이 예제는 켄트벡이 TDD 스크린 캐스트에서 사용한 예제인데, key-value 스토어의 일종이 tyrant의 클라이언트를 만드는 예제이다. 1. 이와 같이 클라이언트를 생성하고, put하고 get하여 동일한 값인지 확인하는 테스트로 시작한다. 이 기능을 구현하려면 많은 시간이 소요된다. 켄트벡 자신이 2시간 가량 걸릴 것이라고 했으니.
  • 28.
    Usage of Extract Method Object 이 보다는 본래의 의도는 커멘트 처리하고 소켓을 생성해서 서버에 연결이 되는지 부터 확인하는 것이 쉽다. 디버깅 필요 없이 바로 실행이 되니
  • 29.
    Usage of Extract Method Object 이렇게 테스트 코드에서 실제 구현을 다 해보는 것이다. 바로 수행해 볼 수 있으니 매우 편리하고 스피디하게 진행된다.
  • 30.
    Usage of Extract Method Object 기능이 다 구현되면 extract method object 기법을 이용해서 production class로 추출한다.
  • 31.
    Usage of Extract Method Object 약간의 리팩토리을 통해 상수를 제거하고, open, close, get 메소드를 추출하면 이와 같이 된다. 테스트에서 시작해서 production code을 추출했기 때문에 대한 테스트가 자연적으로 생기는 것을 알 수 있다. 항상 테스트부터 시작함으로써 테스트가 없는 레거시를 만들지 않고 항상 설계 개선을 위해 리팩토링을 할 수 있는 구조가 된다.
  • 32.
    Quick & Dirtyis OK If you had tests It’ll remove Fear to clean it TDD debugging time / low level documentation / decoupling
  • 33.
    Reference Working Effectively withLegacy Code Clean Coders Code Cast by Uncle Bob TDD Code Cast by Kent beck 토비의 스프링 3
  • 34.
    Q&A msbaek@daumcorp.com http://twitter.com/ctemplate . Grady Booch: Clean code should read like well written prose. . Martin Fowler: 어떤 바보도 컴퓨터가 이해할 수 있는 코드를 작성할 수 있다. 하지만 사람이 이해할 수 있는 코드를 작성하는 것은 좋은 프로그래머를 요한다.
  • 35.
  • 36.
    Etc. Adding New Feature TDD / Programming by Difference Reuse What Study Test Programming by Difference OO에서 새로운 기능을 추가하는 방법 중 하나 기존 클래스를 직접 수정하지 않고 새로운 기능을 추가하기 위해 상속을 사용 새로운 기능을 추가한 후에 어떻게 기능이 통합되어지길 원하는지 정확히 파악할 수 있다. 상속을 후에 위임으로 변경 가능 상속의 문제점 다중 상속 불가(2개 이상의 클래스에 구현된 기능을 상속을 통해 사용할 수 없다). 상위 클래스의 기능을 하위 클래스에서 재사용(???) 한다면 상위 클래스 변경시 많은 하위 클래스의 변경이 유발됨 상위 클래스에 로직을 구성하고 변경 가능한 부분은 인터페이스로 정의하고 필요에 따라 구현 클래스를 추가해야함. 객체지향은 새로운 타입 추가에 열려있음. 새로운 함수 추가가 아니라. 우리가 재사용하고자 하는 것은 상위 레벨의 로직이지 하위 레벨의 API가 아님 Independent Deployable(Development) Layered Architecture
  • 37.
    Don’t Start BigProject New Project for Refactoring or Testing Everything is fine at start Doing New design Cause Double-duty Impossible to replace the old system - 처음엔 좋다. 모든 것을 원하는 대로 할 수 있다. - 좋은 디자인을 위해서도 시간을 좀 보낸다. - 기존 시스템에 버그 수정, 기능 추가 요청이 들어온다. PM이 신 시스템이 나올때까지 요구사항 반영을 미뤄보려 하지만 고객은 기다리지 않는다. - 신규 프로젝트 팀은 기존 기능을 개선해서 구현하는 것 외에 새로운 요구사항도 구현해야 하는 이중고에 시달린다. - 더 이상 신규 프로젝트를 완료해서 신 시스템이 기존 시스템을 교체할 수 있다고 믿지 못한다. - 이 접근법은 항상 실패한다.
  • 38.
    Wait Until NewFeature is Needed Change little bit of design Add a few tests in the affected area Eventually spread the test through out the legacy code Not all but important/vital bits are get tested
  • 39.
    Always Prefer TestFirst For all new code you write write test first Option either modify or add a new module write a new module with TFD
  • 40.
    Have to Understandbut Function is Too Big Function Size(Screenful) Function Should Do One Thing Extract Major Sections into Function Extract Different levels of abstract 함수 내에서 주요 섹션을 새로운 함수로 추출하는 것은 쉽다. 하지만 추상화 수준이 다른 것을 모두 추출하는 것은 모호하다.
  • 41.
    Extract Different Levels of Abstraction Extract Till You Drop Until simply restates the code without changing abstraction level Most basic refactoring technique Safe with tool support - 이 코드가 한가지 일을 하는 것인가 ? 세가지 일을 하는 것인가 ? 이 코드를 한가지로 줄인다면 includeSetupsAndTeardownsIfTestPage로 extract하면 추상화 수준의 변경 없이 단순히 코드를 재인용하는 것 밖에는 안된다. 이런 경우는 더 이상 extract할 수 없는 것이다. - 리팩토링의 가장 기본 기법 - 이클립스와 같은 툴의 지원으로 테스트 없이도 안전하게 수행할 수 있음.
  • 42.
    As a Result ComposedMethod Pattern To Paragraphs All class’s methods are less than 4 lines {} in if, while stmt removed Comment removed Easy to read in top-down way