├── .idea ├── design_patterns_in_ruby.iml ├── encodings.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── README ├── behavioral ├── chain_of_responsibility.rb ├── command1.rb ├── command2.rb ├── domain_function.rb ├── interpreter.rb ├── iterator1.rb ├── iterator2.rb ├── iterator3.rb ├── iterator4.rb ├── mediator.rb ├── memento.rb ├── observer1.rb ├── observer2.rb ├── observer3.rb ├── state.rb ├── strategy1.rb ├── strategy2.rb ├── template_method1.rb ├── template_method2.rb ├── template_method3.rb ├── visitor1.rb └── visitor2.rb ├── creational ├── abstract-factory.rb ├── builder1.rb ├── builder2.rb ├── factory-method.rb ├── lazy-initialization.rb ├── prototype.rb ├── singleton1.rb ├── singleton2.rb └── singleton3.rb ├── enterprise ├── data-access-object.rb ├── dci1.rb ├── dci2.rb ├── dsl11.rb ├── dsl12.rb ├── dsl13.rb ├── dsl2.rb ├── map-reduce.rb └── mvc.rb ├── notes.txt └── structural ├── adapter.rb ├── bridge.rb ├── composite.rb ├── decorator1.rb ├── decorator2.rb ├── decorator3.rb ├── facade.rb ├── flyweight.rb ├── proxy1.rb └── proxy2.rb /.idea/design_patterns_in_ruby.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | 11 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 95 | 96 | 97 | 100 | 101 | 102 | 103 | 104 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 154 | 155 | 156 | 157 | 160 | 161 | 164 | 165 | 166 | 167 | 170 | 171 | 174 | 175 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 376 | 377 | 378 | 379 | 1281993996010 380 | 1281993996010 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 436 | 437 | 438 | 439 | 440 | file://$PROJECT_DIR$/enterprise/dsl4.rb 441 | 263 442 | 443 | 444 | file://$PROJECT_DIR$/../../ui-r16/src/app/models/banner_mapping.rb 445 | 16 446 | 447 | 448 | file://$PROJECT_DIR$/../../ui-r16/src/vendor/plugins/triton/app/helpers/triton/display_banner_helper.rb 449 | 12 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Implementation of Design Patterns in Ruby 2 | -------------------------------------------------------------------------------- /behavioral/chain_of_responsibility.rb: -------------------------------------------------------------------------------- 1 | # chain-of-responsibility.rb 2 | 3 | # Avoid coulpling the sender of a request to it's receiver by giving more than 4 | # one object a chance to handle the request. Chains the receiving object and passes 5 | # the request along the chain until an object handles it. 6 | 7 | # 1. type interface 8 | 9 | class Chain 10 | def handle 11 | end 12 | 13 | def initialize(next_in_chain) 14 | @next_in_chain = next_in_chain 15 | end 16 | end 17 | 18 | # 2. type implementation 19 | 20 | class MyChain < Chain 21 | def initialize(name, next_in_chain=nil) 22 | super(next_in_chain) 23 | 24 | @name = name 25 | end 26 | 27 | def handle 28 | puts "Handling by " + @name + "." 29 | 30 | if(@next_in_chain != nil) 31 | @next_in_chain.handle 32 | end 33 | end 34 | end 35 | 36 | # 3. test 37 | 38 | chain = MyChain.new("chain1", MyChain.new("chain2", MyChain.new("chain3", MyChain.new("chain5", MyChain.new("chain4"))))) 39 | 40 | chain.handle 41 | 42 | -------------------------------------------------------------------------------- /behavioral/command1.rb: -------------------------------------------------------------------------------- 1 | # command1.rb 2 | 3 | # Encapsulate a request as an object, thereby letting you to parametrize 4 | # clients with different requests, queue or log requests, and support 5 | # undoable operations. 6 | 7 | # 1. command abstraction 8 | 9 | class Command 10 | def execute() 11 | end 12 | end 13 | 14 | # 2. command implementations 15 | 16 | class MyCommand1 < Command 17 | def execute 18 | puts "command1" 19 | end 20 | end 21 | 22 | class MyCommand2 < Command 23 | def execute 24 | puts "command2" 25 | end 26 | end 27 | 28 | class MyCommand3 < Command 29 | def execute 30 | puts "command3" 31 | end 32 | end 33 | 34 | 35 | # 3. test 36 | 37 | command1 = MyCommand1.new 38 | command2 = MyCommand2.new 39 | command3 = MyCommand3.new 40 | 41 | command1.execute 42 | command2.execute 43 | command3.execute 44 | 45 | 46 | -------------------------------------------------------------------------------- /behavioral/command2.rb: -------------------------------------------------------------------------------- 1 | # command2.rb 2 | 3 | # We use blocks for command implementation. 4 | 5 | # 1. class that uses commands-blocks 6 | 7 | class CommandUser 8 | def initialize(&command) 9 | @command = command 10 | end 11 | 12 | def operation() 13 | @command.call if @command != nil 14 | end 15 | end 16 | 17 | 18 | # 2. test 19 | 20 | command_user = CommandUser.new do 21 | puts "command1" 22 | end 23 | 24 | command_user.operation -------------------------------------------------------------------------------- /behavioral/domain_function.rb: -------------------------------------------------------------------------------- 1 | # domain-function.bsh 2 | # better than visitor 3 | # http://blogs.concedere.net:8080/blog/discipline/java/?permalink=Domain-Function-Pattern.html 4 | 5 | class Employee 6 | def initialize(type, name) 7 | @type = type 8 | @name = name 9 | end 10 | 11 | def type 12 | @type 13 | end 14 | 15 | def to_s 16 | @name 17 | end 18 | end 19 | 20 | 21 | class EmployeeDomainFunction 22 | def call(employee) 23 | end 24 | end 25 | 26 | class VacationPlanner < EmployeeDomainFunction 27 | def call(employee) 28 | if(employee.type == :contractor) 29 | puts "Vacation planning for Contractor: " + 0.to_s 30 | elsif(employee.type == :executive) 31 | puts "Vacation planning for Executive: " + 90.to_s 32 | elsif(employee.type == :manager) 33 | puts "Vacation planning for Manager: " + 45.to_s 34 | elsif(employee.type == :secretary) 35 | puts "Vacation planning for Secretary: " + 15.to_s 36 | elsif(employee.type == :engineer) 37 | puts "Vacation planning for Engineer: " + 10.to_s 38 | else 39 | raise "unknown employee type: " + employee.type 40 | end 41 | 42 | end 43 | end 44 | 45 | 46 | vacation_planner = VacationPlanner.new 47 | 48 | employees = [] 49 | 50 | employees << Employee.new(:contractor, "contractor 1") 51 | employees << Employee.new(:contractor, "contractor 2") 52 | employees << Employee.new(:executive, "executive 1") 53 | employees << Employee.new(:manager, "manager 1") 54 | employees << Employee.new(:secretary, "secretary 1") 55 | employees << Employee.new(:engineer, "engineer 1") 56 | employees << Employee.new(:engineer, "engineer 2") 57 | 58 | puts "employees: " + employees.join(', ') 59 | 60 | puts "Vacation planning:" 61 | 62 | employees.each do |employee| 63 | vacation_planner.call(employee) 64 | end 65 | 66 | -------------------------------------------------------------------------------- /behavioral/interpreter.rb: -------------------------------------------------------------------------------- 1 | # interpreter.rb 2 | 3 | # Given a language, defines a representation for it's grammar along with an interpreter 4 | # that uses the representation to interpret sentences in the language 5 | 6 | # 1. context 7 | 8 | class NamesInterpreterContext 9 | def initialize 10 | @names = [] 11 | 12 | @names << "monitor" 13 | @names << "keyboard" 14 | @names << "mouse" 15 | @names << "system-block" 16 | end 17 | 18 | def names 19 | @names 20 | end 21 | end 22 | 23 | class NamesInterpreter 24 | def initialize(context) 25 | @context = context 26 | end 27 | 28 | # expression syntax: 29 | # show names 30 | def interpret(expression) 31 | result = '' 32 | 33 | tokens = expression.chomp.scan(/\(|\)|[\w\.\*]+/) # extract each word 34 | 35 | i = 0 36 | while i <= tokens.size do 37 | token = tokens[i] 38 | 39 | if(token != nil) 40 | if (token == 'show') 41 | token = tokens[i+1] 42 | i = i + 1 43 | 44 | if (token == 'names') 45 | result = result + @context.names.join(', ') 46 | else 47 | result = result + "error!" 48 | end 49 | else 50 | result = result + "error!" 51 | end 52 | end 53 | 54 | i = i + 1 55 | end 56 | 57 | result 58 | end 59 | end 60 | 61 | # test 62 | 63 | interpreter = NamesInterpreter.new(NamesInterpreterContext.new) 64 | 65 | puts "interpreting show names: " + interpreter.interpret("show names") 66 | -------------------------------------------------------------------------------- /behavioral/iterator1.rb: -------------------------------------------------------------------------------- 1 | # iterator1.rb 2 | 3 | # Provides a way to access the elements of an aggregate object sequentially without exposing 4 | # its underlying representation. 5 | 6 | # 1. iterator implementation (original collection is initialized internally) 7 | 8 | module Iterator 9 | def initialize() 10 | @array = [] 11 | end 12 | 13 | def each() 14 | copy = Array.new(@array) 15 | 16 | i = 0 17 | 18 | while i < copy.length 19 | if block_given? 20 | yield(copy[i]) 21 | end 22 | 23 | i += 1 24 | end 25 | end 26 | end 27 | 28 | class MyIterator 29 | include Iterator 30 | 31 | def initialize 32 | super 33 | 34 | @array = ['e1', 'e2', 'e3', 'e4'] 35 | end 36 | end 37 | 38 | 39 | # test 40 | 41 | iterator = MyIterator.new() 42 | 43 | iterator.each {|e| puts e.to_s} 44 | -------------------------------------------------------------------------------- /behavioral/iterator2.rb: -------------------------------------------------------------------------------- 1 | # iterator2.rb 2 | 3 | # 1. iterator implementation (original collection is initialized externally) 4 | 5 | class ExternalIterator 6 | def initialize(array) 7 | @array = Array.new(array) 8 | @index = 0 9 | end 10 | 11 | def has_next? 12 | @index < @array.length 13 | end 14 | 15 | def next 16 | value = @array[@index] 17 | 18 | @index += 1 19 | 20 | value 21 | end 22 | 23 | def current_element 24 | @array[@index] 25 | end 26 | end 27 | 28 | # 2. test 29 | 30 | array = ['e1', 'e2', 'e3', 'e4'] 31 | 32 | iterator = ExternalIterator.new(array) 33 | 34 | while iterator.has_next? 35 | puts iterator.next.to_s 36 | end 37 | -------------------------------------------------------------------------------- /behavioral/iterator3.rb: -------------------------------------------------------------------------------- 1 | # iterator3.rb 2 | 3 | # Iterator with typical ruby implementation 4 | 5 | # 1. iterator implementation 6 | 7 | class MyEnumerable 8 | def for_each(array) 9 | i = 0 10 | while i < array.length 11 | yield(array[i]) 12 | i+=1 13 | end 14 | end 15 | end 16 | 17 | # 2. test 18 | 19 | array = ['e1', 'e2', 'e3', 'e4'] 20 | 21 | my_enumerable = MyEnumerable.new 22 | 23 | my_enumerable.for_each(array) do |e| 24 | puts e 25 | end 26 | 27 | -------------------------------------------------------------------------------- /behavioral/iterator4.rb: -------------------------------------------------------------------------------- 1 | # iterator4.rb 2 | 3 | # Iterator with help of Enumerable 4 | 5 | # 1. iterator implementation 6 | 7 | class MyEnumerable 8 | include Enumerable 9 | 10 | def initialize 11 | @array = [] 12 | end 13 | 14 | def each(&block) 15 | @array.each(&block) 16 | end 17 | 18 | def <<(element) 19 | @array << element 20 | end 21 | end 22 | 23 | 24 | # 2. implementation of collection's element 25 | 26 | class MyElement 27 | attr_accessor :name, :value 28 | 29 | def initialize(name, value) 30 | @name = name 31 | @value = value 32 | end 33 | 34 | # this method is important for Enumerable 35 | def <=>(other) 36 | value <=> other.value 37 | end 38 | 39 | def to_s 40 | "[name: #{name}, value: #{value}]" 41 | end 42 | end 43 | 44 | 45 | # 3. test 46 | 47 | my_enumerable = MyEnumerable.new 48 | 49 | el = MyElement.new("n2", 2001) 50 | 51 | my_enumerable << MyElement.new("n1", 1) 52 | my_enumerable << el 53 | my_enumerable << MyElement.new("n3", 22) 54 | my_enumerable << MyElement.new("n4", 64) 55 | 56 | puts "Has n2: " + my_enumerable.include?(el).to_s 57 | 58 | puts "All elements have value >= 10: " + (my_enumerable.all? {|e| e.value >= 10}).to_s 59 | 60 | puts "Has any element with value >= 2000: " + (my_enumerable.any? {|e| e.value > 2000}).to_s 61 | 62 | puts "Display collection: " 63 | 64 | my_enumerable.each do |e| 65 | puts e 66 | end 67 | 68 | -------------------------------------------------------------------------------- /behavioral/mediator.rb: -------------------------------------------------------------------------------- 1 | # mediator.bsh 2 | 3 | # Define an object that encapsulates how a set of objects interact. 4 | # Promotes loose coupling by keeping objects from referring to each other 5 | # explicitly and it lets you vary their interactions independently. 6 | 7 | # 1. mediator interface 8 | 9 | class Mediator 10 | def operation1 11 | end 12 | 13 | def operation2 14 | end 15 | end 16 | 17 | # 2. Mediator implementation 18 | 19 | class MyMediator < Mediator 20 | def operation1 21 | puts "mediator: operation 1" 22 | end 23 | 24 | def operation2 25 | puts "mediator: operation 2" 26 | end 27 | end 28 | 29 | # 3. Concrete classes that are aware of mediator 30 | 31 | class Product1 32 | def initialize(mediator) 33 | @mediator = mediator 34 | end 35 | 36 | def perform 37 | @mediator.operation1 38 | end 39 | end 40 | 41 | class Product2 42 | def initialize(mediator) 43 | @mediator = mediator 44 | end 45 | 46 | def perform 47 | @mediator.operation1 48 | end 49 | end 50 | 51 | class Product3 52 | def initialize(mediator) 53 | @mediator = mediator 54 | end 55 | 56 | def perform 57 | @mediator.operation2 58 | end 59 | end 60 | 61 | # 4. test 62 | 63 | mediator = MyMediator.new 64 | 65 | product11 = Product1.new(mediator) 66 | product12 = Product1.new(mediator) 67 | 68 | product21 = Product2.new(mediator) 69 | product22 = Product2.new(mediator) 70 | 71 | product31 = Product3.new(mediator) 72 | product32 = Product3.new(mediator) 73 | 74 | product11.perform 75 | product12.perform 76 | 77 | product21.perform 78 | product21.perform 79 | 80 | product31.perform 81 | product31.perform 82 | 83 | -------------------------------------------------------------------------------- /behavioral/memento.rb: -------------------------------------------------------------------------------- 1 | # memento.bsh 2 | 3 | # Without violating encapsulation, capture and externalize an object's internal state 4 | # so that the object can be restored to this state later. 5 | 6 | # 1. This class don't want to reveal it's internal state and delegates presistance work to memento 7 | # object. This object should have access to private properties of enclosed class. 8 | 9 | class Memento 10 | def initialize(originator, memento_state) 11 | @originator = originator 12 | @memento_state = memento_state 13 | end 14 | 15 | def restore_memento 16 | @originator.state = @memento_state 17 | end 18 | end 19 | 20 | class Originator 21 | def initialize 22 | @state = nil 23 | end 24 | 25 | def state=(state) 26 | @state = state 27 | end 28 | 29 | def state_to_s 30 | @state.to_s 31 | end 32 | 33 | def create_memento 34 | Memento.new(self, @state) 35 | end 36 | 37 | def restore_memento(m) 38 | m.restore_memento 39 | end 40 | end 41 | 42 | 43 | # 2. Caretaker holds previously creted mementos: storage 44 | 45 | class Caretaker 46 | def initialize 47 | @saved_states = [] 48 | end 49 | 50 | def add_memento(memento) 51 | @saved_states << memento 52 | end 53 | 54 | def [](index) 55 | @saved_states[index] 56 | end 57 | end 58 | 59 | # 3. test 60 | 61 | caretaker = Caretaker.new 62 | originator = Originator.new 63 | 64 | originator.state = "State1" 65 | puts "step1: " + originator.state_to_s 66 | 67 | originator.state = "State2" 68 | puts "step2: " + originator.state_to_s 69 | 70 | caretaker.add_memento(originator.create_memento()) 71 | puts "step3: " + originator.state_to_s 72 | 73 | originator.state = "State3" 74 | puts "step4: "+ originator.state_to_s 75 | 76 | caretaker.add_memento(originator.create_memento()) 77 | puts "step5: " + originator.state_to_s 78 | 79 | originator.state = "State4" 80 | puts "step6: " + originator.state_to_s 81 | 82 | originator.restore_memento(caretaker[1]) 83 | puts "step7: " + originator.state_to_s 84 | -------------------------------------------------------------------------------- /behavioral/observer1.rb: -------------------------------------------------------------------------------- 1 | # observer1.rb 2 | 3 | # Define a one-to-many dependency between objects so that when one object 4 | # changes state, all it's dependents are notified and updated automatically. 5 | 6 | # 1. observer 7 | 8 | class Observer 9 | def initialize(name) 10 | @name = name 11 | end 12 | 13 | def update(value) 14 | puts "updated " + @name + ". New value: " + value 15 | end 16 | end 17 | 18 | # 2. Observable (serves as a container for observers and takes care of notifying them) 19 | 20 | module Observable 21 | def initialize 22 | @observers = [] 23 | end 24 | 25 | def <<(observer) 26 | @observers << observer 27 | end 28 | 29 | def >>(observer) 30 | @observers.delete(observer) 31 | end 32 | 33 | protected 34 | 35 | def notify_observers(value) 36 | @observers.clone.each {|observer| observer.update(value) } 37 | end 38 | end 39 | 40 | # 3. Implementation of the observer 41 | class MyObservable 42 | include Observable 43 | 44 | attr_reader :my_property 45 | 46 | def my_property=(my_property) 47 | @my_property = my_property 48 | 49 | notify_observers(my_property) 50 | end 51 | end 52 | 53 | observer1 = Observer.new("n1") 54 | observer2 = Observer.new("n2") 55 | observer3 = Observer.new("n3") 56 | 57 | my_observable = MyObservable.new 58 | 59 | my_observable << observer1 60 | my_observable << observer2 61 | my_observable << observer3 62 | 63 | my_observable.my_property = 'red' 64 | 65 | puts 'Deleting observer 3 ----------' 66 | 67 | my_observable >> observer3 68 | 69 | my_observable.my_property = 'green' 70 | -------------------------------------------------------------------------------- /behavioral/observer2.rb: -------------------------------------------------------------------------------- 1 | # observer2.rb 2 | 3 | # Slight modification of previous example with metaprogramming to wrap methods 4 | # that needs to be observed. 5 | 6 | # 1. observer 7 | 8 | class Observer 9 | def initialize(name) 10 | @name = name 11 | end 12 | 13 | def update(value) 14 | puts "updated " + @name + ". New value: " + value 15 | end 16 | end 17 | 18 | # 2. Observable, serves as a container for observers and takes care of notifying them 19 | 20 | module ObservableClassMethods 21 | def act_as_observable *list 22 | list.each do |observable| 23 | method_name = "#{observable.to_s}=" 24 | no_callback_method_name = "no_callback_#{observable.to_s}=" 25 | 26 | alias_method no_callback_method_name, method_name 27 | 28 | define_method method_name do |value| 29 | send no_callback_method_name, value 30 | 31 | notify_observers(value) 32 | end 33 | end 34 | end 35 | end 36 | 37 | module Observable 38 | def self.included(base) 39 | base.extend(ObservableClassMethods) 40 | end 41 | 42 | def initialize 43 | @observers = [] 44 | end 45 | 46 | def <<(observer) 47 | @observers << observer 48 | end 49 | 50 | def >>(observer) 51 | @observers.delete(observer) 52 | end 53 | 54 | protected 55 | 56 | def notify_observers(value) 57 | @observers.clone.each do |observer| 58 | observer.update(value) if observer.kind_of? Observer 59 | observer.call(value) if observer.kind_of? Proc 60 | end 61 | end 62 | end 63 | 64 | # 3. Implementation of the observer 65 | class MyObservable 66 | include Observable 67 | 68 | attr_accessor :my_property 69 | 70 | # def my_property=(my_property) 71 | # @my_property = my_property 72 | # 73 | # notify_observers(my_property) 74 | # end 75 | act_as_observable :my_property 76 | end 77 | 78 | # 4. test 79 | 80 | observer1 = Observer.new("n1") 81 | observer2 = Observer.new("n2") 82 | observer3 = Observer.new("n3") 83 | observer4 = lambda { |value| puts "updated from lambda (update is :#{value})" } 84 | 85 | my_observable = MyObservable.new 86 | 87 | my_observable << observer1 88 | my_observable << observer2 89 | my_observable << observer3 90 | my_observable << observer4 91 | 92 | my_observable.my_property = 'red' 93 | 94 | puts 'Deleting observer 3 ----------' 95 | 96 | my_observable >> observer3 97 | 98 | my_observable.my_property = 'green' 99 | -------------------------------------------------------------------------------- /behavioral/observer3.rb: -------------------------------------------------------------------------------- 1 | # observer3.rb 2 | 3 | # Implements Observer pattern with the help of ruby observer library 4 | 5 | require 'observer' 6 | 7 | # 1. observer 8 | 9 | class Observer 10 | def initialize(name) 11 | @name = name 12 | end 13 | 14 | def update(value) 15 | puts "updated " + @name + ". New value: " + value 16 | end 17 | end 18 | 19 | # 2. Observable is imported from 'observer' module 20 | 21 | # 3. Implementation of the observer 22 | 23 | class MyObservable 24 | include Observable 25 | 26 | def my_property= my_property 27 | @my_property = my_property 28 | 29 | changed # specific to ruby Observable library 30 | notify_observers(my_property) 31 | end 32 | end 33 | 34 | # 4. test 35 | 36 | observer1 = Observer.new("n1") 37 | observer2 = Observer.new("n2") 38 | observer3 = Observer.new("n3") 39 | 40 | observer4 = Proc.new {} 41 | 42 | observer4.instance_eval do 43 | def update(value) 44 | puts "updated from lambda (update is :#{value})" 45 | end 46 | end 47 | 48 | my_observable = MyObservable.new 49 | 50 | my_observable.add_observer observer1 51 | my_observable.add_observer observer2 52 | my_observable.add_observer observer3 53 | my_observable.add_observer observer4 54 | 55 | my_observable.my_property = 'red' 56 | 57 | puts 'Deleting observer 3 ----------' 58 | 59 | my_observable.delete_observer observer3 60 | 61 | my_observable.my_property = 'green' 62 | -------------------------------------------------------------------------------- /behavioral/state.rb: -------------------------------------------------------------------------------- 1 | # states.bsh 2 | 3 | # Allow an object to alter it's behavior when it's internal state changes. 4 | # The object will appear to change it's class. 5 | 6 | # in ruby you can achieve same goal with metaprogramming 7 | 8 | # 1. state type and it's implementations 9 | 10 | class State 11 | def handle 12 | end 13 | end 14 | 15 | class MyState1 < State 16 | def handle 17 | puts "handle1" 18 | end 19 | end 20 | 21 | class MyState2 < State 22 | def handle 23 | puts "handle2" 24 | end 25 | end 26 | 27 | # 2. context's type and it's implementation 28 | 29 | class Context 30 | def set_state(state) 31 | end 32 | 33 | def request 34 | end 35 | end 36 | 37 | class MyContext < Context 38 | def state=(state) 39 | @state = state 40 | end 41 | 42 | def request 43 | @state.handle 44 | end 45 | end 46 | 47 | # 3. test 48 | 49 | context = MyContext.new 50 | 51 | state1 = MyState1.new 52 | state2 = MyState2.new 53 | 54 | context.state = state1 55 | context.request 56 | 57 | context.state = state2 58 | context.request 59 | -------------------------------------------------------------------------------- /behavioral/strategy1.rb: -------------------------------------------------------------------------------- 1 | # strategy1.rb 2 | 3 | # Defines a family of algorithms, encapsulate each one and make them interchangeable. 4 | # Let the algorithm vart independently from clients that use it. 5 | 6 | # 1. strategy implementations 7 | 8 | class MyStrategy1 9 | def operation 10 | puts "operation1" 11 | end 12 | end 13 | 14 | class MyStrategy2 15 | def operation 16 | puts "operation2" 17 | end 18 | end 19 | 20 | # 2. strategy context 21 | 22 | class StrategyContext 23 | attr_writer :strategy 24 | 25 | def execute 26 | @strategy.operation 27 | end 28 | end 29 | 30 | # 3. test 31 | 32 | context = StrategyContext.new 33 | 34 | strategy1 = MyStrategy1.new 35 | strategy2 = MyStrategy2.new 36 | 37 | context.strategy = strategy1 38 | context.execute 39 | 40 | context.strategy = strategy2 41 | context.execute 42 | -------------------------------------------------------------------------------- /behavioral/strategy2.rb: -------------------------------------------------------------------------------- 1 | # strategy2.rb 2 | 3 | # Defines strategy on the fly with the help of ruby blocks. 4 | 5 | # 1. strategy context 6 | 7 | class StrategyContext 8 | 9 | def execute &strategy 10 | strategy.call(self) 11 | end 12 | end 13 | 14 | # 2. test 15 | 16 | context = StrategyContext.new 17 | 18 | context.execute do |context| 19 | # strategy 1 implementation 20 | puts "operation1 from #{context}" 21 | end 22 | 23 | context.execute do |context| 24 | # strategy 2 implementation 25 | puts "operation2 from #{context}" 26 | end 27 | -------------------------------------------------------------------------------- /behavioral/template_method1.rb: -------------------------------------------------------------------------------- 1 | # template_method1.rb 2 | 3 | # Defines the skeleton of an algorithm in an operation, deferring some steps 4 | # to subclasses. Let subclasses redefine certain steps of an algorithm without 5 | # changing the algorithm's structure. 6 | 7 | # 1. template 8 | 9 | module AlgorithmTemplate 10 | # These methods are "primitive operations" and must be overridden in the concrete templates 11 | def step1 12 | raise 'Called abstract method: step1' 13 | end 14 | 15 | def step2 16 | raise 'Called abstract method: step2' 17 | end 18 | 19 | def step3 20 | raise 'Called abstract method: step3' 21 | end 22 | 23 | # The "template method" - calls the concrete class methods, is not overridden 24 | def some_template_method 25 | step1 26 | step2 27 | step3 28 | end 29 | end 30 | 31 | # 2. adding template behavior 32 | 33 | class MyAlgorithmTemplate1 34 | include AlgorithmTemplate 35 | 36 | protected 37 | 38 | def step1 39 | puts "algorithm1: step1" 40 | end 41 | 42 | def step2 43 | puts "algorithm1: step2" 44 | end 45 | 46 | def step3 47 | puts "algorithm1: step3" 48 | end 49 | end 50 | 51 | class MyAlgorithmTemplate2 52 | include AlgorithmTemplate 53 | 54 | protected 55 | 56 | def step1 57 | puts "algorithm2: step1" 58 | end 59 | 60 | def step2 61 | puts "algorithm2: step2" 62 | end 63 | 64 | def step3 65 | puts "algorithm2: step3" 66 | end 67 | end 68 | 69 | # test 70 | 71 | template1 = MyAlgorithmTemplate1.new 72 | template2 = MyAlgorithmTemplate2.new 73 | 74 | template1.some_template_method 75 | template2.some_template_method 76 | -------------------------------------------------------------------------------- /behavioral/template_method2.rb: -------------------------------------------------------------------------------- 1 | # template_method2.rb 2 | 3 | # This example is slightly different from previous implementation in the way of handling abstract methods. 4 | # We extend ruby Module class to support "abstract_methods" meta-method. 5 | 6 | # ruby language extension 7 | class Module 8 | 9 | # Invoked during class definitions 10 | def abstract_methods(*names) 11 | names.each do | name | 12 | define_method "#{name}" do 13 | raise 'Called abstract method: ' + name.to_s 14 | end 15 | end 16 | end 17 | 18 | end 19 | 20 | # 1. template 21 | 22 | module AlgorithmTemplate 23 | # These methods are "primitive operations" and must be overridden in the concrete templates 24 | abstract_methods :step1, :step2, :step3 25 | 26 | # The "template method" - calls the concrete class methods, is not overridden 27 | def some_template_method 28 | step1 29 | step2 30 | step3 31 | end 32 | end 33 | 34 | # 2. adding template behavior 35 | 36 | class MyAlgorithmTemplate1 37 | include AlgorithmTemplate 38 | 39 | protected 40 | 41 | def step1 42 | puts "algorithm1: step1" 43 | end 44 | 45 | def step2 46 | puts "algorithm1: step2" 47 | end 48 | 49 | def step3 50 | puts "algorithm1: step3" 51 | end 52 | end 53 | 54 | class MyAlgorithmTemplate2 55 | include AlgorithmTemplate 56 | 57 | protected 58 | 59 | def step1 60 | puts "algorithm2: step1" 61 | end 62 | 63 | def step2 64 | puts "algorithm2: step2" 65 | end 66 | 67 | def step3 68 | puts "algorithm2: step3" 69 | end 70 | end 71 | 72 | # test 73 | 74 | template1 = MyAlgorithmTemplate1.new 75 | template2 = MyAlgorithmTemplate2.new 76 | 77 | template1.some_template_method 78 | template2.some_template_method 79 | -------------------------------------------------------------------------------- /behavioral/template_method3.rb: -------------------------------------------------------------------------------- 1 | # template-method3.bsh 2 | 3 | # This example uses Strategy Pattern for storing steps of template method algorithm. 4 | 5 | # 1. Strategy 6 | 7 | class TemplateMethodStep 8 | def initialize &code 9 | @code = code 10 | end 11 | 12 | def operation 13 | @code.call 14 | end 15 | end 16 | 17 | # 2. template method algorithm; also acts as strategy context 18 | 19 | module AlgorithmTemplate 20 | def initialize 21 | @steps = [] # strategies 22 | end 23 | 24 | # The "template method" 25 | def some_template_method 26 | @steps.each { | step | step.operation } 27 | end 28 | end 29 | 30 | class MyAlgorithmTemplate1 31 | include AlgorithmTemplate 32 | 33 | def initialize 34 | super 35 | 36 | @steps << TemplateMethodStep.new { puts "stategy1: step1" } 37 | @steps << TemplateMethodStep.new { puts "stategy1: step2" } 38 | @steps << TemplateMethodStep.new { puts "stategy1: step3" } 39 | end 40 | end 41 | 42 | class MyAlgorithmTemplate2 43 | include AlgorithmTemplate 44 | 45 | def initialize 46 | super 47 | 48 | @steps << TemplateMethodStep.new { puts "stategy2: step1" } 49 | @steps << TemplateMethodStep.new { puts "stategy2: step2" } 50 | @steps << TemplateMethodStep.new { puts "stategy2: step3" } 51 | end 52 | end 53 | 54 | 55 | # test 56 | 57 | template1 = MyAlgorithmTemplate1.new 58 | template2 = MyAlgorithmTemplate2.new 59 | 60 | template1.some_template_method 61 | template2.some_template_method 62 | 63 | -------------------------------------------------------------------------------- /behavioral/visitor1.rb: -------------------------------------------------------------------------------- 1 | # visitor1.rb 2 | 3 | # Represents an operation to be performed on the elements of an object structure. 4 | # Lets you define a new operation without changing the classes of the elements 5 | # on which it operates. 6 | 7 | # 1. visitable and visitor interfaces 8 | 9 | 10 | def underscore(camel_cased_word) 11 | camel_cased_word.to_s.gsub(/::/, '/'). 12 | gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2'). 13 | gsub(/([a-z\d])([A-Z])/, '\1_\2'). 14 | tr("-", "_"). 15 | downcase 16 | end 17 | 18 | module Visitable 19 | def accept(visitor) 20 | visitor.visit(self) 21 | end 22 | end 23 | 24 | module Visitor 25 | def visit(visitable) 26 | end 27 | end 28 | 29 | # 2. type implementation with visitable behavior 30 | 31 | # basic parts 32 | 33 | class MyVisitable1 34 | include Visitable 35 | end 36 | 37 | class MyVisitable2 38 | include Visitable 39 | end 40 | 41 | class MyVisitable3 42 | include Visitable 43 | end 44 | 45 | 46 | # compound 47 | 48 | class MyCompoundVisitable 49 | include Visitable 50 | 51 | def initialize 52 | @visitable1 = MyVisitable1.new 53 | @visitable2 = MyVisitable2.new 54 | 55 | @visitables3 = [ 56 | MyVisitable3.new, MyVisitable3.new, MyVisitable3.new 57 | ] 58 | end 59 | 60 | alias original_accept accept 61 | 62 | def accept(visitor) 63 | original_accept visitor 64 | 65 | # takes care of components 66 | @visitable1.accept(visitor) 67 | @visitable2.accept(visitor) 68 | 69 | @visitables3.each { |visitable| visitable.accept(visitor) } 70 | end 71 | end 72 | 73 | # 3. visitor implementations 74 | 75 | class MyCompoundVisitor1 76 | include Visitor 77 | 78 | def visit(visitable) 79 | method_name = "visit_#{underscore(visitable.class.name)}" 80 | 81 | send method_name.to_sym, visitable 82 | end 83 | 84 | def visit_my_visitable1 visitable 85 | puts "visitor: visiting my visitable 11" 86 | end 87 | 88 | def visit_my_visitable2 visitable 89 | puts "visitor: visiting my visible 12" 90 | end 91 | 92 | def visit_my_visitable3 visitable 93 | puts "visitor: visiting my visitable 13" 94 | end 95 | 96 | def visit_my_compound_visitable visitable 97 | puts "visitor: visiting my compound visitable 1" 98 | end 99 | end 100 | 101 | # 4. test 102 | 103 | # creating complex visitable 104 | 105 | visitable = MyCompoundVisitable.new 106 | 107 | visitor1 = MyCompoundVisitor1.new 108 | 109 | visitable.accept(visitor1) 110 | 111 | # creating visitor dynamically 112 | 113 | class MyCompoundVisitor2 114 | include Visitor 115 | def visit visitable 116 | if(visitable.kind_of? MyVisitable1) 117 | puts "visitor: visiting my visitable 21" 118 | elsif(visitable.kind_of? MyVisitable2) 119 | puts "visitor: visiting my visible 22" 120 | elsif(visitable.kind_of? MyVisitable3) 121 | puts "visitor: visiting my visitable 23" 122 | elsif(visitable.kind_of? MyCompoundVisitable) 123 | puts "visitor: visiting my compound visitable 2" 124 | end 125 | end 126 | end 127 | 128 | visitor2 = MyCompoundVisitor2.new 129 | 130 | visitable.accept(visitor2) 131 | 132 | -------------------------------------------------------------------------------- /behavioral/visitor2.rb: -------------------------------------------------------------------------------- 1 | # visitor2.rb 2 | 3 | # Instead of using separate visitor class this example uses blocks of code. 4 | 5 | # 1. type interface 6 | 7 | module Visitable 8 | def accept(&visitor_code) 9 | visitor_code.call(self) 10 | end 11 | end 12 | 13 | # we don't need visitor here: use code fragment instead 14 | 15 | #class Visitor 16 | # def visit(visitable) 17 | # end 18 | #end 19 | 20 | 21 | # 2. type implementation with visitable behavior 22 | 23 | # basic parts 24 | 25 | class MyVisitable1 26 | include Visitable 27 | end 28 | 29 | class MyVisitable2 30 | include Visitable 31 | end 32 | 33 | class MyVisitable3 34 | include Visitable 35 | end 36 | 37 | # compound 38 | 39 | class MyCompoundVisitable 40 | include Visitable 41 | 42 | def initialize 43 | @visitable1 = MyVisitable1.new 44 | @visitable2 = MyVisitable2.new 45 | 46 | @visitables3 = [ 47 | MyVisitable3.new, MyVisitable3.new, MyVisitable3.new 48 | ] 49 | end 50 | 51 | def accept(&visitor_code) 52 | visitor_code.call(self) 53 | #visitor.visit(self) 54 | 55 | # takes care of components 56 | @visitable1.accept(&visitor_code) 57 | @visitable2.accept(&visitor_code) 58 | 59 | @visitables3.each { |visitable| visitable.accept(&visitor_code) } 60 | end 61 | end 62 | 63 | # 3. visitor implementations 64 | 65 | # 4. test 66 | 67 | visitable = MyCompoundVisitable.new 68 | 69 | # creating visitor dynamically 70 | 71 | visitor_code = Proc.new do |visitable| 72 | if(visitable.kind_of? MyVisitable1) 73 | puts "visitor: visiting my visitable 1" 74 | elsif(visitable.kind_of? MyVisitable2) 75 | puts "visitor: visiting my visible 2" 76 | elsif(visitable.kind_of? MyVisitable3) 77 | puts "visitor: visiting my visitable 3" 78 | elsif(visitable.kind_of? MyCompoundVisitable) 79 | puts "visitor: visiting my compound visitable" 80 | end 81 | end 82 | 83 | visitable.accept &visitor_code 84 | -------------------------------------------------------------------------------- /creational/abstract-factory.rb: -------------------------------------------------------------------------------- 1 | # abstract-factory.rb 2 | 3 | # Provides an interface for creationg families of related or dependent objects without specifying 4 | # their concrete class. 5 | # 6 | # Provides one level of interface higher than the Factory Method pattern. 7 | # It is used to return one of the several factories. 8 | 9 | # 1. common interface for type and it's specializations 10 | 11 | class Alive 12 | def live 13 | end 14 | end 15 | 16 | class Plant < Alive 17 | end 18 | 19 | class Animal < Alive 20 | end 21 | 22 | # 2. implementations of specializations 23 | 24 | class Roze < Plant 25 | def live 26 | puts "roze" 27 | end 28 | end 29 | 30 | class Elephant < Animal 31 | def live 32 | puts "elephant" 33 | end 34 | end 35 | 36 | # 3. interface for factory (abstract factory) 37 | 38 | class AliveFactory 39 | def create_alive 40 | end 41 | end 42 | 43 | # 4. different factories implementations 44 | 45 | class PlantFactory < AliveFactory 46 | def create_alive 47 | Roze.new 48 | end 49 | end 50 | 51 | class AnimalFactory < AliveFactory 52 | def create_alive 53 | Elephant.new 54 | end 55 | end 56 | 57 | class DynamicAliveFactory 58 | def initialize(alive_class) 59 | @alive_class = alive_class 60 | end 61 | 62 | def create_alive 63 | @alive_class.new 64 | end 65 | end 66 | 67 | # 5. factories manager (optional) 68 | 69 | class AliveManager 70 | def create_alive_factory(type) 71 | if(type == :plant) 72 | PlantFactory.new 73 | elsif(type == :animal) 74 | AnimalFactory.new 75 | end 76 | end 77 | end 78 | 79 | # 6. test 80 | 81 | alive_manager = AliveManager.new 82 | 83 | alive_factory1 = alive_manager.create_alive_factory(:plant) 84 | alive_factory2 = alive_manager.create_alive_factory(:animal) 85 | alive_factory3 = DynamicAliveFactory.new(Roze) 86 | 87 | alive1 = alive_factory1.create_alive 88 | alive2 = alive_factory2.create_alive 89 | alive3 = alive_factory3.create_alive 90 | 91 | alive1.live 92 | alive2.live 93 | alive3.live 94 | -------------------------------------------------------------------------------- /creational/builder1.rb: -------------------------------------------------------------------------------- 1 | # builder1.rb 2 | 3 | # Separates the construction of a complex object from its representation so that the same 4 | # construction process can create different representations. 5 | 6 | # 1. type builder interface 7 | 8 | class ProductBuilder 9 | def build_part1 10 | end 11 | 12 | def build_part2 13 | end 14 | 15 | def build_part3 16 | end 17 | 18 | end 19 | 20 | # 2. type builder implementations 21 | 22 | class ComputerBuilder < ProductBuilder 23 | def build_part1 24 | puts "Building part1: motherboard" 25 | end 26 | 27 | def build_part2 28 | puts "Building part2: CPU" 29 | end 30 | 31 | def build_part3 32 | puts "Building part3: display" 33 | end 34 | end 35 | 36 | class TableBuilder < ProductBuilder 37 | def build_part1 38 | puts "Building part1: legs" 39 | end 40 | 41 | def build_part2 42 | puts "Building part2: top" 43 | end 44 | 45 | def build_part3 46 | puts "Building part3: mounting" 47 | end 48 | end 49 | 50 | # 3. director 51 | 52 | class Director 53 | def construct(builder) 54 | builder.build_part1 55 | builder.build_part2 56 | builder.build_part3 57 | end 58 | end 59 | 60 | # 4. test 61 | 62 | director = Director.new 63 | 64 | director.construct(ComputerBuilder.new) 65 | director.construct(TableBuilder.new) 66 | 67 | -------------------------------------------------------------------------------- /creational/builder2.rb: -------------------------------------------------------------------------------- 1 | # builder2.rb 2 | 3 | # This example uses ruby "method_missing" to implement so-called "magic method" 4 | 5 | # 1. type builder interface 6 | 7 | class ProductBuilder 8 | def build_part1 9 | end 10 | 11 | def build_part2 12 | end 13 | 14 | def build_part3 15 | end 16 | 17 | def method_missing(name, *args) 18 | words = name.to_s.split("_") 19 | 20 | return super(name, *args) unless words.shift == 'build' 21 | 22 | words.each do |word| 23 | next if word == 'and' 24 | 25 | build_part1 if word == 'part1' 26 | build_part2 if word == 'part2' 27 | build_part3 if word == 'part3' 28 | end 29 | end 30 | 31 | end 32 | 33 | # 2. type builder implementations 34 | 35 | class ComputerBuilder < ProductBuilder 36 | def build_part1 37 | puts "Building part1: motherboard" 38 | end 39 | 40 | def build_part2 41 | puts "Building part2: CPU" 42 | end 43 | 44 | def build_part3 45 | puts "Building part3: display" 46 | end 47 | end 48 | 49 | class TableBuilder < ProductBuilder 50 | def build_part1 51 | puts "Building part1: legs" 52 | end 53 | 54 | def build_part2 55 | puts "Building part2: top" 56 | end 57 | 58 | def build_part3 59 | puts "Building part3: mounting" 60 | end 61 | end 62 | 63 | # 3. director 64 | 65 | class Director 66 | def construct_with_magic(builder) 67 | builder.build_part1_and_part2_and_part3 68 | end 69 | end 70 | 71 | # 4. test 72 | 73 | director = Director.new 74 | 75 | puts "with magic:" 76 | director.construct_with_magic(ComputerBuilder.new) 77 | director.construct_with_magic(TableBuilder.new) -------------------------------------------------------------------------------- /creational/factory-method.rb: -------------------------------------------------------------------------------- 1 | # factory-method.rb 2 | 3 | # Defines an interface for creating an object, but let subclasses decide which 4 | # class to instantiate. Lets a class to defer instantiation to sublcasses. 5 | 6 | # 1. common type interface 7 | 8 | class Shape 9 | def draw 10 | end 11 | end 12 | 13 | # 2. implementations of common type interface 14 | 15 | class Line < Shape 16 | def draw 17 | puts "line" 18 | end 19 | end 20 | 21 | class Square < Shape 22 | def draw 23 | puts "square" 24 | end 25 | end 26 | 27 | # 3. Creator class for various types 28 | 29 | class ShapeCreator 30 | 31 | # factory methods 32 | 33 | def create_line 34 | Line.new 35 | end 36 | 37 | def create_square 38 | Square.new 39 | end 40 | 41 | # or universal factory method (parameterized factory method) 42 | 43 | def create_shape(type) 44 | if(type == :line) 45 | Line.new 46 | elsif(type== :square) 47 | Square.new 48 | end 49 | end 50 | 51 | end 52 | 53 | # 4. test 54 | 55 | shape_creator = ShapeCreator.new 56 | 57 | shape1 = shape_creator.create_line 58 | shape2 = shape_creator.create_square 59 | shape3 = shape_creator.create_shape :line 60 | 61 | shape1.draw 62 | shape2.draw 63 | shape3.draw 64 | 65 | -------------------------------------------------------------------------------- /creational/lazy-initialization.rb: -------------------------------------------------------------------------------- 1 | # lazy-initialization.rb 2 | 3 | # Lazy initialization is the tactic of delaying the creation of an object, 4 | # the calculation of a value, or some other expensive process until 5 | # the first time it is needed. 6 | 7 | class Item 8 | def operation 9 | puts "executing operation..." 10 | end 11 | end 12 | 13 | class ItemProxy < Item 14 | def self.creational_block(&creation_block) 15 | @@creation_block = creation_block 16 | end 17 | 18 | def operation 19 | @subject.operation 20 | end 21 | 22 | def self.subject 23 | if (@subject == nil) 24 | puts "Creating new instance" 25 | else 26 | puts "Using existing instance" 27 | end 28 | 29 | @subject || (@subject = @@creation_block.call) 30 | end 31 | end 32 | 33 | 34 | # test 35 | 36 | ItemProxy.creational_block { Item.new } 37 | 38 | item1 = ItemProxy.subject 39 | 40 | item2 = ItemProxy.subject 41 | 42 | item3 = ItemProxy.subject 43 | 44 | item1.operation 45 | item2.operation 46 | item3.operation 47 | -------------------------------------------------------------------------------- /creational/prototype.rb: -------------------------------------------------------------------------------- 1 | # prototype.rb 2 | 3 | # Specify the kind of objects to create using prototypical instance, 4 | # and create new objects by copying this prototype. 5 | 6 | class Prototype 7 | def make_prototype 8 | end 9 | end 10 | 11 | class Product < Prototype 12 | attr_accessor :name 13 | 14 | def initialize 15 | @name = "name" 16 | end 17 | 18 | def make_prototype 19 | cloned_product = Product.new 20 | 21 | cloned_product.name = @name 22 | 23 | cloned_product 24 | end 25 | 26 | def to_s 27 | super + ", name: #{@name}" 28 | end 29 | end 30 | 31 | # test 32 | 33 | original_product = Product.new 34 | 35 | cloned_product = original_product.make_prototype 36 | 37 | puts "original product: " + original_product.to_s 38 | puts "cloned product : " + cloned_product.to_s 39 | 40 | -------------------------------------------------------------------------------- /creational/singleton1.rb: -------------------------------------------------------------------------------- 1 | # singleton1.rb 2 | 3 | # Ensure a class only has one instance and provide a global point of access to it. 4 | 5 | class MySingleton 6 | @@instance = nil 7 | 8 | def self.instance() 9 | if @@instance == nil 10 | public_class_method :new # enables instance creation for this class 11 | 12 | @@instance = new() unless @@instance 13 | 14 | private_class_method :new # disable instance creation for this class 15 | end 16 | 17 | @@instance 18 | end 19 | 20 | private_class_method :new # disable instance creation for this class 21 | end 22 | 23 | 24 | # MySingleton1.new # error 25 | 26 | puts "singleton: " + MySingleton.instance.to_s 27 | puts "singleton: " + MySingleton.instance.to_s 28 | -------------------------------------------------------------------------------- /creational/singleton2.rb: -------------------------------------------------------------------------------- 1 | # singleton2.rb 2 | 3 | # This example does the same qith the help of 'singleton' library. 4 | 5 | require 'singleton' 6 | 7 | class MySingleton 8 | include Singleton 9 | end 10 | 11 | puts "singleton: " + MySingleton.instance.to_s 12 | puts "singleton: " + MySingleton.instance.to_s -------------------------------------------------------------------------------- /creational/singleton3.rb: -------------------------------------------------------------------------------- 1 | # singleton3.rb 2 | 3 | # Usually you have to avoid using Singeton, because it's not possible to mock (and consequently test) it. 4 | # 5 | # This example is trying to replace Singleton pattern with interface inheritance and dependency injection. 6 | # Now we can mock the singleton. 7 | 8 | # 1. Define singleton. 9 | 10 | class Singleton 11 | def operation 12 | end 13 | end 14 | 15 | # 2. Singleton implementation. 16 | 17 | class MySingleton < Singleton 18 | def operation 19 | puts "operation" 20 | end 21 | end 22 | 23 | # 3. Class that uses the singleton object (we should be able to set it up from outside). 24 | 25 | class SingletonUser 26 | def initialize(singleton) 27 | @singleton = singleton 28 | end 29 | 30 | def do_something 31 | @singleton.operation 32 | end 33 | 34 | end 35 | 36 | # 4. Create mock object for the singleton. 37 | 38 | class MockSingleton < Singleton 39 | 40 | def operation 41 | puts "mock operation" 42 | end 43 | 44 | end 45 | 46 | 47 | # 5. test 48 | 49 | class SingletonTest 50 | def test_singleton 51 | singleton = MockSingleton.new 52 | 53 | singleton_user = SingletonUser.new(singleton) 54 | 55 | singleton_user.do_something 56 | 57 | # assertions 58 | end 59 | end 60 | 61 | tester = SingletonTest.new 62 | 63 | tester.test_singleton 64 | 65 | -------------------------------------------------------------------------------- /enterprise/data-access-object.rb: -------------------------------------------------------------------------------- 1 | # data-access-object.bsh 2 | 3 | # 1. 4 | 5 | class TransferObject 6 | def data(key) 7 | end 8 | 9 | def set_data(key, value) 10 | end 11 | end 12 | 13 | class TransferObjectDAO 14 | def set_data_source(dataSource) 15 | end 16 | 17 | def create 18 | end 19 | 20 | def update(transfer_object) 21 | end 22 | 23 | def delete(transfer_object) 24 | end 25 | 26 | def find(id) 27 | end 28 | 29 | def find_all 30 | end 31 | end 32 | 33 | class DataSource 34 | def generate_next_id 35 | end 36 | 37 | def execute_command(command, param) 38 | end 39 | end 40 | 41 | class TransferObjectDAOFactory 42 | def transfer_object_dao 43 | end 44 | end 45 | 46 | # 2. 47 | 48 | class MyTransferObject < TransferObject 49 | def initialize 50 | @data = {} 51 | end 52 | 53 | def data(key) 54 | @data[key] 55 | end 56 | 57 | def set_data(key, value) 58 | @data[key] = value 59 | end 60 | 61 | def to_s 62 | @data.to_s 63 | end 64 | end 65 | 66 | class MyDataSource < DataSource 67 | def initialize 68 | @generated_id = 0 69 | 70 | @objects = [] 71 | end 72 | 73 | def generate_next_id 74 | @generated_id = @generated_id + 1 75 | end 76 | 77 | def execute_command(command, param=nil) 78 | result = nil 79 | 80 | if(command == :insert) 81 | result = (@objects << param) 82 | elsif(command == :update) 83 | index = @objects.index(param) 84 | @objects.delete_at(index) 85 | @objects.insert(index, param) 86 | 87 | result = true 88 | elsif(command == :delete) 89 | result = (@objects.delete(param)) 90 | elsif(command == :find) 91 | id = param 92 | 93 | @objects.each do |transfer_object| 94 | current_id = transfer_object.data("id") 95 | 96 | if(current_id == id) 97 | result = transfer_object 98 | 99 | break 100 | end 101 | end 102 | elsif(command == :find_all) 103 | result = @objects 104 | end 105 | 106 | result 107 | end 108 | end 109 | 110 | class MyTransferObjectDAO < TransferObjectDAO 111 | 112 | def data_source=(data_source) 113 | @data_source = data_source 114 | end 115 | 116 | def create 117 | transfer_object = MyTransferObject.new 118 | 119 | id = @data_source.generate_next_id 120 | 121 | transfer_object.set_data("id", id) 122 | 123 | @data_source.execute_command(:insert, transfer_object) 124 | 125 | id 126 | end 127 | 128 | def update(transfer_object) 129 | @data_source.execute_command(:update, transfer_object) 130 | end 131 | 132 | def delete(transfer_object) 133 | @data_source.execute_command(:delete, transfer_object) 134 | end 135 | 136 | def find(id) 137 | @data_source.execute_command(:find, id) 138 | end 139 | 140 | def find_all 141 | @data_source.execute_command(:find_all) 142 | end 143 | end 144 | 145 | class MyTransferObjectDAOFactory < TransferObjectDAOFactory 146 | 147 | def initialize(data_source) 148 | @data_source = data_source 149 | end 150 | 151 | def transfer_object_dao 152 | transfer_object_dao = MyTransferObjectDAO.new 153 | 154 | transfer_object_dao.data_source = @data_source 155 | 156 | transfer_object_dao 157 | end 158 | end 159 | 160 | 161 | # test - or business object, or client 162 | 163 | dataSource = MyDataSource.new 164 | factory = MyTransferObjectDAOFactory.new(dataSource) 165 | 166 | dao = factory.transfer_object_dao 167 | 168 | # create a new domain object 169 | new_id = dao.create 170 | 171 | # Find a domain object. 172 | o1 = dao.find(new_id) 173 | 174 | puts "o1: " + o1.to_s 175 | 176 | # modify the values in the Transfer Object. 177 | o1.set_data("address", "a1") 178 | o1.set_data("email", "em1") 179 | 180 | # update the domain object using the DAO 181 | dao.update(o1) 182 | 183 | puts "o1: " + o1.to_s 184 | 185 | list = dao.find_all 186 | 187 | puts "list before delete: " + list.join(', ') 188 | 189 | # delete a domain object 190 | dao.delete(o1) 191 | 192 | puts "list after delete: " + list.join(', ') 193 | -------------------------------------------------------------------------------- /enterprise/dci1.rb: -------------------------------------------------------------------------------- 1 | # dci1.rb 2 | # Data-Context-Interaction pattern 3 | 4 | # The code for separate use case in OO is typically spread out between lots of differen classes. 5 | # To understand how the code works, you must also know the relationships between objects in runtime. 6 | # These relationships aren't set in code, they depend on the situation. 7 | # What DCI proposes is that code for a given use-case is separated out from the classes and put into 8 | # a different artifact called context. Objects of different classes can enter into a relationship in this 9 | # context and take part in interaction where they have different roles. 10 | 11 | # Suggestions: 12 | # 13 | # a) keep core model classes very thin 14 | # b) keep additional logic/behaviour in roles 15 | # c) do not do class oriented programming, do object oriented programming 16 | 17 | # 1. Data 18 | class DataObject # thin data/model 19 | attr_accessor :basic_property 20 | end 21 | 22 | # 2. Roles: broken into pieces functionality 23 | module Role1 24 | attr_accessor :role1_property 25 | end 26 | 27 | module Role2 28 | attr_accessor :role2_property 29 | end 30 | 31 | # 3. The place where role is assigned to data/model. 32 | # We do it dynamically at run-time, on object, not class declaration level. 33 | 34 | class Context # or use case 35 | def initialize data 36 | @data = data 37 | end 38 | 39 | def execute 40 | p @data.basic_property # OK 41 | 42 | begin 43 | @data.role1_property 44 | rescue 45 | p "Error: role1_property is not available yet" 46 | end 47 | 48 | @data.extend Role1 49 | 50 | @data.role1_property = "test_role1" 51 | p @data.role1_property 52 | end 53 | end 54 | 55 | class UseCase # or context 56 | def initialize data 57 | @data = data 58 | end 59 | 60 | def execute 61 | p @data.basic_property # OK 62 | 63 | begin 64 | @data.role2_property 65 | rescue 66 | p "Error: role2_property is not available yet" 67 | end 68 | 69 | @data.extend Role2 70 | 71 | @data.role2_property = "test_role2" 72 | p @data.role2_property 73 | end 74 | end 75 | 76 | # 4. Actual Program 77 | 78 | class Interaction 79 | def initialize 80 | @data = DataObject.new 81 | @data.basic_property = "test_basic" 82 | end 83 | 84 | def interact 85 | context = Context.new @data 86 | context.execute 87 | 88 | use_case = UseCase.new @data 89 | use_case.execute 90 | end 91 | end 92 | 93 | interaction = Interaction.new 94 | interaction.interact -------------------------------------------------------------------------------- /enterprise/dci2.rb: -------------------------------------------------------------------------------- 1 | # dci2.rb 2 | # Data-Context-Interaction pattern 3 | 4 | require 'rubygems' 5 | require 'mixology' 6 | 7 | # 1. Data (or model) 8 | class DataObject # thin data/model 9 | attr_accessor :basic_property 10 | end 11 | 12 | # 2. Roles: broken into pieces functionality 13 | module Role1 14 | attr_accessor :role1_property 15 | end 16 | 17 | module Role2 18 | attr_accessor :role2_property 19 | end 20 | 21 | # 3. The place where role is assigned to data/model. 22 | # We do it dynamically at run-time, on object, not class declaration level. 23 | 24 | class Context # or use case (or controller) 25 | def initialize data 26 | @data = data 27 | end 28 | 29 | def execute 30 | p @data.basic_property # OK 31 | 32 | begin 33 | @data.role1_property 34 | rescue 35 | p "Error: role1_property is not available yet" 36 | end 37 | 38 | @data.mixin Role1 39 | 40 | @data.role1_property = "test_role1" 41 | p @data.role1_property 42 | 43 | @data.unmix Role1 44 | 45 | begin 46 | @data.role1_property 47 | rescue 48 | p "Error: role1_property is not available again" 49 | end 50 | 51 | end 52 | end 53 | 54 | class UseCase # or context 55 | def initialize data 56 | @data = data 57 | end 58 | 59 | def execute 60 | p @data.basic_property # OK 61 | 62 | begin 63 | @data.role2_property 64 | rescue 65 | p "Error: role2_property is not available yet" 66 | end 67 | 68 | @data.mixin Role2 69 | 70 | @data.role2_property = "test_role2" 71 | p @data.role2_property 72 | 73 | @data.unmix Role2 74 | 75 | begin 76 | @data.role2_property 77 | rescue 78 | p "Error: role2_property is not available again" 79 | end 80 | end 81 | end 82 | 83 | # 4. Actual Program 84 | 85 | class Interaction 86 | def initialize 87 | @data = DataObject.new 88 | @data.basic_property = "test_basic" 89 | end 90 | 91 | def interact 92 | context = Context.new @data 93 | context.execute 94 | 95 | use_case = UseCase.new @data 96 | use_case.execute 97 | end 98 | end 99 | 100 | interaction = Interaction.new 101 | interaction.interact 102 | -------------------------------------------------------------------------------- /enterprise/dsl11.rb: -------------------------------------------------------------------------------- 1 | # dsl11.rb 2 | 3 | # Domain Specific Language (DSL) is simple set of commands for serving a specific domain. 4 | # It could be implemented on object or class level. With object level we've got DSL scripts, with 5 | # the class level - meta-programming statements 6 | 7 | # This implementation uses object level. 8 | 9 | # 1. Defines DSL elements: tree, trunk, branch, root, leaf. 10 | # If one element appears inside another, use collection and don't forget to eval 11 | # incoming block for the parent. 12 | 13 | module HasName 14 | attr_reader :name 15 | 16 | def initialize(name = '') 17 | @name = name 18 | end 19 | 20 | def to_s 21 | "[name: #{name}]" 22 | end 23 | end 24 | 25 | class Leaf 26 | include HasName 27 | 28 | def to_s 29 | "Leaf[name: #{name}]" 30 | end 31 | end 32 | 33 | class Branch 34 | include HasName 35 | 36 | attr_reader :leaves 37 | 38 | def initialize name 39 | super 40 | 41 | @leaves = [] 42 | end 43 | 44 | def leaf(name, &block) 45 | leaf = Leaf.new(name) 46 | leaf.instance_eval(&block) if block_given? 47 | 48 | @leaves << leaf 49 | 50 | leaf 51 | end 52 | 53 | def to_s 54 | "Branch[name: #{name}; leaves: #{leaves.join(', ')}]" 55 | end 56 | end 57 | 58 | class Root 59 | include HasName 60 | 61 | def to_s 62 | "Root[name: #{name}]" 63 | end 64 | end 65 | 66 | class Trunk 67 | include HasName 68 | 69 | attr_reader :branches, :roots 70 | 71 | def initialize 72 | super 73 | 74 | @branches = [] 75 | @roots = [] 76 | end 77 | 78 | def branch(name, &block) 79 | branch = Branch.new(name) 80 | branch.instance_eval(&block) if block_given? 81 | 82 | @branches << branch 83 | 84 | branch 85 | end 86 | 87 | def root(name, &block) 88 | root = Root.new(name) 89 | root.instance_eval(&block) if block_given? 90 | 91 | @roots << root 92 | 93 | root 94 | end 95 | 96 | def to_s 97 | " Trunk[\n" + 98 | " branches: #{branches.join(', ')};\n" + 99 | " roots: #{roots.join(', ')};\n" + 100 | " ]" 101 | end 102 | end 103 | 104 | class Tree 105 | include HasName 106 | 107 | def initialize name 108 | super 109 | @trunk = Trunk.new 110 | end 111 | 112 | def type(type) 113 | @type = type 114 | end 115 | 116 | def trunk(&block) 117 | @trunk.instance_eval(&block) 118 | end 119 | 120 | def to_s 121 | "Tree [\n name: #{name};\n type: #{@type};\n trunk: #{@trunk}\n]" 122 | end 123 | end 124 | 125 | # 2. Defines DSL for describing the tree. It acts as the tree builder. 126 | 127 | module Tree::DSL 128 | def tree(name, &block) 129 | tree = Tree.new(name) 130 | tree.instance_eval(&block) 131 | 132 | tree 133 | end 134 | end 135 | 136 | # 3. test 137 | 138 | include Tree::DSL 139 | 140 | # tree description 141 | 142 | tree :my_favorite_tree do 143 | type :oak 144 | 145 | trunk do 146 | branch :b1 do 147 | leaf :l1 148 | leaf :l2 149 | end 150 | 151 | branch :b2 do 152 | leaf :l3 153 | end 154 | 155 | root :r1 156 | root :r2 157 | end 158 | 159 | puts self # prints the tree 160 | end 161 | 162 | -------------------------------------------------------------------------------- /enterprise/dsl12.rb: -------------------------------------------------------------------------------- 1 | # dsl12.rb 2 | 3 | # This is another implementation of DSL with the help of DSL build. 4 | 5 | module Kernel 6 | def singleton_class 7 | class << self; 8 | self; 9 | end 10 | end 11 | 12 | def define_attribute(name, value=nil) 13 | unless instance_variable_defined?("@#{name}".to_sym) 14 | singleton_class.send :attr_accessor, name 15 | 16 | send "#{name}=".to_sym, value unless value.nil? 17 | end 18 | 19 | instance_variable_get "@#{name}".to_sym 20 | end 21 | 22 | def constantize name 23 | Kernel.const_get(name.to_s.capitalize) 24 | end 25 | end 26 | 27 | 28 | module Visitable 29 | def accept(level, &visitor_code) 30 | visitor_code.call(level, self) 31 | end 32 | end 33 | 34 | module HasName 35 | attr_accessor :name 36 | end 37 | 38 | # 1. Defines DSL elements: tree, trunk, branch, root, leaf. No need to specify 39 | # relationship between them - we'll do it later with DSL builder. 40 | 41 | class TreeElement 42 | include HasName 43 | end 44 | 45 | class Leaf < TreeElement 46 | end 47 | 48 | class Branch < TreeElement 49 | end 50 | 51 | class Root < TreeElement 52 | end 53 | 54 | class Trunk < TreeElement 55 | end 56 | 57 | class Tree < TreeElement 58 | def type(type=nil) 59 | @type = type.nil? ? @type : type 60 | end 61 | end 62 | 63 | 64 | # 2. DSL Builder 65 | 66 | class DSLBuilder 67 | def initialize &block 68 | @nodes = [] 69 | @records = [] 70 | 71 | instance_eval(&block) 72 | end 73 | 74 | def parent 75 | @parent 76 | end 77 | 78 | def add_node node 79 | @nodes << node unless @nodes.include? node 80 | end 81 | 82 | def parent parent 83 | @parent = parent 84 | add_node(parent) 85 | 86 | @records << {:root => parent} 87 | 88 | parent_class = constantize(parent) 89 | 90 | parent_class.instance_eval do 91 | define_method parent do |name, &block | 92 | instance = parent_class.new(name) 93 | 94 | instance.instance_eval(&block) 95 | end 96 | end 97 | end 98 | 99 | def child parent, child 100 | add_node(parent) 101 | add_node(child) 102 | 103 | @records << {:parent => parent, :child => child} 104 | 105 | parent_class = constantize(parent) 106 | child_class = constantize(child) 107 | 108 | parent_class.instance_eval do 109 | define_method child do | &block | 110 | child = define_attribute(child, child_class.new) 111 | 112 | child.instance_eval(&block) unless block.nil? 113 | end 114 | end 115 | end 116 | 117 | def children parent, child 118 | add_node(parent) 119 | add_node(child) 120 | 121 | @records << {:parent => parent, :children => child} 122 | 123 | parent_class = constantize(parent) 124 | child_class = constantize(child) 125 | 126 | parent_class.instance_eval do 127 | define_method child do |name, &block | 128 | instance = child_class.new 129 | instance.name = name 130 | 131 | children = define_attribute("#{child}_list".to_sym, []) 132 | 133 | children << instance 134 | 135 | instance.instance_eval(&block) unless block.nil? 136 | end 137 | end 138 | end 139 | 140 | def build name 141 | language_class = Class.new 142 | language = language_class.new 143 | language.define_attribute(:parent, @parent) 144 | language.define_attribute(:name, name) 145 | 146 | def language.program(name, &block) 147 | instance = constantize(@parent).new 148 | instance.name = name 149 | 150 | instance.instance_eval(&block) 151 | 152 | instance 153 | end 154 | 155 | assign_visitors 156 | 157 | language 158 | end 159 | 160 | private 161 | 162 | 163 | def assign_visitor_to element 164 | element_class = constantize(element) 165 | element_class.send :include, Visitable unless element_class.include? Visitable 166 | end 167 | 168 | def override_accept_method element, components_patch 169 | element_class = constantize(element) 170 | 171 | element_class.class_eval <<-CODE 172 | alias original_accept accept 173 | 174 | def accept(level, &visitor_code) 175 | original_accept level, &visitor_code 176 | 177 | # takes care of components 178 | #{components_patch} 179 | end 180 | CODE 181 | end 182 | 183 | def assign_visitors 184 | @nodes.each do |node| 185 | assign_visitor_to(node) 186 | 187 | children_records = @records.select { |record| record[:parent] == node && record[:children] != nil } 188 | 189 | if children_records.size > 0 190 | patch = "" 191 | children_records.each do |record| 192 | child = record[:children] 193 | 194 | patch += <<-CODE 195 | @#{child}_list.each do |visitable| 196 | visitable.accept(level+1, &visitor_code) 197 | end 198 | CODE 199 | end 200 | 201 | override_accept_method(node, patch) 202 | end 203 | 204 | child_records = @records.select { |record| record[:parent] == node && record[:child] != nil } 205 | 206 | if child_records.size > 0 207 | child = child_records[0][:child] 208 | 209 | patch = "@#{child}.accept(level+1, &visitor_code)" 210 | 211 | override_accept_method(node, patch) 212 | end 213 | end 214 | end 215 | end 216 | 217 | 218 | # 3. Creates new 'tree' language with the help of DSL builder. 219 | 220 | dsl_builder = DSLBuilder.new do 221 | parent :tree 222 | 223 | child :tree, :trunk 224 | children :trunk, :branch 225 | children :trunk, :root 226 | children :branch, :leaf 227 | end 228 | 229 | tree_language = dsl_builder.build :tree 230 | 231 | puts "language: #{tree_language.name}" 232 | 233 | # 4. Creates new tree description in 'tree' language. 234 | 235 | tree_program = tree_language.program :my_favorite_tree do 236 | type :oak 237 | 238 | trunk do 239 | branch :b1 do 240 | leaf :l1 241 | leaf :l2 242 | end 243 | 244 | branch :b2 do 245 | leaf :l3 246 | end 247 | 248 | root :r1 249 | root :r2 250 | end 251 | end 252 | 253 | # printing the program 254 | 255 | puts "program: #{tree_program.name}" 256 | puts "program body:" 257 | 258 | # visiting all program's elements 259 | 260 | 261 | tree_program.accept(1) do |level, visitable| 262 | spaces = " " * ((level-1)*2) 263 | if (visitable.kind_of? Tree) 264 | puts "#{spaces}Tree[name: #{visitable.name}; type: #{visitable.type}]" 265 | elsif (visitable.kind_of? Trunk) 266 | puts "#{spaces}Trunk" 267 | elsif (visitable.kind_of? Branch) 268 | puts "#{spaces}Branch[name: #{visitable.name}]" 269 | elsif (visitable.kind_of? Root) 270 | puts "#{spaces}Root[name: #{visitable.name}]" 271 | elsif (visitable.kind_of? Leaf) 272 | puts "#{spaces}Leaf[name: #{visitable.name}]" 273 | else 274 | puts "oops: #{visitable.class.name}" 275 | end 276 | end 277 | 278 | -------------------------------------------------------------------------------- /enterprise/dsl13.rb: -------------------------------------------------------------------------------- 1 | # http://www.skorks.com/2011/02/a-unit-testing-framework-in-44-lines-of-ruby/ 2 | 3 | module Kernel 4 | def describe(description, &block) 5 | tests = Dsl.new.parse(description, block) 6 | tests.execute 7 | end 8 | end 9 | 10 | class Object 11 | def should 12 | self 13 | end 14 | end 15 | 16 | class Dsl 17 | def initialize 18 | @tests = {} 19 | end 20 | def parse(description, block) 21 | self.instance_eval(&block) 22 | Executor.new(description, @tests) 23 | end 24 | def it(description, &block) 25 | @tests[description] = block 26 | end 27 | end 28 | 29 | class Executor 30 | def initialize(description, tests) 31 | @description = description 32 | @tests = tests 33 | @success_count = 0 34 | @failure_count = 0 35 | end 36 | def execute 37 | puts "#{@description}" 38 | @tests.each_pair do |name, block| 39 | print " - #{name}" 40 | result = self.instance_eval(&block) 41 | result ? @success_count += 1 : @failure_count += 1 42 | puts result ? " SUCCESS" : " FAILURE" 43 | end 44 | summary 45 | end 46 | def summary 47 | puts "\n#{@tests.keys.size} tests, #{@success_count} success, #{@failure_count} failure" 48 | end 49 | end 50 | 51 | describe "some test" do 52 | it "should be true" do 53 | true.should == true 54 | end 55 | 56 | it "should show that an expression can be true" do 57 | (5 == 5).should == true 58 | end 59 | 60 | it "should be failing deliberately" do 61 | 5.should == 6 62 | end 63 | end -------------------------------------------------------------------------------- /enterprise/dsl2.rb: -------------------------------------------------------------------------------- 1 | # dsl2.rb 2 | 3 | # This implementation uses class level. 4 | 5 | class Object 6 | def self.property_creator(*names) 7 | names.each do |name| 8 | class_eval <<-CODE 9 | def #{name} 10 | @#{name} 11 | end 12 | 13 | def #{name}=(val) 14 | @#{name} = val 15 | end 16 | CODE 17 | end 18 | end 19 | end 20 | 21 | class Bean 22 | property_creator :prop1, :prop2 23 | 24 | def initialize(prop1, prop2) 25 | @prop1 = prop1 26 | @prop2 = prop2 27 | end 28 | end 29 | 30 | bean = Bean.new('red', 'morning') 31 | 32 | puts bean.prop1 33 | puts bean.prop2 34 | -------------------------------------------------------------------------------- /enterprise/map-reduce.rb: -------------------------------------------------------------------------------- 1 | # map-reduce.bsh 2 | 3 | # http://www.theserverside.com/tt/knowledgecenter-tc/knowledgecenter-tc.tss?l=MapReduce 4 | # http://www.theserverside.com/tt/articles/article.tss?l=MapReduceRedux 5 | 6 | # MapReduce is a distributed programming model intended for processing massive amounts of data in large clusters, 7 | # developed by Jeffrey Dean and Sanjay Ghemawat at Google. The implementation of MapReduce separates the business 8 | # logic from the multi-processing logic. 9 | # 10 | # MapReduce is implemented as two functions, Map which applies a function to all the members of a collection and 11 | # returns a list of results based on that processing, and Reduce, which collates and resolves the results from 12 | # two or more Maps executed in parallel by multiple threads, processors, or stand-alone systems. Both Map() 13 | # and Reduce() may run in parallel, though not necessarily in the same system at the same time. 14 | 15 | # 1. functors interfaces 16 | 17 | class MapFunctor 18 | def function(object) 19 | end 20 | end 21 | 22 | class ReduceFunctor 23 | def function(list, object) 24 | end 25 | end 26 | 27 | # 2. map-reduce interface 28 | 29 | class MapReduce 30 | def map(map_functor, list) 31 | end 32 | 33 | def reduce(reduce_functor, list, init_list) 34 | end 35 | end 36 | 37 | 38 | # 3. implementations 39 | 40 | class MapFunctorImpl < MapFunctor 41 | def function(object) # copier 42 | object 43 | end 44 | end 45 | 46 | class ReduceFunctorImpl < ReduceFunctor 47 | 48 | def function(list, object) # duplication reducer 49 | if(!list.include? object) 50 | list << object 51 | end 52 | 53 | list 54 | end 55 | 56 | end 57 | 58 | class MapReduceImpl < MapReduce 59 | 60 | def map(map_functor, list) 61 | intermediate_result = [] 62 | 63 | list.each do |element| 64 | result = map_functor.function(element) 65 | 66 | intermediate_result << result 67 | end 68 | 69 | intermediate_result 70 | end 71 | 72 | def reduce(reduce_functor, list, init_list) 73 | result = init_list 74 | 75 | list.each do |value| 76 | result = reduce_functor.function(result, value) 77 | end 78 | 79 | result 80 | end 81 | 82 | end 83 | 84 | # 4. test 85 | 86 | # input data 87 | 88 | bucket1 = [ "1", "2", "3", "4", "5" ] 89 | 90 | bucket2 = [ "6", "4", "8", "5", "10" ] 91 | 92 | # Business logic is concentrated in functors 93 | 94 | map_functor = MapFunctorImpl.new 95 | reduce_functor = ReduceFunctorImpl.new 96 | 97 | # Different instances of map-reduce objects. They can be used for "map" or "reduce" operations. 98 | 99 | map_reduce1 = MapReduceImpl.new 100 | map_reduce2 = MapReduceImpl.new 101 | map_reduce3 = MapReduceImpl.new 102 | 103 | intermediate_data1 = map_reduce1.map(map_functor, bucket1) 104 | intermediate_data2 = map_reduce2.map(map_functor, bucket2) 105 | 106 | final_data = map_reduce3.reduce(reduce_functor, intermediate_data2, intermediate_data1) 107 | 108 | puts "bucket1: " + bucket1.join(', ') 109 | puts "bucket2: " + bucket2.join(', ') 110 | 111 | puts "intermediate data 1: " + intermediate_data1.join(', ') 112 | puts "intermediate data 2: " + intermediate_data2.join(', ') 113 | 114 | puts "final data: " + final_data.join(', ') 115 | -------------------------------------------------------------------------------- /enterprise/mvc.rb: -------------------------------------------------------------------------------- 1 | # mvc.bsh 2 | 3 | # 1. observer and observable interfaces (see Observer pattern) 4 | 5 | class Observer 6 | def update(key, value) 7 | end 8 | end 9 | 10 | class Observable # see Observer pattern 11 | def add_observer(observer) 12 | end 13 | 14 | def remove_observer(observer) 15 | end 16 | 17 | def notifyObservers(key, value) 18 | end 19 | end 20 | 21 | # 2. Model, Controller-Mediator-Observer, View 22 | 23 | class Model 24 | def value(key) 25 | end 26 | 27 | def set_data(key, value) 28 | end 29 | 30 | def get_observable 31 | end 32 | end 33 | 34 | class Controller < Observer 35 | # Binds a model to this controller. Once added, the controller will listen for all 36 | # model property changes and propogate them on to registered views. In addition, 37 | # it is also responsible for resetting the model properties when a view changes state. 38 | def add_model(model) 39 | end 40 | 41 | def remove_model(model) 42 | end 43 | 44 | # Binds a view to this controller. The controller will propogate all model property 45 | # changes to each view for consideration. 46 | def add_view(view) 47 | end 48 | 49 | def remove_view(view) 50 | end 51 | 52 | def models 53 | end 54 | 55 | def views 56 | end 57 | 58 | def operation1(value) 59 | end 60 | 61 | def operation2(value) 62 | end 63 | end 64 | 65 | class View < Observer 66 | def set_controller(controller) 67 | end 68 | end 69 | 70 | # 3. Implementation of observable 71 | 72 | class AbstractObservable < Observable 73 | def initialize 74 | @observers = [] 75 | end 76 | 77 | def add_observer(observer) 78 | @observers << observer 79 | end 80 | 81 | def removeObserver(observer) 82 | @observers.delete(observer) 83 | end 84 | 85 | def notify_observers(key, value) 86 | @observers.clone().each { |observer| observer.update(key, value) } 87 | end 88 | end 89 | 90 | # 4. implementations 91 | 92 | class MyModel < Model 93 | def initialize 94 | @observable = AbstractObservable.new 95 | @data = {} 96 | end 97 | 98 | def observable 99 | @observable 100 | end 101 | 102 | # model implementation 103 | 104 | def value(key) 105 | @data[key] 106 | end 107 | 108 | def set_data(key, value) # mutator 109 | @data[key] = value 110 | 111 | @observable.notify_observers(key, value) # notify about state change 112 | end 113 | 114 | def to_s 115 | "model: " + @data.to_s 116 | end 117 | end 118 | 119 | # this controller mediates all changes in model and propagates them to 120 | # all registered views through update() method 121 | class AbstractController < Controller 122 | # controller behavior 123 | 124 | def initialize 125 | @models = [] 126 | @views = [] 127 | end 128 | 129 | def add_model(model) 130 | @models << model 131 | 132 | model.observable.add_observer(self) 133 | end 134 | 135 | def remove_model(model) 136 | @models.remove(model) 137 | 138 | model.observable.remove_observer(self) 139 | end 140 | 141 | def add_view(view) 142 | @views << view 143 | 144 | view.controller = self 145 | end 146 | 147 | def remove_view(view) 148 | @views.remove(view) 149 | end 150 | 151 | def models 152 | @models 153 | end 154 | 155 | def views 156 | @views 157 | end 158 | 159 | # observer behavior 160 | 161 | # This method represents changes model -> views 162 | 163 | # Use this to observe property changes from registered models 164 | # and propagate them on to all registered views. 165 | def update(key, value) 166 | @views.each { |view| view.update(key, value) } 167 | end 168 | 169 | # This method represents changes view -> models 170 | 171 | # Convienence method that subclasses can call upon to fire off property changes 172 | # back to the models. This method used reflection to inspect each of the model 173 | # classes to determine if it is the owner of the property in question. If it 174 | # isn't, a NoSuchMethodException is throws (which the method ignores). 175 | def set_model_property(key, value) 176 | @models.each { |model| model.set_data(key, value) } 177 | end 178 | end 179 | 180 | class MyView < View 181 | def controller=(controller) 182 | @controller = controller 183 | end 184 | 185 | def property1=(value) 186 | @property1 = value 187 | 188 | @controller.operation1(value) 189 | end 190 | 191 | def property2=(value) 192 | @property2 = value 193 | 194 | @controller.operation2(value) 195 | end 196 | 197 | def update(key, value) 198 | if(key == :property1) 199 | @property1 = value 200 | end 201 | 202 | if(key == :property2) 203 | @property2 = value 204 | end 205 | end 206 | 207 | def to_s 208 | "view[property1: " + @property1.to_s + "; property2: " + @property2.to_s + "]" 209 | end 210 | end 211 | 212 | class MyController < AbstractController 213 | # implementing Mediator 214 | 215 | def operation1(value) 216 | set_model_property(:property1, value) 217 | end 218 | 219 | def operation2(value) 220 | set_model_property(:property2, value) 221 | end 222 | end 223 | 224 | # 5. test 225 | 226 | def print_controller(controller) 227 | i = 1 228 | controller.models.each { |model| puts "model" + i.to_s + ": " + model.to_s; i = i+1 } 229 | 230 | i = 1 231 | controller.views.each { |view| puts "view" + i.to_s + ": " + view.to_s; i = i+1 } 232 | end 233 | 234 | controller1 = MyController.new 235 | 236 | model1 = MyModel.new 237 | 238 | view11 = MyView.new 239 | view12 = MyView.new 240 | 241 | controller1.add_model(model1) 242 | controller1.add_view(view11) 243 | controller1.add_view(view12) 244 | 245 | controller2 = MyController.new 246 | 247 | model2 = MyModel.new 248 | model3 = MyModel.new 249 | 250 | view21 = MyView.new 251 | view22 = MyView.new 252 | view23 = MyView.new 253 | 254 | controller2.add_model(model2) 255 | controller2.add_model(model3) 256 | controller2.add_view(view21) 257 | controller2.add_view(view22) 258 | controller2.add_view(view23) 259 | 260 | puts "1. changes in model1" 261 | 262 | model1.set_data(:property1, "555") 263 | 264 | print_controller(controller1) 265 | 266 | puts "2. changes in view12" 267 | 268 | view12.property2 = "777" 269 | 270 | print_controller(controller1) 271 | 272 | puts "3. changes in model3" 273 | 274 | model3.set_data(:property1, "111") 275 | 276 | print_controller(controller2) 277 | 278 | puts "4. changes in view23" 279 | 280 | view23.property1 = "222" 281 | view23.property2 = "333" 282 | 283 | print_controller(controller2) 284 | -------------------------------------------------------------------------------- /notes.txt: -------------------------------------------------------------------------------- 1 | Relations: 2 | 3 | 1. Decorator is Adapter with additional pre-, post- and around- code; 4 | 5 | 2. Factory Method is Template Method but applied to object creation instead of class creation. -------------------------------------------------------------------------------- /structural/adapter.rb: -------------------------------------------------------------------------------- 1 | # adapter.rb 2 | 3 | # Converst the interface of a class into another intrerface clients expect. 4 | # Lets classes work together that couldn't otherwise because of incompatible interfaces. 5 | 6 | # 1. existing layout 7 | 8 | class ClientItem 9 | def some_operation 10 | end 11 | end 12 | 13 | class MyClientItem < ClientItem 14 | def some_operation 15 | puts "some operation" 16 | end 17 | end 18 | 19 | class Client 20 | def process(item) 21 | item.some_operation 22 | end 23 | end 24 | 25 | # 2. class that creates conflict (adaptee): we cannot pass it in client's process() method. 26 | 27 | class OtherItem # adaptee 28 | def other_operation 29 | puts "other operation" 30 | end 31 | end 32 | 33 | # 3. adaptation 34 | 35 | class OtherItemAdapter < ClientItem # adapter 36 | def initialize 37 | @other_item = OtherItem.new 38 | end 39 | 40 | # actual adaptation 41 | def some_operation 42 | @other_item.other_operation 43 | end 44 | end 45 | 46 | # 4. test 47 | 48 | client = Client.new 49 | 50 | client.process(MyClientItem.new) # OK 51 | 52 | # client.process(OtherItem.new) # OtherItem does not conform CientItem protocol 53 | 54 | client.process(OtherItemAdapter.new) # OtherItem is adapted to CientItem protocol through adapter 55 | 56 | # here we adapt incompatible interface "on the fly" using ruby ability to dynamically 57 | # add missing method to the class. 58 | 59 | other_item = OtherItem.new 60 | 61 | class << other_item 62 | def some_operation 63 | puts "some operation for this instance (v1)" 64 | end 65 | end 66 | 67 | client.process(other_item) 68 | 69 | other_item2 = OtherItem.new 70 | 71 | def other_item2.some_operation 72 | puts "some operation for this instance (v2)" 73 | end 74 | 75 | client.process(other_item2) 76 | 77 | # or adding this method directly to the class by openning it: 78 | class OtherItem 79 | def some_operation 80 | puts "some operation for this instance (v3)" 81 | end 82 | end 83 | 84 | client.process(OtherItem.new) -------------------------------------------------------------------------------- /structural/bridge.rb: -------------------------------------------------------------------------------- 1 | # bridge.rb 2 | 3 | # Decouples an abstraction from it's implementation so that the two can vary independently. 4 | 5 | # 1. implementor 6 | 7 | class Material 8 | def make 9 | end 10 | end 11 | 12 | # 2. concrete implementors 13 | 14 | class MyMaterial1 < Material 15 | def make 16 | puts "make: my material 1" 17 | end 18 | end 19 | 20 | class MyMaterial2 < Material 21 | def make 22 | puts "make: my material 2" 23 | end 24 | end 25 | 26 | # 3. abstraction 27 | 28 | class Product 29 | def taste 30 | end 31 | end 32 | 33 | # 4. refined abstraction 34 | 35 | class MyProduct < Product 36 | def initialize(material) # injecting implementor 37 | @material = material 38 | end 39 | 40 | def taste 41 | @material.make 42 | end 43 | 44 | end 45 | 46 | # 5. test (bridge) 47 | 48 | material1 = MyMaterial1.new 49 | 50 | product1 = MyProduct.new(MyMaterial1.new) 51 | product2 = MyProduct.new(MyMaterial2.new) 52 | 53 | product1.taste 54 | product2.taste 55 | 56 | -------------------------------------------------------------------------------- /structural/composite.rb: -------------------------------------------------------------------------------- 1 | # composite.rb 2 | 3 | # Compose objects into tree structures to represents part-whole hierarchies. 4 | # Lets clients treat individual objects and compositions of objects uniformly. 5 | 6 | # 1. basic elements 7 | 8 | module Component 9 | def operation 10 | puts "component operation" 11 | end 12 | end 13 | 14 | module Composite 15 | def initialize 16 | @children = [] 17 | end 18 | 19 | def <<(component) 20 | @children << component 21 | end 22 | 23 | def >>(component) 24 | @children.delete(component) 25 | end 26 | 27 | def [](index) 28 | @children[index] 29 | end 30 | 31 | def []=(index, value) 32 | @children[index] = value 33 | end 34 | 35 | def operation 36 | puts "composite operation" 37 | 38 | @children.each {|child| child.operation} 39 | end 40 | 41 | def to_s 42 | "children: " + @children.join(', ') 43 | end 44 | end 45 | 46 | 47 | # 2. implementations 48 | 49 | class Leaf 50 | include Component 51 | 52 | def initialize(name) 53 | @name = name 54 | end 55 | 56 | def to_s 57 | "leaf[name: #{@name}]" 58 | end 59 | end 60 | 61 | class Tree 62 | include Component 63 | include Composite 64 | 65 | def initialize(name) 66 | super() 67 | @name = name 68 | end 69 | 70 | def to_s 71 | "tree[name: #{@name}; children: #{@children.join(', ')}]" 72 | end 73 | end 74 | 75 | # 3. test 76 | 77 | tree1 = Tree.new("t1") 78 | tree2 = Tree.new("t2") 79 | 80 | leaf1 = Leaf.new("l1") 81 | leaf2 = Leaf.new("l2") 82 | leaf3 = Leaf.new("l3") 83 | 84 | tree1 << tree2 85 | tree1 << leaf1 86 | tree1 << leaf2 87 | 88 | tree2 << leaf3 89 | 90 | puts "Hierarchy: \n" + tree1.to_s 91 | puts "-------" 92 | 93 | puts "Second element: " + tree1[1].to_s 94 | puts "-------" 95 | 96 | tree1[1] = Leaf.new("l4") 97 | puts "Set second element to: " + tree1[1].to_s 98 | puts "-------" 99 | 100 | tree1 >> leaf1 101 | 102 | puts "Hierarchy: \n" + tree1.to_s 103 | puts "-------" 104 | 105 | puts "Operation on composite: \n" 106 | 107 | tree1.operation 108 | puts "-------" 109 | -------------------------------------------------------------------------------- /structural/decorator1.rb: -------------------------------------------------------------------------------- 1 | # decorator1.rb 2 | 3 | # Attaches additional responsibilities to an object dynamically. 4 | # Provides a flexible alternative to subclassing for extending functionality. 5 | 6 | # 1. type interface 7 | 8 | class Component 9 | def operation 10 | end 11 | end 12 | 13 | # 2. type implementation 14 | 15 | class ConcreteComponent < Component 16 | def operation 17 | puts "my component" 18 | end 19 | end 20 | 21 | # 3a. traditional solution based on inheritance 22 | 23 | class TraditionalComponent < ConcreteComponent 24 | def pre_decoration 25 | puts "my component pre decoration" 26 | end 27 | 28 | def post_decoration 29 | puts "my component post decoration" 30 | end 31 | 32 | def operation 33 | pre_decoration 34 | 35 | super 36 | 37 | post_decoration 38 | end 39 | end 40 | 41 | # 3b. solution based on decorator 42 | 43 | class Decorator < Component 44 | def initialize(component) 45 | @component = component 46 | end 47 | 48 | def pre_decoration 49 | puts "my component pre decoration" 50 | end 51 | 52 | def post_decoration 53 | puts "my component post decoration" 54 | end 55 | 56 | def operation 57 | pre_decoration 58 | 59 | @component.operation 60 | 61 | post_decoration 62 | end 63 | end 64 | 65 | 66 | # 4. test 67 | 68 | component1 = TraditionalComponent.new 69 | component1.operation 70 | puts '-------' 71 | 72 | component2 = Decorator.new(ConcreteComponent.new) 73 | component2.operation 74 | puts '-------' 75 | 76 | component4 = ConcreteComponent.new 77 | 78 | class << component4 79 | alias old_operation operation 80 | 81 | def operation 82 | puts "my component pre decoration" 83 | 84 | old_operation 85 | 86 | puts "my component post decoration" 87 | end 88 | end 89 | 90 | component4.operation 91 | puts '-------' 92 | -------------------------------------------------------------------------------- /structural/decorator2.rb: -------------------------------------------------------------------------------- 1 | # decorator2.rb 2 | 3 | # Attaches additional responsibilities to an object dynamically. 4 | # Provides a flexible alternative to subclassing for extending functionality. 5 | 6 | # 1. type interface 7 | 8 | class Component 9 | def operation 10 | end 11 | end 12 | 13 | # 2. type implementation 14 | 15 | class ConcreteComponent < Component 16 | def operation 17 | puts "my component" 18 | end 19 | end 20 | 21 | # 3. This solution uses 'forwardable' library 22 | 23 | require 'forwardable' 24 | 25 | class ForwardableDecorator 26 | extend Forwardable 27 | 28 | def_delegators :@component, :operation 29 | 30 | def initialize(component) 31 | @component = component 32 | end 33 | end 34 | 35 | # 4. test 36 | 37 | component = ForwardableDecorator.new(ConcreteComponent.new) 38 | component.operation 39 | puts '-------' 40 | -------------------------------------------------------------------------------- /structural/decorator3.rb: -------------------------------------------------------------------------------- 1 | # decorator3.rb 2 | 3 | # This example changes original method behavior for the instance with the help of ruby "alias". 4 | 5 | # 1. type interface 6 | 7 | class Component 8 | def operation 9 | end 10 | end 11 | 12 | # 2. type implementation 13 | 14 | class ConcreteComponent < Component 15 | def operation 16 | puts "my component" 17 | end 18 | end 19 | 20 | 21 | # 3. test 22 | 23 | component = ConcreteComponent.new 24 | 25 | class << component 26 | alias old_operation operation 27 | 28 | def operation 29 | puts "my component pre decoration" 30 | 31 | old_operation 32 | 33 | puts "my component post decoration" 34 | end 35 | end 36 | 37 | component.operation 38 | puts '-------' 39 | -------------------------------------------------------------------------------- /structural/facade.rb: -------------------------------------------------------------------------------- 1 | # facade.rb 2 | 3 | # Provides an unified interface to a set of interfaces in a subsystem. 4 | # Defines a high-level interface that makes the sybsystem easier to use. 5 | 6 | # 1. different parts of the system 7 | 8 | class Component1 9 | def operation1 10 | puts "operation1" 11 | end 12 | end 13 | 14 | class Component2 15 | def operation2 16 | puts "operation2" 17 | end 18 | end 19 | 20 | class Component3 21 | def operation3 22 | puts "operation3" 23 | end 24 | end 25 | 26 | # 2. facade to different parts; end user will communicate with them through the facade only. 27 | 28 | class MyFacade 29 | def do_something 30 | component1 = Component1.new 31 | component2 = Component2.new 32 | component3 = Component3.new 33 | 34 | component1.operation1 35 | component3.operation3 36 | component2.operation2 37 | end 38 | end 39 | 40 | # 3. test 41 | 42 | facade = MyFacade.new 43 | 44 | facade.do_something 45 | -------------------------------------------------------------------------------- /structural/flyweight.rb: -------------------------------------------------------------------------------- 1 | # flyweight.bsh 2 | 3 | # Uses sharing to support large numbers of fine grained objects efficiently. 4 | 5 | # 1. type interface 6 | 7 | class Item 8 | def process 9 | end 10 | end 11 | 12 | # 2. type implementations 13 | 14 | class MyItem1 < Item 15 | def process 16 | puts "my item1 process" 17 | end 18 | end 19 | 20 | class MyItem2 < Item 21 | def process 22 | puts "my item2 process" 23 | end 24 | end 25 | 26 | # 3. type factory; some kind of cache 27 | 28 | class ItemCache 29 | def initialize 30 | @item1 = MyItem1.new 31 | @item2 = MyItem2.new 32 | end 33 | 34 | def item(type) 35 | if(type == 1) 36 | return item1 37 | elsif(type == 2) 38 | return item2 39 | end 40 | end 41 | end 42 | 43 | //4. test 44 | 45 | cache = ItemCache.new 46 | 47 | items = [ 48 | cache.item(1), 49 | cache.item(2), 50 | cache.item(2), 51 | cache.item(2), 52 | cache.item(1), 53 | cache.item(2), 54 | cache.item(1), 55 | cache.item(2) 56 | ] 57 | 58 | for(item in items) do 59 | item.process 60 | end 61 | -------------------------------------------------------------------------------- /structural/proxy1.rb: -------------------------------------------------------------------------------- 1 | # proxy1.rb 2 | 3 | # Provides a surrogate or placeholder for another object to control access to it. 4 | 5 | # 1. type interface 6 | 7 | class Subject 8 | def process 9 | end 10 | end 11 | 12 | # 2. type implementation 13 | 14 | class MySubject < Subject 15 | def process 16 | puts "my process" 17 | end 18 | end 19 | 20 | # 3. type proxy implementation 21 | 22 | class SubjectProxy < Subject 23 | 24 | def initialize(subject) 25 | @subject = subject 26 | end 27 | 28 | def process 29 | puts("Delegating 'process' message to subject.") 30 | 31 | @subject.process 32 | end 33 | end 34 | 35 | 36 | # 4. test 37 | 38 | proxy = SubjectProxy.new(MySubject.new) 39 | 40 | proxy.process 41 | -------------------------------------------------------------------------------- /structural/proxy2.rb: -------------------------------------------------------------------------------- 1 | # proxy2.rb 2 | 3 | # Provides proxy implementation with ruby's method_missing. 4 | 5 | # 1. type interface 6 | 7 | class Subject 8 | def process 9 | end 10 | end 11 | 12 | # 2. type implementation 13 | 14 | class MySubject < Subject 15 | def process 16 | puts "my process" 17 | end 18 | end 19 | 20 | # 3. type proxy implementation 21 | 22 | class DynamicSubjectProxy 23 | 24 | def initialize(subject) 25 | @subject = subject 26 | end 27 | 28 | def method_missing(name, *args) 29 | puts("Delegating '#{name}' message to subject.") 30 | 31 | @subject.send(name, *args) 32 | end 33 | end 34 | 35 | 36 | # 4. test 37 | 38 | dynamic_proxy = DynamicSubjectProxy.new(MySubject.new) 39 | 40 | dynamic_proxy.process 41 | --------------------------------------------------------------------------------