Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Code Smells & Refactoring

Code Smells & Refactoring

Radoslav Stankov

March 30, 2013
Tweet

More Decks by Radoslav Stankov

Other Decks in Technology

Transcript

  1. Radoslav Stankov
    VarnaConf 2013 20/07/2013
    Code Smells & Refactoring

    View full-size slide

  2. Кой съм аз?
    @rstankov
    http://rstankov.com
    http://github.com/rstankov

    View full-size slide

  3. Know your tools

    View full-size slide

  4. DRY
    don’t repeat yourself

    View full-size slide

  5. SLAP
    Single Level of Abstraction Principle

    View full-size slide

  6. Law of Demeter

    View full-size slide

  7. Out side of the box

    View full-size slide

  8. Code smell is any symptom in the source code of a
    program that possibly indicates a deeper problem.
    Code smells

    View full-size slide

  9. Code smells are usually not bugs—they are not
    technically incorrect and don't currently prevent
    the program from functioning. Instead, they
    indicate weaknesses in design that may be slowing
    down development or increasing the risk of bugs
    or failures in the future.
    Code smells

    View full-size slide

  10. Code refactoring is a disciplined technique for
    restructuring an existing body of code, altering its
    internal structure without changing its external
    behavior
    Refactoring

    View full-size slide

  11. Exam ExamQuestion
    ExamEntry ExamAnswer

    View full-size slide

  12. class ExamEntry < ActiveRecord::Base
    def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if (question.type == 'single' && question.correct_answer == answer.text) ||
    (question.type == 'multi' && question.correct_answers.include?(answer.text))
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless (question.type == 'single' && question.correct_answer == answer.text) ||
    (question.type == 'multi' && question.correct_answers.include?(answer.text))
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end
    end

    View full-size slide

  13. A method, function, or procedure that has grown
    too large.
    Long Method

    View full-size slide

  14. describe ExamEntry do
    describe "#result" do
    it "works ( I hope :P )" do
    exam = create :exam
    single_1 = create :single_question, exam: exam, points: 1
    single_2 = create :single_question, exam: exam, points: 2
    multi_1 = create :multi_question, exam: exam, points: 1
    multi_2 = create :multi_question, exam: exam, points: 2
    entry = create(:exam_entry, answers: [
    create(:answer, question: single_1, text: single_1.correct_answer),
    create(:answer, question: single_2, text: '-wrong-answer-'),
    create(:answer, question: multi_1, text: multi_1.correct_answers.first),
    create(:answer, question: multi_2, text: '-wrong-answer-')
    ])
    result = entry.result
    result.keys.should eq [:correct_answers, :wrong_answers, :score]
    result[:correct_answers].map(&:question).should eq [single_1, multi_1]
    result[:wrong_answers].map(&:question).should eq [single_2, multi_2]
    result[:score].should eq single_1.points + multi_1.points
    end
    end
    end

    View full-size slide

  15. describe ExamEntry do
    describe "#result" do
    it "works ( I hope :P )" do
    exam = create :exam
    single_1 = create :single_question, exam: exam, points: 1
    single_2 = create :single_question, exam: exam, points: 2
    multi_1 = create :multi_question, exam: exam, points: 1
    multi_2 = create :multi_question, exam: exam, points: 2
    entry = create(:exam_entry, answers: [
    create(:answer, question: single_1, text: single_1.correct_answer),
    create(:answer, question: single_2, text: '-wrong-answer-'),
    create(:answer, question: multi_1, text: multi_1.correct_answers.first),
    create(:answer, question: multi_2, text: '-wrong-answer-')
    ])
    result = entry.result
    result.keys.should eq [:correct_answers, :wrong_answers, :score]
    result[:correct_answers].map(&:question).should eq [single_1, multi_1]
    result[:wrong_answers].map(&:question).should eq [single_2, multi_2]
    result[:score].should eq single_1.points + multi_1.points
    end
    end
    end

    View full-size slide

  16. describe ExamEntry do
    describe "#result" do
    it "works ( I hope :P )" do
    exam = create :exam
    single_1 = create :single_question, exam: exam, points: 1
    single_2 = create :single_question, exam: exam, points: 2
    multi_1 = create :multi_question, exam: exam, points: 1
    multi_2 = create :multi_question, exam: exam, points: 2
    entry = create(:exam_entry, answers: [
    create(:answer, question: single_1, text: single_1.correct_answer),
    create(:answer, question: single_2, text: '-wrong-answer-'),
    create(:answer, question: multi_1, text: multi_1.correct_answers.first),
    create(:answer, question: multi_2, text: '-wrong-answer-')
    ])
    result = entry.result
    result.keys.should eq [:correct_answers, :wrong_answers, :score]
    result[:correct_answers].map(&:question).should eq [single_1, multi_1]
    result[:wrong_answers].map(&:question).should eq [single_2, multi_2]
    result[:score].should eq single_1.points + multi_1.points
    end
    end
    end

    View full-size slide

  17. describe ExamEntry do
    describe "#result" do
    it "works ( I hope :P )" do
    exam = create :exam
    single_1 = create :single_question, exam: exam, points: 1
    single_2 = create :single_question, exam: exam, points: 2
    multi_1 = create :multi_question, exam: exam, points: 1
    multi_2 = create :multi_question, exam: exam, points: 2
    entry = create(:exam_entry, answers: [
    create(:answer, question: single_1, text: single_1.correct_answer),
    create(:answer, question: single_2, text: '-wrong-answer-'),
    create(:answer, question: multi_1, text: multi_1.correct_answers.first),
    create(:answer, question: multi_2, text: '-wrong-answer-')
    ])
    result = entry.result
    result.keys.should eq [:correct_answers, :wrong_answers, :score]
    result[:correct_answers].map(&:question).should eq [single_1, multi_1]
    result[:wrong_answers].map(&:question).should eq [single_2, multi_2]
    result[:score].should eq single_1.points + multi_1.points
    end
    end
    end

    View full-size slide

  18. describe ExamEntry do
    describe "#result" do
    it "works ( I hope :P )" do
    exam = create :exam
    single_1 = create :single_question, exam: exam, points: 1
    single_2 = create :single_question, exam: exam, points: 2
    multi_1 = create :multi_question, exam: exam, points: 1
    multi_2 = create :multi_question, exam: exam, points: 2
    entry = create(:exam_entry, answers: [
    create(:answer, question: single_1, text: single_1.correct_answer),
    create(:answer, question: single_2, text: '-wrong-answer-'),
    create(:answer, question: multi_1, text: multi_1.correct_answers.first),
    create(:answer, question: multi_2, text: '-wrong-answer-')
    ])
    result = entry.result
    result.keys.should eq [:correct_answers, :wrong_answers, :score]
    result[:correct_answers].map(&:question).should eq [single_1, multi_1]
    result[:wrong_answers].map(&:question).should eq [single_2, multi_2]
    result[:score].should eq single_1.points + multi_1.points
    end
    end
    end

    View full-size slide

  19. describe ExamEntry do
    describe "#result" do
    it "works ( I hope :P )" do
    exam = create :exam
    single_1 = create :single_question, exam: exam, points: 1
    single_2 = create :single_question, exam: exam, points: 2
    multi_1 = create :multi_question, exam: exam, points: 1
    multi_2 = create :multi_question, exam: exam, points: 2
    entry = create(:exam_entry, answers: [
    create(:answer, question: single_1, text: single_1.correct_answer),
    create(:answer, question: single_2, text: '-wrong-answer-'),
    create(:answer, question: multi_1, text: multi_1.correct_answers.first),
    create(:answer, question: multi_2, text: '-wrong-answer-')
    ])
    result = entry.result
    result.keys.should eq [:correct_answers, :wrong_answers, :score]
    result[:correct_answers].map(&:question).should eq [single_1, multi_1]
    result[:wrong_answers].map(&:question).should eq [single_2, multi_2]
    result[:score].should eq single_1.points + multi_1.points
    end
    end
    end

    View full-size slide

  20. describe ExamEntry do
    describe "#result" do
    it "works ( I hope :P )" do
    exam = create :exam
    single_1 = create :single_question, exam: exam, points: 1
    single_2 = create :single_question, exam: exam, points: 2
    multi_1 = create :multi_question, exam: exam, points: 1
    multi_2 = create :multi_question, exam: exam, points: 2
    entry = create(:exam_entry, answers: [
    create(:answer, question: single_1, text: single_1.correct_answer),
    create(:answer, question: single_2, text: '-wrong-answer-'),
    create(:answer, question: multi_1, text: multi_1.correct_answers.first),
    create(:answer, question: multi_2, text: '-wrong-answer-')
    ])
    result = entry.result
    result.keys.should eq [:correct_answers, :wrong_answers, :score]
    result[:correct_answers].map(&:question).should eq [single_1, multi_1]
    result[:wrong_answers].map(&:question).should eq [single_2, multi_2]
    result[:score].should eq single_1.points + multi_1.points
    end
    end
    end

    View full-size slide

  21. describe ExamEntry do
    describe "#result" do
    it "works ( I hope :P )" do
    exam = create :exam
    single_1 = create :single_question, exam: exam, points: 1
    single_2 = create :single_question, exam: exam, points: 2
    multi_1 = create :multi_question, exam: exam, points: 1
    multi_2 = create :multi_question, exam: exam, points: 2
    entry = create(:exam_entry, answers: [
    create(:answer, question: single_1, text: single_1.correct_answer),
    create(:answer, question: single_2, text: '-wrong-answer-'),
    create(:answer, question: multi_1, text: multi_1.correct_answers.first),
    create(:answer, question: multi_2, text: '-wrong-answer-')
    ])
    result = entry.result
    result.keys.should eq [:correct_answers, :wrong_answers, :score]
    result[:correct_answers].map(&:question).should eq [single_1, multi_1]
    result[:wrong_answers].map(&:question).should eq [single_2, multi_2]
    result[:score].should eq single_1.points + multi_1.points
    end
    end
    end

    View full-size slide

  22. describe ExamEntry do
    describe "#result" do
    it "works ( I hope :P )" do
    exam = create :exam
    single_1 = create :single_question, exam: exam, points: 1
    single_2 = create :single_question, exam: exam, points: 2
    multi_1 = create :multi_question, exam: exam, points: 1
    multi_2 = create :multi_question, exam: exam, points: 2
    entry = create(:exam_entry, answers: [
    create(:answer, question: single_1, text: single_1.correct_answer),
    create(:answer, question: single_2, text: '-wrong-answer-'),
    create(:answer, question: multi_1, text: multi_1.correct_answers.first),
    create(:answer, question: multi_2, text: '-wrong-answer-')
    ])
    result = entry.result
    result.keys.should eq [:correct_answers, :wrong_answers, :score]
    result[:correct_answers].map(&:question).should eq [single_1, multi_1]
    result[:wrong_answers].map(&:question).should eq [single_2, multi_2]
    result[:score].should eq single_1.points + multi_1.points
    end
    end
    end

    View full-size slide

  23. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if (question.type == 'single' && question.correct_answer == answer.text) ||
    (question.type == 'multi' && question.correct_answers.include?(answer.text))
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless (question.type == 'single' && question.correct_answer == answer.text) ||
    (question.type == 'multi' && question.correct_answers.include?(answer.text))
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  24. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if (question.type == 'single' && question.correct_answer == answer.text) ||
    (question.type == 'multi' && question.correct_answers.include?(answer.text))
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless (question.type == 'single' && question.correct_answer == answer.text) ||
    (question.type == 'multi' && question.correct_answers.include?(answer.text))
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  25. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if (question.type == 'single' && question.correct_answer == answer.text) ||
    (question.type == 'multi' && question.correct_answers.include?(answer.text))
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless (question.type == 'single' && question.correct_answer == answer.text) ||
    (question.type == 'multi' && question.correct_answers.include?(answer.text))
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  26. Conditional complexity

    View full-size slide

  27. Branches that check lots of unrelated conditions
    and edge cases that don't seem to capture the
    meaning of a block of code.
    Complex conditionals

    View full-size slide

  28. (question.type == 'single' && question.correct_answer == answer.text) ||
    (question.type == 'multi' && question.correct_answers.include?(answer.text))

    View full-size slide

  29. describe ExamQuestion do
    describe "#correct_answer?" do
    context "single" do
    it "returns true if answer is correct"
    it "returns false if answer is wrong"
    end
    context "multi" do
    it "returns true if answer included in answers list"
    it "returns false if answer isn’t included in answers list"
    end
    end
    end

    View full-size slide

  30. class ExamQuestion < ActiveRecord::Base
    def correct_answer?(answer)
    if type == 'single'
    correct_answer == answer
    else
    correct_answers.include? answer
    end
    end
    end

    View full-size slide

  31. class ExamQuestion < ActiveRecord::Base
    def correct_answer?(answer)
    if type == 'single'
    correct_answer == answer
    else
    correct_answers.include? answer
    end
    end
    end

    View full-size slide

  32. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if (question.type == 'single' && question.correct_answer == answer.text) || (question.t
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless (question.type == 'single' && question.correct_answer == answer.text) || (questi
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  33. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if (question.type == 'single' && question.correct_answer == answer.text) || (question.t
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless (question.type == 'single' && question.correct_answer == answer.text) || (questi
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  34. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if (question.type == 'single' && question.correct_answer == answer.text) || (question.t
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless (question.type == 'single' && question.correct_answer == answer.text) || (questi
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  35. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless question.correct_answer? answer.text
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  36. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless question.correct_answer? answer.text
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  37. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless question.correct_answer? answer.text
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  38. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless question.correct_answer? answer.text
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  39. Code duplication

    View full-size slide

  40. Identical or very similar code exists in more than
    one location.
    Duplicated code

    View full-size slide

  41. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless question.correct_answer? answer.text
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  42. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless question.correct_answer? answer.text
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  43. def result
    correct_answers = []
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    end
    end
    wrong_answers = []
    for answer in answers
    question = answer.question
    unless question.correct_answer? answer.text
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  44. def result
    correct_answers = []
    wrong_answers = []
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    else
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  45. def result
    correct_answers = []
    wrong_answers = []
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    else
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  46. def result
    correct_answers = []
    wrong_answers = []
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    else
    wrong_answers << answer
    end
    end
    score = 0
    for answer in correct_answers
    score += answer.question.points
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  47. def result
    correct_answers = []
    wrong_answers = []
    score = 0
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    score += question.points
    else
    wrong_answers << answer
    end
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  48. def result
    correct_answers = []
    wrong_answers = []
    score = 0
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    score += question.points
    else
    wrong_answers << answer
    end
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  49. def result
    correct_answers = []
    wrong_answers = []
    score = 0
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    score += question.points
    else
    wrong_answers << answer
    end
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  50. def result
    correct_answers = []
    wrong_answers = []
    score = 0
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    score += question.points
    else
    wrong_answers << answer
    end
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  51. Feature Envy

    View full-size slide

  52. A class that uses methods of another class
    excessively.
    Feature envy

    View full-size slide

  53. def result
    correct_answers = []
    wrong_answers = []
    score = 0
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    score += question.points
    else
    wrong_answers << answer
    end
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  54. describe ExamAnswer do
    describe "#correct?" do
    it "returns true if answer is correct"
    it "returns false if answer is wrong"
    end
    end

    View full-size slide

  55. class ExamAnswer < ActiveRecord::Base
    def correct?
    question.correct_answer? text
    end
    end

    View full-size slide

  56. def result
    correct_answers = []
    wrong_answers = []
    score = 0
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    score += question.points
    else
    wrong_answers << answer
    end
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  57. def result
    correct_answers = []
    wrong_answers = []
    score = 0
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    score += question.points
    else
    wrong_answers << answer
    end
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  58. def result
    correct_answers = []
    wrong_answers = []
    score = 0
    for answer in answers
    question = answer.question
    if question.correct_answer? answer.text
    correct_answers << answer
    score += question.points
    else
    wrong_answers << answer
    end
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  59. def result
    correct_answers = []
    wrong_answers = []
    score = 0
    for answer in answers
    if answer.correct?
    correct_answers << answer
    score += answer.question.points
    else
    wrong_answers << answer
    end
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  60. def result
    correct_answers = []
    wrong_answers = []
    score = 0
    for answer in answers
    if answer.correct?
    correct_answers << answer
    score += answer.question.points
    else
    wrong_answers << answer
    end
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  61. def result
    correct_answers = []
    wrong_answers = []
    score = 0
    for answer in answers
    if answer.correct?
    correct_answers << answer
    score += answer.question.points
    else
    wrong_answers << answer
    end
    end
    {
    correct_answers: correct_answers,
    wrong_answers: wrong_answers,
    score: score
    }
    end

    View full-size slide

  62. Message chain

    View full-size slide

  63. Long sequences of method calls to get routine
    data. Intermediaries are dependencies in disguise.
    Message chain

    View full-size slide

  64. describe ExamAnswer do
    describe "#points" do
    context "correct answer" do
    it "returns its question points"
    end
    context "wrong answer" do
    it "returns 0"
    end
    end
    end

    View full-size slide

  65. describe ExamAnswer do
    describe "#points" do
    context "correct answer" do
    it "returns its question points"
    end
    context "wrong answer" do
    it "returns 0"
    end
    end
    end

    View full-size slide

  66. class ExamAnswer < ActiveRecord::Base
    def points
    return 0 unless correct?
    question.points
    end
    end

    View full-size slide