TDD Beyond Basics : Open Closed Principle
- Learn how to apply Open Closed Principle
This lesson builds on top of the previous lesson on Fakes and Mocks. What does test driven development have to do with design principles? Test driven development is not just about reducing the duplication in code. It involves driving the design of the production code through tests.
scanner_spec.rb into spec directory. Move
real_display.rb and scanner.rb into lib directory. Change the
require_relative statement like this:
Run the specs. It will pass.
We now have a new requirement where we need to use a touch screen display. Let's write the spec for this new requirement. Add the following spec to scanner_spec.rb.
it 'scans and displays the details on touch display' do touch_display = TouchDisplay.new scanner = Scanner.new(touch_display) scanner.scan('1') expect(touch_display.last_line_item).to eq('Milk $3.99') end
to the top of the scanner_spec.rb
Create a touch_display.rb in lib directory with the following contents:
class TouchDisplay attr_reader :last_line_item def display(line_item) @last_line_item = line_item p 'Logic to display the line_item to touch display' end end
Run all the specs. All specs will pass.
To satisfy our new requirement we added new code, we did not modify the existing production code. Open Closed Principle states that a module should be open for extension and closed for modification. Our scanner class satisfies this principle.
We were able to achieve this by using dependency injection to decouple the display from the scanner. As long as any new concrete implementation of the display implements our
display(line_item) interface with an accessor for lastlineitem, we can extend our program without violating Open Closed Principle. If you notice the scanner class, it has the
attr_reader only to allow the test to be written, so why do we need code that is only required by the test? Does the code that is only required by the test violate encapsulation? What do you think?
In this lesson you learned how to apply Open Closed Principle to design a system that is flexible to new requirements. We used constructor dependency injection to achieve decoupling of the scanner from the display. So interchanging new displays that conform to our interface works without any modification to the original code.