├── .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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
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 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
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 |
79 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
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 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 | $USER_HOME$/.subversion_IDEA
373 | false
374 | 125
375 |
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 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
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 |
--------------------------------------------------------------------------------