Refactoring at Large     Danilo Sato - @dtsato        ThoughtWorks       www.dtsato.com
My Plan for Today+ •    “Inside out” approach  • Show me Code (Java)  • Strategies and Objectives  • Refactoring Ruby/Rail...
Why do we change     code?
Why do we change       code?• Add features
Why do we change       code?• Add features• Fix bugs
Why do we change       code?• Add features• Fix bugs• Improve design
Why do we change       code?• Add features• Fix bugs• Improve design• Optimization
Why do we change       code?• Add features• Fix bugs• Improve design• Optimization
"Refactoring is a disciplined technique for restructuring anexisting body of code, altering its    internal structure with...
Refactoring    Code
Refactoring    Code
Refactoring    Code           Extr act Method
Refactoring    Code
Refactoring    Code
Refactoring    Code           Eliminate Duplication
•   Extract Method•   Move Method•   Extract Class•   Remove Middle Man•   Self Encapsulate Field•   Decompose Conditional...
Tests!                CodeTest
Tests!                CodeTest
Tests!                     CodeTest                Eliminate Duplication
Other types...                    CodeTest
Other types...                 Code   CodeTest
Other types...                 Code   CodeTest   Test
Other types...                 Code         CodeTest   Test                   Split Responsibility
Other types...                    CodeTest
Other types...Test               Code
Other types...Test               Code
Other types...Test               Code                   Change Design
Even more...
“Big Picture”
“Big Picture”
“Big Picture”
“Big Picture”
“Big Picture”
StrategyMechanics
Coding Kata!
Coding Kata!
CheckoutControllerTest   CheckoutController                         Order        Cart
-                                    1 2/10/refactoring                 o.com/blog/2011/http://www.dtsat                  ...
public class CheckoutControllerTest {    private   CheckoutController checkoutController;    private   Cart cart = new Car...
@Controller public class CheckoutController {    private final Cart cart;    private final Order order;    @Autowired publ...
Mechanics                       t his video on:        riment               Watch           factoring-expe               c...
First Attempt...                       t his video on:        riment               Watch           factoring-expe         ...
Strategy                       t his video on:        riment               Watch           factoring-expe               co...
Identify your Objective
Identify your Objective
Identify your Objective      “Active” design
Identify your Objective            “Active” design•   OO Design
Identify your Objective              “Active” design•   OO Design•   Project direction
Identify your Objective              “Active” design•   OO Design•   Project direction•   Architectural decisions
Identify your Objective      “Passive” design
Identify your Objective                 “Passive” design•   Bad Smells
Identify your Objective                 “Passive” design•   Bad Smells•   Metrics
Identify your Objective                 “Passive” design•   Bad Smells•   Metrics•   Visualizations
Strategize
Strategize    ?                ??
Strategize
Strategize• Isolate the impact of Change
Strategize• Isolate the impact of Change• Baby Steps
Strategize• Isolate the impact of Change• Baby Steps• Keep the tests green
Strategize•   Isolate the impact of Change•   Baby Steps•   Keep the tests green•   Moving backwards before moving forward
CalendarSearchController                           CalendarSearchController          Test         Event                   ...
r                       dtsato/kata-ref actoring-calendahttp ://github.com/  CalendarSearchController                     ...
def index(params)  scope = Event  case params[:timeframe]  when tomorrow    scope = scope.between_day(DateTime.now + 1)  w...
def index(params)  scope = Event                     switch/case  case params[:timeframe]  when tomorrow    scope = scope....
def index(params)  scope = Event  case params[:timeframe]  when tomorrow    scope = scope.between_day(DateTime.now + 1)  w...
def index(params)  scope = Event  case params[:timeframe]  when tomorrow    scope = scope.between_day(DateTime.now + 1)  w...
class Event < ActiveRecord::Base  named_scope :between_dates, lambda { |start_date, end_date|    {:conditions => ["at >= ?...
different ways                                                to do the sameclass Event < ActiveRecord::Base              ...
class Event < ActiveRecord::Base  named_scope :between_dates, lambda { |start_date, end_date|    {:conditions => ["at >= ?...
ObjectivesActive: • Isolate unit tests from the databasePassive: • Deal with bad smells
Strategizing1. Centralize date manipulation logic2. Unify named scopes3. Extract logic from Controller4. Break timeframe i...
def index(params)  case params[:timeframe]  when tomorrow    date = DateTime.now + 1    start_date = date.at_beginning_of_...
class Event < ActiveRecord::Base  named_scope :between_dates, lambda { |start_date, end_date|    {:conditions => {:at => s...
class Event < ActiveRecord::Base  named_scope :between_dates, lambda { |start_date, end_date|    {:conditions => {:at => s...
Before                . ............                             ds              0 .49367 secon Finished in           res ...
Before                               .......                                                                After         ...
Strategizing1. Centralize date manipulation logic2. Unify named scopes3. Extract logic from Controller4. Break timeframe i...
class CalendarSearchController  attr_reader :events  def index(params)    timeframe = TimeFrame.for(params[:timeframe], pa...
describe "Acceptance tests" do  around(:each) do |example|    Event.transaction { example.run; raise ActiveRecord::Rollbac...
describe "Acceptance tests" do  around(:each) do |example|                                         After                  ...
class TimeFrame < Struct.new(:start_date, :end_date)  def self.for(type, start_date, end_date, hour)    case type    when ...
describe TimeFrame do  before do    @today = DateTime.strptime(06/01/2011, %m/%d/%Y)    DateTime.stub!(:now).and_return(@t...
describe TimeFrame do  before do    @today = DateTime.strptime(06/01/2011, %m/%d/%Y)    DateTime.stub!(:now).and_return(@t...
Before .......                             dsdescribe d in 0 .25355 secon Finishe TimeFrame do es  before les, 0 failur  7...
Before                             ........                                                              After .......    ...
Strategizing1. Centralize date manipulation logic2. Unify named scopes3. Extract logic from Controller4. Break timeframe i...
class TimeFrame  attr_reader :start_date, :end_date  class Today < TimeFrame    def initialize      date = DateTime.now   ...
class TimeFrame  ...  def self.for(type, start_date, end_date, hour)    case type    when tomorrow then Tomorrow.new    wh...
class TimeFrame  ...          Before .def .self.for(type, start_date, end_date, hour)   ... ...     case type             ...
class TimeFrame  ...         Before                          ............                                                 ...
class TimeFrame  ...         Before                          ............                                                 ...
“Big Picture”• Architectural Changes• Consolidating different approaches• Substituting components/frameworks
“Big Picture”
“Big Picture”Scope    Risk   Effort   Communication
Change
x x            x x  x              x x      Change      x xx       x
x x            x x  x              x x      Change      x xx       x
x         x       x x                x x         x                  x x  xx x               Changexxx                 x xx...
x                 x                              xx x         x                      x       x x                x x       ...
x                   x                                        xx x                 x                        x              ...
x                     x                      x             x        xx x     x                                   xx x     ...
Change
x x            x x  x              x x      Change      x xx       x
x x            x x  x              x x      Change      x xx       x
xPrereq       x            x x       x              x x           Change           x xx            x
xPrereq       x              x                    x Prereq       x              x x           Change           x xx       ...
xPrereq       x              x                    x Prereq       x              x x           Change           x xx       ...
Prereq            Prereq         Change         Prereq
x                x       Prereq                    Prereq  xx x                      Changexxx                        Prereq
x                         Prereq                     x            Prereq                    Prereq  xx x Prereq           ...
Prereq         Prereq              PrereqPrereq              Change                    Prereq
Prereq         Prereq              PrereqPrereq              Change                    Prereq
Prereq       Prereq              PrereqPrereq ✔          Change                  Prereq
Prereq       Prereq              PrereqPrereq ✔          Change                  Prereq
Prereq   ✔       Prereq                PrereqPrereq ✔          Change                  Prereq
Prereq   ✔       Prereq   ✔            PrereqPrereq ✔            Change                    Prereq
Prereq   ✔       Prereq   ✔            PrereqPrereq ✔            Change                    Prereq
Prereq   ✔       Prereq   ✔            Prereq   ✔Prereq ✔            Change                    Prereq
Prereq   ✔       Prereq   ✔            Prereq   ✔Prereq ✔            Change                    Prereq
Prereq   ✔       Prereq   ✔                Prereq   ✔Prereq ✔            Change                    Prereq   ✔
Prereq   ✔       Prereq   ✔                Prereq   ✔Prereq ✔            Change   ✔                    Prereq   ✔
A story          APP
A story                APPSVC
A story                APPSVC
A story                APPSVC
The truth              APP              Model              View            Controller
The truth              APP              Model              View            Controller             Helper
The truth                APP                Model                ViewSVC              Controller               Helper
The truth                             APP                             Model                             ViewSVC           ...
TransitionalArchitecture            APP            Model            View          ControllerSVC           Helper
TransitionalArchitecture            APP            Model            View          ControllerSVC           Helper
TransitionalArchitecture            APP            Model            View          ControllerSVC           Helper
TransitionalArchitecture            APP            Model            View          ControllerSVC           Helper
TransitionalArchitecture            APP            Model            View          ControllerSVC           Helper
TransitionalArchitecture            APP            Model            View          ControllerSVC           Helper
Transitional         Architecture• Go: • “Branch by Abstraction” • Migrating from iBatis to Hibernate • Migrating from Vel...
Transitional              Architecture    • Go:     • “Branch by Abstraction”     • Migrating from iBatis to Hibernate    ...
ReferencesBooks:"Refactoring: Improving the Design of Existing Code" - Martin Fowler, Kent Beck, JohnBrant, William Opdyke...
Obrigado!Danilo Sato - @dtsato   ThoughtWorks  www.dtsato.com
Refactoring at Large
Refactoring at Large
Refactoring at Large
Refactoring at Large
Refactoring at Large
Refactoring at Large
Refactoring at Large
Refactoring at Large
Refactoring at Large
Upcoming SlideShare
Loading in...5
×

Refactoring at Large

2,206

Published on

Slides from my session at the Atlanta Software Craftsmanship meetup on "Refactoring at Large". Covering examples of refactoring in Java, Ruby/Rails and some ways to make architecture changes in a safer and step-by-step way

Published in: Technology
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,206
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
49
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide
  • \n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Assumptions:\n- You&amp;#x2019;re not in a complete mess\n- You have tests\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • zoom out\n
  • zoom out\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
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Mec&amp;#xE2;nica: IDE resolve, ferramentas, cat&amp;#xE1;logos...\n
  • Exerc&amp;#xED;cios para praticar\nMelhoram uma habilidade espec&amp;#xED;fica: TDD, Algoritmos, Design, C&amp;#xF3;digo ruim? etc...\n
  • Cobertura 100%\nApplica&amp;#xE7;&amp;#xE3;o Java/Spring\n
  • \n
  • Objetivo: extrair placeOrder para outro controller\n
  • Extracting placeOrder() into a new OrderController class, and a new OrderControllerTest.\nNote: Controller Annotation is copy/pasted\n
  • Note: Controller name first typo (Products -&gt; SuggestedProducts)\n\n
  • Extracting Upseller collaborator\nNote: Controller annotation needs to be copy/pasted\n
  • \n
  • N&amp;#xE3;o tem segredo para identificar objetivo (NFRs)\n
  • N&amp;#xE3;o tem segredo para identificar objetivo (NFRs)\n
  • N&amp;#xE3;o tem segredo para identificar objetivo (NFRs)\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  • Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  • Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  • Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  • Aplica&amp;#xE7;&amp;#xE3;o Rails. Cobertura 100%\nForm de busca de eventos\nEm Ruby -&gt; n&amp;#xE3;o tem mesmas ferramentas avan&amp;#xE7;adas de refatora&amp;#xE7;&amp;#xE3;o\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • * Escopo &amp;#xFA;nico: between_dates\n* Mais feio (indo para tr&amp;#xE1;s antes de ir para frente)\n* Isolando mudan&amp;#xE7;a (toda a l&amp;#xF3;gica de datas num &amp;#xFA;nico lugar)\n
  • * Remo&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo &gt; Adi&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo\n* Testes mais r&amp;#xE1;pidos pois os testes dos 3 named scopes acessavam o BD\n
  • * Remo&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo &gt; Adi&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo\n* Testes mais r&amp;#xE1;pidos pois os testes dos 3 named scopes acessavam o BD\n
  • * Remo&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo &gt; Adi&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo\n* Testes mais r&amp;#xE1;pidos pois os testes dos 3 named scopes acessavam o BD\n
  • \n
  • Skinny Controller\nTestes do Controller usam mocks para testar colabora&amp;#xE7;&amp;#xE3;o\n
  • Testes do controller moveram para teste de aceita&amp;#xE7;&amp;#xE3;o (cucumber?)\n\n
  • * Timeframe s&amp;#xF3; cont&amp;#xEA;m start/end\n* L&amp;#xF3;gica ainda &amp;#xE9; feia\n
  • * Teste n&amp;#xE3;o acessa mais o BD!\n* Adi&amp;#xE7;&amp;#xE3;o de muitos testes\n* Pouca mudan&amp;#xE7;a no c&amp;#xF3;digo em si\n
  • * Teste n&amp;#xE3;o acessa mais o BD!\n* Adi&amp;#xE7;&amp;#xE3;o de muitos testes\n* Pouca mudan&amp;#xE7;a no c&amp;#xF3;digo em si\n
  • * Teste n&amp;#xE3;o acessa mais o BD!\n* Adi&amp;#xE7;&amp;#xE3;o de muitos testes\n* Pouca mudan&amp;#xE7;a no c&amp;#xF3;digo em si\n
  • \n
  • * Separa&amp;#xE7;&amp;#xE3;o de subclasses em TimeFrame para remover switch/case\n* Testes mudam junto\n
  • * Factory method ainda tem switch/case (discutir poss&amp;#xED;vel uso de metaprograma&amp;#xE7;&amp;#xE3;o)\n* Redu&amp;#xE7;&amp;#xE3;o total dos testes unit&amp;#xE1;rios: de 0.4936 para 0.19023 (61%)\n
  • * Factory method ainda tem switch/case (discutir poss&amp;#xED;vel uso de metaprograma&amp;#xE7;&amp;#xE3;o)\n* Redu&amp;#xE7;&amp;#xE3;o total dos testes unit&amp;#xE1;rios: de 0.4936 para 0.19023 (61%)\n
  • * Factory method ainda tem switch/case (discutir poss&amp;#xED;vel uso de metaprograma&amp;#xE7;&amp;#xE3;o)\n* Redu&amp;#xE7;&amp;#xE3;o total dos testes unit&amp;#xE1;rios: de 0.4936 para 0.19023 (61%)\n
  • \n
  • L&amp;#xED;der T&amp;#xE9;cnico: compartilha a vis&amp;#xE3;o\nWorkshops de arquitetura presente vs. futura\nRequires a disciplined approach to tackling large changes\n
  • L&amp;#xED;der T&amp;#xE9;cnico: compartilha a vis&amp;#xE3;o\nWorkshops de arquitetura presente vs. futura\nRequires a disciplined approach to tackling large changes\n
  • L&amp;#xED;der T&amp;#xE9;cnico: compartilha a vis&amp;#xE3;o\nWorkshops de arquitetura presente vs. futura\nRequires a disciplined approach to tackling large changes\n
  • L&amp;#xED;der T&amp;#xE9;cnico: compartilha a vis&amp;#xE3;o\nWorkshops de arquitetura presente vs. futura\nRequires a disciplined approach to tackling large changes\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Escolha objetivo, comece de forma inocente, aprenda e desenho um grafo, rollback quando est&amp;#xE1; quebrado, trabalhe das folhas para a ra&amp;#xED;z\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • \n
  • \n
  • \n
  • Refactoring at Large

    1. 1. Refactoring at Large Danilo Sato - @dtsato ThoughtWorks www.dtsato.com
    2. 2. My Plan for Today+ • “Inside out” approach • Show me Code (Java) • Strategies and Objectives • Refactoring Ruby/Rails- • Larger refactorings
    3. 3. Why do we change code?
    4. 4. Why do we change code?• Add features
    5. 5. Why do we change code?• Add features• Fix bugs
    6. 6. Why do we change code?• Add features• Fix bugs• Improve design
    7. 7. Why do we change code?• Add features• Fix bugs• Improve design• Optimization
    8. 8. Why do we change code?• Add features• Fix bugs• Improve design• Optimization
    9. 9. "Refactoring is a disciplined technique for restructuring anexisting body of code, altering its internal structure withoutchanging its external behavior."
    10. 10. Refactoring Code
    11. 11. Refactoring Code
    12. 12. Refactoring Code Extr act Method
    13. 13. Refactoring Code
    14. 14. Refactoring Code
    15. 15. Refactoring Code Eliminate Duplication
    16. 16. • Extract Method• Move Method• Extract Class• Remove Middle Man• Self Encapsulate Field• Decompose Conditional• ...
    17. 17. Tests! CodeTest
    18. 18. Tests! CodeTest
    19. 19. Tests! CodeTest Eliminate Duplication
    20. 20. Other types... CodeTest
    21. 21. Other types... Code CodeTest
    22. 22. Other types... Code CodeTest Test
    23. 23. Other types... Code CodeTest Test Split Responsibility
    24. 24. Other types... CodeTest
    25. 25. Other types...Test Code
    26. 26. Other types...Test Code
    27. 27. Other types...Test Code Change Design
    28. 28. Even more...
    29. 29. “Big Picture”
    30. 30. “Big Picture”
    31. 31. “Big Picture”
    32. 32. “Big Picture”
    33. 33. “Big Picture”
    34. 34. StrategyMechanics
    35. 35. Coding Kata!
    36. 36. Coding Kata!
    37. 37. CheckoutControllerTest CheckoutController Order Cart
    38. 38. - 1 2/10/refactoring o.com/blog/2011/http://www.dtsat / h rough-experimentst rategies-a-walkt - 0 9/01/refactoringhttp://paulh ammant.com/2011/experiment CheckoutControllerTest CheckoutController Order Cart
    39. 39. public class CheckoutControllerTest { private CheckoutController checkoutController; private Cart cart = new Cart(); // could as easily be a mock private Order order = new Order(); // could as easily be a mock private ModelMap model = new ModelMap(); @Before public void setup() { checkoutController = new CheckoutController(cart, order); } @Test public void checkout_should_add_an_upsell() { String result = checkoutController.goCheckout(model); assertThat(result, equalTo("checkout")); assertThat(model.toString(), equalTo("{cart=Cart{contents=[[iPad]]}}")); } @Test public void place_order_in_checkout_should_make_an_order() { cart.addTo("dummy"); String result = checkoutController.placeOrder(model); assertThat(result, equalTo("orderPlaced")); assertThat(model.toString(),equalTo("{order=Order{cart=Cart{contents=[[dummy]]}}}")); }}
    40. 40. @Controller public class CheckoutController { private final Cart cart; private final Order order; @Autowired public CheckoutController(Cart cart, Order order) { this.cart = cart; this.order = order; } @RequestMapping("/go/to/checkout/") public String goCheckout(ModelMap model) { cart.addTo(findUpsell(cart)); model.addAttribute("cart", cart); return "checkout"; } @RequestMapping("/place/order") public String placeOrder(ModelMap model) { order.place(cart); model.addAttribute("order", order); return "orderPlaced"; } String findUpsell(Cart cart) { return cart.size() == 0 ? "iPad" : "iPod Nano"; }}
    41. 41. Mechanics t his video on: riment Watch factoring-expe com/dt sato/re p://vimeo.htt
    42. 42. First Attempt... t his video on: riment Watch factoring-expe com/dt sato/re p://vimeo.htt
    43. 43. Strategy t his video on: riment Watch factoring-expe com/dt sato/re p://vimeo.htt
    44. 44. Identify your Objective
    45. 45. Identify your Objective
    46. 46. Identify your Objective “Active” design
    47. 47. Identify your Objective “Active” design• OO Design
    48. 48. Identify your Objective “Active” design• OO Design• Project direction
    49. 49. Identify your Objective “Active” design• OO Design• Project direction• Architectural decisions
    50. 50. Identify your Objective “Passive” design
    51. 51. Identify your Objective “Passive” design• Bad Smells
    52. 52. Identify your Objective “Passive” design• Bad Smells• Metrics
    53. 53. Identify your Objective “Passive” design• Bad Smells• Metrics• Visualizations
    54. 54. Strategize
    55. 55. Strategize ? ??
    56. 56. Strategize
    57. 57. Strategize• Isolate the impact of Change
    58. 58. Strategize• Isolate the impact of Change• Baby Steps
    59. 59. Strategize• Isolate the impact of Change• Baby Steps• Keep the tests green
    60. 60. Strategize• Isolate the impact of Change• Baby Steps• Keep the tests green• Moving backwards before moving forward
    61. 61. CalendarSearchController CalendarSearchController Test Event Event
    62. 62. r dtsato/kata-ref actoring-calendahttp ://github.com/ CalendarSearchController CalendarSearchController Test Event Event
    63. 63. def index(params) scope = Event case params[:timeframe] when tomorrow scope = scope.between_day(DateTime.now + 1) when this_week scope = scope.between_dates(DateTime.now, ( DateTime.now + 6 ).end_of_day) when custom if params[:start_date].blank? params[:start_date] = DateTime.now.beginning_of_week.strftime(%m/%d/%Y) end if params[:end_date].blank? params[:end_date] = (DateTime.now.end_of_week - 2).strftime(%m/%d/%Y) end scope = scope.between_dates(DateTime.strptime(params[:start_date], %m/%d/%Y),DateTime.strptime(params[:end_date], %m/%d/%Y).end_of_day) when hour scope = scope.between_hour_on_day(DateTime.strptime(params[:hour], %m/%d/%Y %H:%M)) when today scope = scope.between_day(DateTime.now) end @events = scope.allend
    64. 64. def index(params) scope = Event switch/case case params[:timeframe] when tomorrow scope = scope.between_day(DateTime.now + 1) when this_week scope = scope.between_dates(DateTime.now, ( DateTime.now + 6 ).end_of_day) when custom if params[:start_date].blank? params[:start_date] = DateTime.now.beginning_of_week.strftime(%m/%d/%Y) end if params[:end_date].blank? params[:end_date] = (DateTime.now.end_of_week - 2).strftime(%m/%d/%Y) end scope = scope.between_dates(DateTime.strptime(params[:start_date], %m/%d/%Y),DateTime.strptime(params[:end_date], %m/%d/%Y).end_of_day) when hour scope = scope.between_hour_on_day(DateTime.strptime(params[:hour], %m/%d/%Y %H:%M)) when today scope = scope.between_day(DateTime.now) end @events = scope.allend
    65. 65. def index(params) scope = Event case params[:timeframe] when tomorrow scope = scope.between_day(DateTime.now + 1) when this_week string parsing scope = scope.between_dates(DateTime.now, ( DateTime.now + 6 ).end_of_day) when custom if params[:start_date].blank? params[:start_date] = DateTime.now.beginning_of_week.strftime(%m/%d/%Y) end if params[:end_date].blank? params[:end_date] = (DateTime.now.end_of_week - 2).strftime(%m/%d/%Y) end scope = scope.between_dates(DateTime.strptime(params[:start_date], %m/%d/%Y),DateTime.strptime(params[:end_date], %m/%d/%Y).end_of_day) when hour scope = scope.between_hour_on_day(DateTime.strptime(params[:hour], %m/%d/%Y %H:%M)) when today scope = scope.between_day(DateTime.now) end @events = scope.allend
    66. 66. def index(params) scope = Event case params[:timeframe] when tomorrow scope = scope.between_day(DateTime.now + 1) when this_week scope = scope.between_dates(DateTime.now, ( DateTime.now + 6 ).end_of_day) when custom if params[:start_date].blank? params[:start_date] = DateTime.now.beginning_of_week.strftime(%m/%d/%Y) end if params[:end_date].blank? params[:end_date] = (DateTime.now.end_of_week - 2).strftime(%m/%d/%Y) end scope = scope.between_dates(DateTime.strptime(params[:start_date], %m/%d/%Y),DateTime.strptime(params[:end_date], %m/%d/%Y).end_of_day) when hour scope = scope.between_hour_on_day(DateTime.strptime(params[:hour], %m/%d/%Y %H:%M)) when today scope = scope.between_day(DateTime.now) end multiple named scopes @events = scope.allend
    67. 67. class Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => ["at >= ? AND at <= ?", start_date, end_date ] } } named_scope :between_hour_on_day, lambda { |start_hour| end_date = start_hour + 1.hour-1.second { :conditions => {:at => start_hour..end_date} } } named_scope :between_day, lambda { |date| start_date = date.at_beginning_of_day end_date = start_date.end_of_day { :conditions => {:at => start_date..end_date} } }end
    68. 68. different ways to do the sameclass Event < ActiveRecord::Base thing named_scope :between_dates, lambda { |start_date, end_date| {:conditions => ["at >= ? AND at <= ?", start_date, end_date ] } } named_scope :between_hour_on_day, lambda { |start_hour| end_date = start_hour + 1.hour-1.second { :conditions => {:at => start_hour..end_date} } } named_scope :between_day, lambda { |date| start_date = date.at_beginning_of_day end_date = start_date.end_of_day { :conditions => {:at => start_date..end_date} } }end
    69. 69. class Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => ["at >= ? AND at <= ?", start_date, end_date ] } } named_scope :between_hour_on_day, lambda { |start_hour| end_date = start_hour + 1.hour-1.second { :conditions => {:at => start_hour..end_date} } } more logic named_scope :between_day, lambda { |date| start_date = date.at_beginning_of_day end_date = start_date.end_of_day { :conditions => {:at => start_date..end_date} } }end
    70. 70. ObjectivesActive: • Isolate unit tests from the databasePassive: • Deal with bad smells
    71. 71. Strategizing1. Centralize date manipulation logic2. Unify named scopes3. Extract logic from Controller4. Break timeframe into separate pieces
    72. 72. def index(params) case params[:timeframe] when tomorrow date = DateTime.now + 1 start_date = date.at_beginning_of_day end_date = start_date.end_of_day when this_week start_date = DateTime.now end_date = (DateTime.now + 6 ).end_of_day when custom start_date = params[:start_date].blank? ? DateTime.now.beginning_of_week :DateTime.strptime(params[:start_date], %m/%d/%Y) end_date = params[:end_date].blank? ? (DateTime.now.end_of_week - 2) :DateTime.strptime(params[:end_date], %m/%d/%Y).end_of_day when hour start_date = DateTime.strptime(params[:hour], %m/%d/%Y %H:%M) end_date = start_date + 1.hour-1.second when today date = DateTime.now start_date = date.at_beginning_of_day end_date = start_date.end_of_day end @events = Event.between_dates(start_date, end_date)end
    73. 73. class Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => {:at => start_date..end_date}} }end
    74. 74. class Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => {:at => start_date..end_date}} }end lib/calendar_search_controller.rb | 27 +++++++++++++-------------- lib/event.rb | 13 +------------ spec/event_spec.rb | 37 ------------------------------------- 3 files changed, 14 insertions(+), 63 deletions(-)
    75. 75. Before . ............ ds 0 .49367 secon Finished in res 13 examp les, 0 failuclass Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => {:at => start_date..end_date}} }end lib/calendar_search_controller.rb | 27 +++++++++++++-------------- lib/event.rb | 13 +------------ spec/event_spec.rb | 37 ------------------------------------- 3 files changed, 14 insertions(+), 63 deletions(-)
    76. 76. Before ....... After . ............ ds Finished in 0 .49367 secon 0.25355 seco Finished in res 7 examples, nds 13 examp les, 0 failu 0 failuresclass Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => {:at => start_date..end_date}} }end lib/calendar_search_controller.rb | 27 +++++++++++++-------------- lib/event.rb | 13 +------------ spec/event_spec.rb | 37 ------------------------------------- 3 files changed, 14 insertions(+), 63 deletions(-)
    77. 77. Strategizing1. Centralize date manipulation logic2. Unify named scopes3. Extract logic from Controller4. Break timeframe into separate pieces
    78. 78. class CalendarSearchController attr_reader :events def index(params) timeframe = TimeFrame.for(params[:timeframe], params[:start_date],params[:end_date], params[:hour]) @events = Event.between_dates(timeframe.start_date, timeframe.end_date) endend change to tests:it "should create time frame from params and retrieve events" do timeframe = TimeFrame.new(DateTime.now, DateTime.now) TimeFrame.should_receive(:for).with(today, start, end,hour).and_return(timeframe) events = [Event.new, Event.new] Event.should_receive(:between_dates).with(timeframe.start_date,timeframe.end_date).and_return(events) @controller.index(:timeframe => today, :start_date => start, :end_date =>end, :hour => hour) @controller.events.should == eventsend
    79. 79. describe "Acceptance tests" do around(:each) do |example| Event.transaction { example.run; raise ActiveRecord::Rollback } end before do @controller = CalendarSearchController.new @today = DateTime.strptime(06/01/2011, %m/%d/%Y) DateTime.stub!(:now).and_return(@today) end it "should return todays events" do today_events = [Event.create(:at => @today), Event.create(:at => @today)] tomorrow = @today + 1 tomorrow_events = [Event.create(:at => tomorrow), Event.create(:at => tomorrow)] @controller.index(:timeframe => today) @controller.events.should == today_events end it "should return tomorrows events" ... it "should return this weeks events" ... it "should return events for specified date range" ... it "should default date range to this business week" ... it "returns events for a specified hour" ...end
    80. 80. describe "Acceptance tests" do around(:each) do |example| After ...... Event.transaction { example.run; raise ActiveRecord::Rollback } end Finished in 0.24538 seco before do 6 examples, nds 0 failures @controller = CalendarSearchController.new @today = DateTime.strptime(06/01/2011, %m/%d/%Y) DateTime.stub!(:now).and_return(@today) end it "should return todays events" do today_events = [Event.create(:at => @today), Event.create(:at => @today)] tomorrow = @today + 1 tomorrow_events = [Event.create(:at => tomorrow), Event.create(:at => tomorrow)] @controller.index(:timeframe => today) @controller.events.should == today_events end it "should return tomorrows events" ... it "should return this weeks events" ... it "should return events for specified date range" ... it "should default date range to this business week" ... it "returns events for a specified hour" ...end
    81. 81. class TimeFrame < Struct.new(:start_date, :end_date) def self.for(type, start_date, end_date, hour) case type when tomorrow date = DateTime.now + 1 TimeFrame.new(date.at_beginning_of_day, date.end_of_day) when this_week TimeFrame.new(DateTime.now, (DateTime.now + 6 ).end_of_day) when custom start_date = start_date.blank? ? DateTime.now.beginning_of_week :DateTime.strptime(start_date, %m/%d/%Y) end_date = end_date.blank? ? (DateTime.now.end_of_week - 2) :DateTime.strptime(end_date, %m/%d/%Y).end_of_day TimeFrame.new(start_date, end_date) when hour start_date = DateTime.strptime(hour, %m/%d/%Y %H:%M) end_date = start_date + 1.hour-1.second TimeFrame.new(start_date, end_date) when today date = DateTime.now TimeFrame.new(date.at_beginning_of_day, date.end_of_day) end endend
    82. 82. describe TimeFrame do before do @today = DateTime.strptime(06/01/2011, %m/%d/%Y) DateTime.stub!(:now).and_return(@today) end describe "todays event" do it "should use beginning and end of day" do timeframe = TimeFrame.for(today, nil, nil, nil) timeframe.start_date.should == @today.at_beginning_of_day timeframe.end_date.should == @today.end_of_day end end ...end
    83. 83. describe TimeFrame do before do @today = DateTime.strptime(06/01/2011, %m/%d/%Y) DateTime.stub!(:now).and_return(@today) end describe "todays event" do it "should use beginning and end of day" do timeframe = TimeFrame.for(today, nil, nil, nil) timeframe.start_date.should == @today.at_beginning_of_day timeframe.end_date.should == @today.end_of_day end Rakefile | 4 ++ end lib/calendar_search_controller.rb | 24 +--------- ... lib/time_frame.rb | 24 ++++++++++end spec/acceptance_tests.rb | 75 +++++++++++++++++++++++++++++++ spec/calendar_search_controller_spec.rb | 75 ++++--------------------------- spec/time_frame_spec.rb | 63 ++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 87 deletions(-)
    84. 84. Before ....... dsdescribe d in 0 .25355 secon Finishe TimeFrame do es before les, 0 failur 7 examp do @today = DateTime.strptime(06/01/2011, %m/%d/%Y) DateTime.stub!(:now).and_return(@today) end describe "todays event" do it "should use beginning and end of day" do timeframe = TimeFrame.for(today, nil, nil, nil) timeframe.start_date.should == @today.at_beginning_of_day timeframe.end_date.should == @today.end_of_day end Rakefile | 4 ++ end lib/calendar_search_controller.rb | 24 +--------- ... lib/time_frame.rb | 24 ++++++++++end spec/acceptance_tests.rb | 75 +++++++++++++++++++++++++++++++ spec/calendar_search_controller_spec.rb | 75 ++++--------------------------- spec/time_frame_spec.rb | 63 ++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 87 deletions(-)
    85. 85. Before ........ After ....... ds Finished indescribe d in 0 .25355 secon she TimeFrame do 0.17938 seco nds Fini 8 examples, before les, 0 failures 7 ex amp do 0 failures @today = DateTime.strptime(06/01/2011, %m/%d/%Y) DateTime.stub!(:now).and_return(@today) end describe "todays event" do it "should use beginning and end of day" do timeframe = TimeFrame.for(today, nil, nil, nil) timeframe.start_date.should == @today.at_beginning_of_day timeframe.end_date.should == @today.end_of_day end Rakefile | 4 ++ end lib/calendar_search_controller.rb | 24 +--------- ... lib/time_frame.rb | 24 ++++++++++end spec/acceptance_tests.rb | 75 +++++++++++++++++++++++++++++++ spec/calendar_search_controller_spec.rb | 75 ++++--------------------------- spec/time_frame_spec.rb | 63 ++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 87 deletions(-)
    86. 86. Strategizing1. Centralize date manipulation logic2. Unify named scopes3. Extract logic from Controller4. Break timeframe into separate pieces
    87. 87. class TimeFrame attr_reader :start_date, :end_date class Today < TimeFrame def initialize date = DateTime.now @start_date, @end_date = date.at_beginning_of_day, date.end_of_day end end ...enddescribe TimeFrame::Today do it "should use beginning and end of day" do timeframe = TimeFrame::Today.new timeframe.start_date.should == @today.at_beginning_of_day timeframe.end_date.should == @today.end_of_day end ...end
    88. 88. class TimeFrame ... def self.for(type, start_date, end_date, hour) case type when tomorrow then Tomorrow.new when this_week then ThisWeek.new when custom then Custom.new(start_date, end_date) when hour then Hour.new(hour) when today then Today.new end endenddescribe TimeFrame::Today do ... describe "parsing" do it "should parse todays timeframe" do TimeFrame.for(today, nil, nil, nil).shouldbe_an_instance_of(TimeFrame::Today) end ... endend
    89. 89. class TimeFrame ... Before .def .self.for(type, start_date, end_date, hour) ... ... case type ds 0 .17938 secon when tomorrow then Tomorrow.new Finished in when les, 0 failures ThisWeek.new 8 examp this_week then when custom then Custom.new(start_date, end_date) when hour then Hour.new(hour) when today then Today.new end endenddescribe TimeFrame::Today do ... describe "parsing" do it "should parse todays timeframe" do TimeFrame.for(today, nil, nil, nil).shouldbe_an_instance_of(TimeFrame::Today) end ... endend
    90. 90. class TimeFrame ... Before ............ After def .self.for(type, start_date, end_date, hour) . .... ... case type ds Finished in when d in 0 .17938 secon she tomorrow then Tomorrow.new 0.19023 seco nds Fini 13 examples, when les, 0 failures ThisWeek.new 8 examp this_week then 0 failures when custom then Custom.new(start_date, end_date) when hour then Hour.new(hour) when today then Today.new end endenddescribe TimeFrame::Today do ... describe "parsing" do it "should parse todays timeframe" do TimeFrame.for(today, nil, nil, nil).shouldbe_an_instance_of(TimeFrame::Today) end ... endend
    91. 91. class TimeFrame ... Before ............ After def .self.for(type, start_date, end_date, hour) . .... ... case type ds Finished in when d in 0 .17938 secon she tomorrow then Tomorrow.new 0.19023 seco nds Fini 13 examples, when les, 0 failures ThisWeek.new 8 examp this_week then 0 failures when custom then Custom.new(start_date, end_date) when hour then Hour.new(hour) when today then Today.new end endenddescribe TimeFrame::Today do ... describe "parsing" do lib/time_frame.rb | 59 +++++++++++++++++++++++++++++++++------------- it "should parse todays timeframe" do spec/time_frame_spec.rb | 44 ++++++++++++++++++++++++++-------- TimeFrame.for(today, nil, nil, nil).should 2 files changed, 75 insertions(+), 28 deletions(-)be_an_instance_of(TimeFrame::Today) end ... endend
    92. 92. “Big Picture”• Architectural Changes• Consolidating different approaches• Substituting components/frameworks
    93. 93. “Big Picture”
    94. 94. “Big Picture”Scope Risk Effort Communication
    95. 95. Change
    96. 96. x x x x x x x Change x xx x
    97. 97. x x x x x x x Change x xx x
    98. 98. x x x x x x x x x xx x Changexxx x xx x
    99. 99. x x xx x x x x x x x x x x xx x Changexxx x xx x
    100. 100. x x xx x x x x x x x x x x xx x Changexxx x xx x xx xx x x xxx xx x
    101. 101. x x x x xx x x xx x x x x x x x x x x x x x xx x x Changexxx x x x xx x x x x xx xx x x x x x xxx xx x x x x x
    102. 102. Change
    103. 103. x x x x x x x Change x xx x
    104. 104. x x x x x x x Change x xx x
    105. 105. xPrereq x x x x x x Change x xx x
    106. 106. xPrereq x x x Prereq x x x Change x xx x
    107. 107. xPrereq x x x Prereq x x x Change x xx Prereq x
    108. 108. Prereq Prereq Change Prereq
    109. 109. x x Prereq Prereq xx x Changexxx Prereq
    110. 110. x Prereq x Prereq Prereq xx x Prereq Changexxx Prereq
    111. 111. Prereq Prereq PrereqPrereq Change Prereq
    112. 112. Prereq Prereq PrereqPrereq Change Prereq
    113. 113. Prereq Prereq PrereqPrereq ✔ Change Prereq
    114. 114. Prereq Prereq PrereqPrereq ✔ Change Prereq
    115. 115. Prereq ✔ Prereq PrereqPrereq ✔ Change Prereq
    116. 116. Prereq ✔ Prereq ✔ PrereqPrereq ✔ Change Prereq
    117. 117. Prereq ✔ Prereq ✔ PrereqPrereq ✔ Change Prereq
    118. 118. Prereq ✔ Prereq ✔ Prereq ✔Prereq ✔ Change Prereq
    119. 119. Prereq ✔ Prereq ✔ Prereq ✔Prereq ✔ Change Prereq
    120. 120. Prereq ✔ Prereq ✔ Prereq ✔Prereq ✔ Change Prereq ✔
    121. 121. Prereq ✔ Prereq ✔ Prereq ✔Prereq ✔ Change ✔ Prereq ✔
    122. 122. A story APP
    123. 123. A story APPSVC
    124. 124. A story APPSVC
    125. 125. A story APPSVC
    126. 126. The truth APP Model View Controller
    127. 127. The truth APP Model View Controller Helper
    128. 128. The truth APP Model ViewSVC Controller Helper
    129. 129. The truth APP Model ViewSVC Controller everything is going to break !!! Helper
    130. 130. TransitionalArchitecture APP Model View ControllerSVC Helper
    131. 131. TransitionalArchitecture APP Model View ControllerSVC Helper
    132. 132. TransitionalArchitecture APP Model View ControllerSVC Helper
    133. 133. TransitionalArchitecture APP Model View ControllerSVC Helper
    134. 134. TransitionalArchitecture APP Model View ControllerSVC Helper
    135. 135. TransitionalArchitecture APP Model View ControllerSVC Helper
    136. 136. Transitional Architecture• Go: • “Branch by Abstraction” • Migrating from iBatis to Hibernate • Migrating from Velocity/JsTemplate to JRuby on Rails
    137. 137. Transitional Architecture • Go: • “Branch by Abstraction” • Migrating from iBatis to Hibernate • Migrating from Velocity/JsTemplate to JRuby on Rails - 0 11/05/make-largehttp://continuo usdelivery.com/2 - l y-with-branch-byscale-cha nges-incrementalabstraction/
    138. 138. ReferencesBooks:"Refactoring: Improving the Design of Existing Code" - Martin Fowler, Kent Beck, JohnBrant, William Opdyke, Don Roberts"Refactoring to Patterns" - Joshua Kerievsky"Working Effectively with Legacy Code" - Michael Feathers"Beheading the Software Beast: Relentless Restructurings with The Mikado Method" -Daniel Brolund, Ola EllnestamCredits (fotos):http://www.flickr.com/photos/dhammza/2227347832/http://www.flickr.com/photos/pragdave/173640462/Links:http://paulhammant.com/2011/09/01/refactoring-experimenthttp://continuousdelivery.com/2011/05/make-large-scale-changes-incrementally-with-branch-by-abstraction/Code:https://github.com/dtsato/refactoring_experimenthttps://github.com/dtsato/kata-refactoring-calendar
    139. 139. Obrigado!Danilo Sato - @dtsato ThoughtWorks www.dtsato.com
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×