├── CommandExample.rb ├── Factory and Factory Method.odp ├── Factory and Factory Method.pdf ├── FactoryExample.rb ├── FactoryExample.rb.html ├── FactoryMethod.rb ├── FactoryMethod.rb.html ├── Folders.rb ├── IncSingleton.rb ├── MySingleton.rb ├── README ├── State-Pattern.odp ├── State-Pattern.pdf ├── ruby-design-patterns-partI.odp ├── ruby-design-patterns-partI.pdf ├── sfii.rb ├── whiteblur.jpg └── whiteblur2.jpg /CommandExample.rb: -------------------------------------------------------------------------------- 1 | require 'singleton' 2 | 3 | class Command 4 | def execute() 5 | end 6 | def unexecute() 7 | end 8 | end 9 | 10 | class Buffer 11 | include Singleton 12 | @buffer = [] 13 | def initialize() 14 | @buffer = [] 15 | end 16 | def insert(n, token) 17 | @buffer.insert( n, token ) 18 | end 19 | def string() 20 | return @buffer.join( " " ) 21 | end 22 | def remove(n) 23 | val = @buffer[n] 24 | @buffer.delete_at(n) 25 | return val 26 | end 27 | end 28 | 29 | class PasteCommand < Command 30 | def initialize(n, token) 31 | @n = n 32 | @token = token 33 | end 34 | def execute() 35 | Buffer.instance.insert(@n, @token) 36 | end 37 | def unexecute() 38 | Buffer.instance.remove(@n) 39 | end 40 | end 41 | 42 | class RemoveCommand < Command 43 | @token = nil 44 | def initialize(n) 45 | @n = n 46 | end 47 | def execute() 48 | @token = Buffer.instance.remove(@n) 49 | end 50 | def unexecute() 51 | Buffer.instance.insert(@n, @token) 52 | end 53 | end 54 | 55 | def example_driver() 56 | puts(Buffer.instance.string()) 57 | actions = [ 58 | PasteCommand.new(0,"Hello"), 59 | PasteCommand.new(1,"World"), 60 | PasteCommand.new(1,"Beautiful"), 61 | RemoveCommand.new(2), 62 | RemoveCommand.new(0), 63 | ] 64 | for action in actions 65 | action.execute() 66 | puts(Buffer.instance.string()) 67 | end 68 | revactions = actions.reverse 69 | for action in revactions 70 | action.unexecute() 71 | puts(Buffer.instance.string()) 72 | end 73 | end 74 | 75 | example_driver() 76 | 77 | class Invoker 78 | def initialize() 79 | @undoqueue = [] 80 | @redoqueue = [] 81 | end 82 | def do(x) 83 | x.execute() 84 | @undoqueue << x 85 | @redoqueue = [] 86 | end 87 | def undo() 88 | x = @undoqueue.pop() 89 | x.unexecute() if x 90 | @redoqueue << x if x 91 | end 92 | def redo() 93 | x = @redoqueue.pop() 94 | if (x) 95 | x.execute() 96 | @undoqueue << x 97 | end 98 | end 99 | end 100 | class BufferInvoker < Invoker 101 | def do(x) 102 | super(x) 103 | puts(Buffer.instance.string()) 104 | end 105 | def undo() 106 | super() 107 | puts(Buffer.instance.string()) 108 | end 109 | def redo() 110 | super() 111 | puts(Buffer.instance.string()) 112 | end 113 | end 114 | 115 | def example_invoker() 116 | invoker = BufferInvoker.new() 117 | [ 118 | PasteCommand.new(0,"Snakes"), 119 | PasteCommand.new(1,"Hiss"), 120 | PasteCommand.new(1,"Go"), 121 | RemoveCommand.new(2), 122 | RemoveCommand.new(0), 123 | ].each { |x| invoker.do( x ) } 124 | for i in (1..5) 125 | invoker.undo() 126 | end 127 | for i in (1..5) 128 | invoker.redo() 129 | end 130 | end 131 | 132 | example_invoker() 133 | -------------------------------------------------------------------------------- /Factory and Factory Method.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abramhindle/ruby-design-patterns/981cd23ca9e8c70f07849026502e22adebeb3517/Factory and Factory Method.odp -------------------------------------------------------------------------------- /Factory and Factory Method.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abramhindle/ruby-design-patterns/981cd23ca9e8c70f07849026502e22adebeb3517/Factory and Factory Method.pdf -------------------------------------------------------------------------------- /FactoryExample.rb: -------------------------------------------------------------------------------- 1 | class CommandFactory 2 | def initialize(context) 3 | @context = context 4 | end 5 | def create_command(name) 6 | if (name == "Paste") 7 | return PasteCommand.new(@context) 8 | elsif (name == "Cut") 9 | return CutCommand.new(@context) 10 | else 11 | throw ("Could Not Construct "+name) 12 | end 13 | end 14 | end 15 | class PasteCommand 16 | def initialize(context) 17 | end 18 | end 19 | class CutCommand 20 | def initialize(context) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /FactoryExample.rb.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 |
6 |class CommandFactory 123 | def initialize(context) 124 | @context = context 125 | end 126 | def create_command(name) 127 | if (name == "Paste") 128 | return PasteCommand.new(@context) 129 | else if (name == "Cut") 130 | return CutCommand.new(@context) 131 | else 132 | throw ("Could Not Construct "+name) 133 | end 134 | end 135 | end 136 | class PasteCommand 137 | def initialize(context) 138 | end 139 | end 140 | class CutCommand 141 | def initialize(context) 142 | end 143 | end 144 |145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /FactoryMethod.rb: -------------------------------------------------------------------------------- 1 | class Printer 2 | def print_header(header) 3 | puts(header) 4 | end 5 | def print_body(body) 6 | puts(body) 7 | end 8 | def print_footer(footer) 9 | puts(footer) 10 | end 11 | end 12 | class AsciiPrinter < Printer 13 | end 14 | class AsciiDraftPrinter < AsciiPrinter 15 | def print_header(header) 16 | end 17 | def print_footer(header) 18 | end 19 | end 20 | class PostScriptPrinter < Printer 21 | end 22 | class PostScriptDraftPrinter < PostScriptPrinter 23 | def print_header(header) 24 | end 25 | def print_footer(header) 26 | end 27 | end 28 | 29 | 30 | class Report 31 | def header() 32 | "header" 33 | end 34 | def body() 35 | "body" 36 | end 37 | def footer() 38 | "footer" 39 | end 40 | def get_printer(type) 41 | throw "Abstract: Please Overload" 42 | end 43 | def print_report(type) 44 | printer = get_printer(type) 45 | printer.print_header(header) 46 | printer.print_body(body) 47 | printer.print_footer(footer) 48 | end 49 | end 50 | class DraftReport < Report 51 | def get_printer(type) 52 | if (type == "ascii") 53 | return AsciiDraftPrinter.new() 54 | elsif (type == "postscript") 55 | return PostScriptDraftPrinter.new() 56 | end 57 | end 58 | end 59 | class BasicReport < Report 60 | def get_printer(type) 61 | if (type == "ascii") 62 | return AsciiPrinter.new() 63 | elsif (type == "postscript") 64 | return PostScriptPrinter.new() 65 | end 66 | end 67 | end 68 | 69 | puts("Draft") 70 | d = DraftReport.new() 71 | d.print_report("ascii") 72 | d.print_report("postscript") 73 | puts("Basic") 74 | b = BasicReport.new() 75 | b.print_report("ascii") 76 | b.print_report("postscript") 77 | -------------------------------------------------------------------------------- /FactoryMethod.rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
47 | class Printer 48 | def print_header(header) 49 | end 50 | def print_body(body) 51 | end 52 | def print_footer(footer) 53 | end 54 | end 55 | class AsciiPrinter < Printer 56 | end 57 | class AsciiDraftPrinter < AsciiPrinter 58 | end 59 | class PostScriptPrinter < Printer 60 | end 61 | class PostScriptDraftPrinter < PostScriptPrinter 62 | end 63 | 64 | 65 | class Report 66 | def get_printer(type) 67 | throw "Abstract: Please Overload" 68 | end 69 | def print_report(type) 70 | printer = get_printer(type) 71 | printer.print_header(@header) 72 | printer.print_body(@body) 73 | printer.print_footer(@footer) 74 | end 75 | end 76 | class DraftReport < Report 77 | def get_printer(type) 78 | if (type == "ascii") 79 | return AsciiDraftPrinter.new() 80 | elsif (type == "postscript") 81 | return PostScriptDraftPrinter.new() 82 | end 83 | end 84 | end 85 | class BasicReport < Report 86 | def get_printer(type) 87 | if (type == "ascii") 88 | return AsciiPrinter.new() 89 | elsif (type == "postscript") 90 | return PostScriptPrinter.new() 91 | end 92 | end 93 | end 94 |95 | 96 | 97 | -------------------------------------------------------------------------------- /Folders.rb: -------------------------------------------------------------------------------- 1 | class NamedSizedItem 2 | @name = nil 3 | @size = 0 4 | def initialize(name) 5 | @name = name 6 | end 7 | def name() 8 | return @name 9 | end 10 | def size() 11 | return @size 12 | end 13 | end 14 | 15 | class NamedFile < NamedSizedItem 16 | def initialize(name) 17 | @name = name 18 | @size = 1 19 | end 20 | end 21 | 22 | class NamedFolder < NamedSizedItem 23 | @files = [] 24 | def initialize(name) 25 | @name = name 26 | @files = [] 27 | end 28 | def addFile(file) 29 | @files << file 30 | end 31 | def size() 32 | return @files.inject(0) { |res,elm| res + elm.size() } 33 | end 34 | end 35 | 36 | file1 = NamedFile.new("readme") 37 | file2 = NamedFile.new("license") 38 | file3 = NamedFile.new("a.out") 39 | subFolder1 = NamedFolder.new("Sub1"); 40 | subFolder2 = NamedFolder.new("Sub2"); 41 | subFolder3 = NamedFolder.new("Sub3"); 42 | subFolder1.addFile(file1) 43 | subFolder2.addFile(file2) 44 | subFolder3.addFile(file3) 45 | subFolder2.addFile(subFolder1) 46 | subFolder3.addFile(subFolder2) 47 | puts(subFolder3.size()) 48 | -------------------------------------------------------------------------------- /IncSingleton.rb: -------------------------------------------------------------------------------- 1 | require 'singleton' 2 | # http://dalibornasevic.com/posts/9-ruby-singleton-pattern-again 3 | 4 | class IncSingleton 5 | include Singleton 6 | end 7 | 8 | 9 | -------------------------------------------------------------------------------- /MySingleton.rb: -------------------------------------------------------------------------------- 1 | # http://apidock.com/ruby/Module/private_class_method 2 | # http://dalibornasevic.com/posts/9-ruby-singleton-pattern-again 3 | class MySingleton 4 | # attempt to limit access to the constructor 5 | private_class_method :new 6 | # class variable instance 7 | @@instance = nil 8 | def self.instance() 9 | if (@@instance.nil?()) 10 | # self. and MySingleton tend not to work 11 | @@instance = self.new() 12 | end 13 | return @@instance 14 | end 15 | end 16 | 17 | 18 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A talk about Design Patterns in Ruby. 2 | 3 | It is Dry because it is supposed to be. This is the basics of OO and ruby definitely can handle them. These patterns described are very relevant to Ruby code. 4 | 5 | -- Abram 6 | 7 | === License === 8 | 9 | Usually (c) 2013 Abram Hindle, Ken Wong 10 | 11 | All Slides are under either CC-BY 3.0 or CC-BY-SA 3.0 (c) 2013 Abram Hindle unless otherwise noted. 12 | ruby-design-patterns-partI.odp is explicitly licensed as (c) 2012 Abram Hindle and Ken Wong CC-BY-SA 3.0 13 | 14 | All source code is under CC0 (public domain) unless otherwise noted (I waive all rights to the source code examples). 15 | -------------------------------------------------------------------------------- /State-Pattern.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abramhindle/ruby-design-patterns/981cd23ca9e8c70f07849026502e22adebeb3517/State-Pattern.odp -------------------------------------------------------------------------------- /State-Pattern.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abramhindle/ruby-design-patterns/981cd23ca9e8c70f07849026502e22adebeb3517/State-Pattern.pdf -------------------------------------------------------------------------------- /ruby-design-patterns-partI.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abramhindle/ruby-design-patterns/981cd23ca9e8c70f07849026502e22adebeb3517/ruby-design-patterns-partI.odp -------------------------------------------------------------------------------- /ruby-design-patterns-partI.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abramhindle/ruby-design-patterns/981cd23ca9e8c70f07849026502e22adebeb3517/ruby-design-patterns-partI.pdf -------------------------------------------------------------------------------- /sfii.rb: -------------------------------------------------------------------------------- 1 | #please excuse the CamelCase 2 | 3 | class Move 4 | def forward() 5 | return false 6 | end 7 | def backward() 8 | return false 9 | end 10 | def up() 11 | return false 12 | end 13 | def down() 14 | return false 15 | end 16 | def downBackward() 17 | return false 18 | end 19 | def downForward() 20 | return false 21 | end 22 | def upBackward() 23 | return false 24 | end 25 | def upForward() 26 | return false 27 | end 28 | end 29 | 30 | 31 | class Up < Move 32 | def up() 33 | return true 34 | end 35 | end 36 | 37 | class Down < Move 38 | def down() 39 | return true 40 | end 41 | end 42 | 43 | class Forward < Move 44 | def forward() 45 | return true 46 | end 47 | end 48 | 49 | class Back < Move 50 | def backward() 51 | return true 52 | end 53 | end 54 | 55 | class Backward < Back 56 | 57 | end 58 | 59 | class DownBackward < Move 60 | def downBackward() 61 | return true 62 | end 63 | end 64 | 65 | class DownForward < Move 66 | def downForward() 67 | return true 68 | end 69 | end 70 | 71 | class UpBack < Move 72 | def upBackward() 73 | return true 74 | end 75 | end 76 | class UpBackward < UpBack 77 | end 78 | 79 | class UpForward < Move 80 | def upForward() 81 | return true 82 | end 83 | end 84 | 85 | class Action 86 | def kick() 87 | return false 88 | end 89 | def punch() 90 | return false 91 | end 92 | end 93 | 94 | class Kick < Action 95 | def kick() 96 | return true 97 | end 98 | end 99 | 100 | class Punch < Action 101 | def punch() 102 | return true 103 | end 104 | end 105 | 106 | class None < Move 107 | end 108 | 109 | class FireballMoveConditional 110 | def initialize() 111 | @state = :none 112 | end 113 | def say() 114 | return "HaDoKen!" 115 | end 116 | # this example has a state value as a guard 117 | # but if I mess up the conditional then all 118 | # hell breaks loose 119 | def nextState( context, move, action ) 120 | if (@state == :none && move.down()) then 121 | @state = :ha 122 | elsif (@state == :ha && move.down()) then 123 | @state = :ha 124 | elsif (@state == :ha && move.downForward()) then 125 | @state = :do 126 | elsif (@state == :do && move.forward() && action.punch()) then 127 | # the body of the state machine is put into 128 | # these conditional blocks 129 | context.execute(FireballMove.new()) 130 | @state = :none 131 | else 132 | # the general default behaviour is here 133 | # but sometimes different states have default 134 | # behaviour too 135 | @state = :none 136 | end 137 | return self 138 | end 139 | end 140 | 141 | class MockContext 142 | def execute(v) 143 | puts(v) 144 | end 145 | end 146 | 147 | 148 | 149 | class FireballMove 150 | def say() 151 | return "HaDoKen!" 152 | end 153 | def nextState( context, move, action ) 154 | if (move.down()) then 155 | return FireballHa.new() 156 | end 157 | return FireballMove.new() 158 | end 159 | end 160 | 161 | class FireballHa 162 | def nextState( context, move, action ) 163 | if (move.downForward()) then 164 | return FireballDo.new() 165 | elsif (move.down()) then 166 | return FireballHa.new() 167 | end 168 | return FireballMove.new() 169 | end 170 | end 171 | 172 | class FireballDo 173 | def nextState( context, move, action ) 174 | if (move.forward() && action.punch()) then 175 | context.execute(FireballMove.new()) 176 | end 177 | return FireballMove.new() 178 | end 179 | end 180 | 181 | 182 | class DragonUppercutMove 183 | def say() 184 | return "ShoRyuKen!" 185 | end 186 | def nextState( context, move, action ) 187 | if (move.forward()) then 188 | return DragonUppercutSho.new() 189 | end 190 | return DragonUppercutMove.new() 191 | end 192 | end 193 | 194 | class DragonUppercutSho 195 | def nextState( context, move, action ) 196 | if (move.downBackward()) then 197 | return DragonUppercutRyu.new() 198 | end 199 | return DragonUppercutMove.new() 200 | end 201 | end 202 | 203 | class DragonUppercutRyu 204 | def nextState( context, move, action ) 205 | if (move.forward() && action.punch()) then 206 | context.execute(DragonUppercutMove.new()) 207 | end 208 | return DragonUppercutMove.new() 209 | end 210 | end 211 | 212 | class WhirlwindKickMove 213 | def say() 214 | return "TaTsunaki!" 215 | end 216 | def nextState( context, move, action ) 217 | if (move.up()) then 218 | return WhirlwindKickTa.new() 219 | end 220 | return WhirlwindKickMove.new() 221 | end 222 | end 223 | 224 | class WhirlwindKickTa 225 | def nextState( context, move, action ) 226 | if (move.down()) then 227 | return WhirlwindKickTsun.new() 228 | end 229 | return WhirlwindKickMove.new() 230 | end 231 | end 232 | 233 | class WhirlwindKickTsun 234 | def nextState( context, move, action ) 235 | if (move.downBackward()) then 236 | return WhirlwindKickAki.new() 237 | end 238 | return WhirlwindKickMove.new() 239 | end 240 | end 241 | class WhirlwindKickAki 242 | def nextState( context, move, action ) 243 | if (move.backward() && action.kick()) then 244 | context.execute(WhirlwindKickMove.new()) 245 | end 246 | return WhirlwindKickMove.new() 247 | end 248 | end 249 | 250 | 251 | 252 | 253 | class RyuPlayer 254 | def initialize() 255 | @moves = [WhirlwindKickMove.new(), FireballMove.new(), DragonUppercutMove.new()] 256 | @todo = [] 257 | end 258 | def execute( move ) 259 | @todo = [ move ] 260 | end 261 | def move( move, action ) 262 | @todo = [] 263 | @moves = @moves.map {|specialMove| specialMove.nextState( self, move, action ) } 264 | # @moves.each {|x| puts("\t" + x.class.name) } 265 | executeMove() 266 | end 267 | def executeMove() 268 | if (@todo.length > 0) 269 | puts(@todo[0].say()) 270 | end 271 | end 272 | end 273 | 274 | 275 | class RyuApp 276 | 277 | def trySomeMoves() 278 | player = RyuPlayer.new() 279 | puts("# walking") 280 | player.move( Forward.new(), Action.new() ) 281 | player.move( Forward.new(), Action.new() ) 282 | puts("# start fireball") 283 | player.move( Down.new(), Action.new() ) 284 | player.move( DownForward.new(), Action.new() ) 285 | player.move( Forward.new(), Punch.new() ) # fireball 286 | puts("# start fireball") 287 | player.move( Down.new(), Action.new() ) 288 | player.move( DownForward.new(), Action.new() ) 289 | player.move( Forward.new(), Punch.new() ) # fireball 290 | puts("# start fireball") 291 | player.move( Down.new(), Action.new() ) 292 | player.move( DownForward.new(), Action.new() ) 293 | player.move( Forward.new(), Punch.new() ) # fireball 294 | puts("# crouching") 295 | player.move( Down.new(), Action.new() ) 296 | player.move( Down.new(), Action.new() ) 297 | player.move( Down.new(), Action.new() ) 298 | puts("# starting fireball") 299 | player.move( Down.new(), Action.new() ) 300 | player.move( DownForward.new(), Action.new() ) 301 | player.move( Forward.new(), Punch.new() ) #fireball 302 | puts("# converting to dragon uppercut") 303 | player.move( DownBackward.new(), Action.new() ) 304 | player.move( Forward.new(), Punch.new() ) #DragonUppercut 305 | puts("# charging whirlwind kick") 306 | player.move( Up.new(), Action.new() ) 307 | player.move( Down.new(), Action.new() ) 308 | player.move( DownBackward.new(), Action.new() ) 309 | player.move( Backward.new(), Kick.new() ) #whirlwind 310 | player.move( Forward.new(), Punch.new() ) 311 | end 312 | 313 | 314 | def genKeymap() 315 | keymap = { 316 | "\e[C"=> [ Forward.new(), Action.new()], 317 | "\e[D"=> [ Backward.new(), Action.new()], 318 | "\e[B"=> [ Down.new(), Action.new()], 319 | "\e[A"=> [ Up.new(), Action.new()], 320 | "\e[C\e[B"=> [ DownForward.new(), Action.new()], 321 | "\e[D\e[B"=> [ DownBackward.new(), Action.new()], 322 | "\e[B\e[C"=> [ DownForward.new(), Action.new()], 323 | "\e[B\e[D"=> [ DownBackward.new(), Action.new()], 324 | "\e[C\e[A"=> [ UpForward.new(), Action.new()], 325 | "\e[D\e[a"=> [ UpBackward.new(), Action.new()], 326 | "\e[A\e[C"=> [ UpForward.new(), Action.new()], 327 | "\e[A\e[D"=> [ UpBackward.new(), Action.new()], 328 | "" => [ None.new(), Action.new() ] 329 | } 330 | keys = keymap.keys 331 | keys.each { |key| keymap[key + 'x'] = [ keymap[key][0] , Kick.new()] } 332 | keys.each { |key| keymap[key + 'z'] = [ keymap[key][0], Punch.new()] } 333 | return keymap 334 | end 335 | 336 | def initialize() 337 | @keymap = genKeymap() 338 | @player = RyuPlayer.new() 339 | end 340 | def get_action() 341 | 342 | end 343 | def run() 344 | while( keys = STDIN.readline().chomp() ) 345 | act = @keymap[keys] 346 | if (act) then 347 | puts(act.map {|x| "\t"+x.class.name }) 348 | @player.move( *act ) 349 | end 350 | end 351 | end 352 | end 353 | 354 | app = RyuApp.new() 355 | app.trySomeMoves() 356 | app.run() 357 | 358 | -------------------------------------------------------------------------------- /whiteblur.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abramhindle/ruby-design-patterns/981cd23ca9e8c70f07849026502e22adebeb3517/whiteblur.jpg -------------------------------------------------------------------------------- /whiteblur2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abramhindle/ruby-design-patterns/981cd23ca9e8c70f07849026502e22adebeb3517/whiteblur2.jpg --------------------------------------------------------------------------------