3. If the AreaComputer changes the way areas are computed, so does the Rectangle. 7 class Rectangle 8 area 9 draw 10 end 11 12 class Renderer 13 def render rectangle 14 rectangle.draw 15 end 16 end 17 18 class AreaComputer 19 def compute rectangle 20 rectangle.area 21 end 22 end
4. Open/ClosedPrinciple SOFTWARE ENTITIES (CLASSES, MODULES, FUNCTIONS, ETC.) SHOULD BE OPEN FOR EXTENSION BUT CLOSED FOR MODIFICATION. It says that you should design modules that never change. When requirements change, you extend the behavior of such modules by adding new code, not by changing old code that already works. 34 module Drawable 35 def draw 36 end 37 end 38 39 class Square 40 include Drawable 41 end 42 43 class Circle 44 include Drawable 45 end 46 47 def draw_all_shapes(shapes) 48 shapes.each { |shape| shape.draw } 49 end
5. Liskov Substitution Principle FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT 56# simple violation of LSP 57 def draw_shape(shape) 58 shape.draw_circleif shape.method_defined? 'draw_cicle' 59 shape.draw_rectangleif shape.method_defined? 'draw_rectangle' 60 end From this code it is quite obvious that the function has to know what kind of shape it is drawing. (Notice the ugly conditional statements)
6. Liskov Substitution Principle cond. A little more subtle violation of LSP. You would expect that a rectangle should act as the methods and properties given, even if it is a derived class. In this case the test would fail, hence breaking the contract of the parent. 62 # more subtle violation 63 class Rectangle 64 def height value 65 @height = value 66 end 67 def width value 68 @width = value 69 end 70 end 71 72 class Square < Rectangle 73 def height value 74 @heigth = @width = value 75 end 76 def width value 77 height = value 78 end 79 end 80 81 describe Square do 82 it "should not violate the contract of it's parent" do 83 rectangle = Square.new 84 rectangle.height = 4 85 rectangle.width = 2 86 area = rectangle.height * rectangle.width 87 area.should be 8 88 end 89 end
7. Interface Segregation Principle CLIENTS SHOULD NOT BE FORCED TO DEPEND UPON INTERFACES THAT THEY DO NOT USE This principle deals with the disadvantages of “fat” interfaces. Classes that have “fat” interfaces are classes whose interfaces are not cohesive. In other words, the interfaces of the class can be broken up into groups of member functions 95 class Door 96 def open 97 end 98 def close 99 end100 def is_open?101 end102 end103 104 class TimedDoor < Door105 def door_time_out?106 end107 end108 109 class TimerClient110 def time_outid111 end 112 end113 114 class DoorTimerAdapter < TimerClient115 def initialize timed_door116 @its_timed_door = timed_door117 end118 def time_out id119 @its_timed_door.door_time_out id120 end121 end
8. Dependency Inversion Principle HIGH LEVEL MODULES SHOULD NEVER DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND ON ABSTRACTIONS ABSTRACTIONS SHOULD NEVER DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTIONS # A simple violation where button is directly dependent on lamp.129 class Lamp130 def turn_on!131 end132 def turn_off!133 end134 end135 136 class Button137 def initialize lamp138 @its_lamp = lamp139 end140 def detect!141 button_on = get_state142 @its_lamp.turn_on! if button_on143 @its_lamp.turn_off! unless button_on144 end145 end
9. Dependency Inversion Principle 146 # A solution to that problem...147 module ButtonClient148 def turn_on!149 def turn_off!150 end151 152 class Button153 def initialize button_client154 @its_client = button_client155 end156 def detect157 button_on = get_state158 @its_client.turn_on! if button_on159 @its_client.turn_off! unless button_on160 end161 def get_state162 end163 end164 165 class Lamp < ButtonClient166 def turn_on!167 end168 def turn_off!169 end170 end171 172 class ButtonImplementation < Button173 def initialize button_client174 end175 def get_state176 end177 end