Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Code Smells: Your Refactoring Cheat Codes

Code Smells: Your Refactoring Cheat Codes

Sure, the TDD cycle is red-green-refactor but what exactly are we refactoring? We just wrote the code, it's green, and it seems reasonable to us. Let's move onto the next test. We're have a deadline, remember?

Whether we're working with code we just wrote or opening up a project for the first time, being able to listen to the hints the code is trying to give you about how it wants to be constructed is the most direct path toward successful refactoring. What the code is telling you nuanced however: no code smell is absolute and none in itself is an indication of a problem. How do we know we need to refactor? What are the code smells telling us to do? How do we balance our short terms needs of shipping our software with the long term maintainability of the code base? In this talk we'll talk through some of the classical code smells and dive into examples of how to put these smells to work for you.

03e04db3b6880c3a2f8114649312f733?s=128

John Pignata

April 04, 2013
Tweet

Transcript

  1. Code Smells: r Refactoring Cheat Codes

  2. @jpignata

  3. None
  4. Code smells are heuristics for refactoring.

  5. None
  6. Our design communicates to us through resistance.

  7. Our code is difficult to understand

  8. Our code is difficult to test

  9. Our code is difficult to change

  10. Our code is difficult to reuse

  11. This resistance is valuable feedback.

  12. Code smells are hints from our software about how to

    reduce this resistance.
  13. This is one way our design emerges.

  14. Application Push Daemon Google Cloud Messaging API Android Device UDP

    HTTPS
  15. Application Push Daemon Google Cloud Messaging API Android Device UDP

    HTTPS
  16. queue = Queue.new client = HTTPClient.new socket = UDPSocket.new 10.times

    do Thread.new do while data = queue.pop client.post("https://android.googleapis.com/gcm/send", data, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end socket.bind("0.0.0.0", 6889) while data = socket.recvfrom(4096) case data[0].split.first when "PING" socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) queue << json end end
  17. PING it responds with PONG SEND it delivers the message

    to the Google Cloud Messaging API Commands
  18. $ ruby ./pushd.rb

  19. $ nc -u 127.0.0.1 6889 PING

  20. $ nc -u 127.0.0.1 6889 PING PONG

  21. PING ✔ it responds with PONG SEND it delivers the

    message to the Google Cloud Messaging API Commands
  22. $ nc -u 127.0.0.1 6889 SEND AP91bQd65Z5IA8p "Steve: What is

    up?"
  23. None
  24. PING ✔ it responds with PONG SEND ✔ it delivers

    the message to the Google Cloud Messaging API Commands
  25. describe "Push Daemon" do let(:socket) { UDPSocket.new } before(:all) do

    Thread.new { load "./pushd.rb" } end describe "commands" do describe "PING" do it "responds with PONG" end describe "SEND" do it "delivers the message to the Google Cloud Messaging API" end end end
  26. it "responds with PONG" do socket.send("PING", 0, "127.0.0.1", 6889) response,

    _ = socket.recvfrom(8) response.should eq("PONG") end
  27. it "delivers the message to the Google Cloud Messaging API"

    do stub_request :post, "https://android.googleapis.com/gcm/send" socket.send('SEND t0k3n "Steve: What is up?"', 0, "127.0.0.1", 6889) assert_requested :post, "https://android.googleapis.com/gcm/send", { body: { "registration_ids" => ["t0k3n"], "data" => { "alert" => "Steve: What is up?" } }.to_json } end
  28. jp@oeuf:~/workspace/mwrc(master)$ rspec Push Daemon commands PING responds with PONG SEND

    delivers the message to the Google Cloud Messaging API Finished in 0.06854 seconds 2 examples, 0 failures
  29. queue = Queue.new client = HTTPClient.new socket = UDPSocket.new 10.times

    do Thread.new do while data = queue.pop client.post("https://android.googleapis.com/gcm/send", data, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end socket.bind("0.0.0.0", 6889) while data = socket.recvfrom(4096) case data[0].split.first when "PING" socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) queue << json end end
  30. Long Method C S

  31. queue = Queue.new client = HTTPClient.new socket = UDPSocket.new 10.times

    do Thread.new do while data = queue.pop client.post("https://android.googleapis.com/gcm/send", data, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end socket.bind("0.0.0.0", 6889) while data = socket.recvfrom(4096) case data[0].split.first when "PING" socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) queue << json end end Collaborator Instantiation [
  32. queue = Queue.new client = HTTPClient.new socket = UDPSocket.new

  33. queue = Queue.new client = HTTPClient.new socket = UDPSocket.new 10.times

    do Thread.new do while data = queue.pop client.post("https://android.googleapis.com/gcm/send", data, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end socket.bind("0.0.0.0", 6889) while data = socket.recvfrom(4096) case data[0].split.first when "PING" socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) queue << json end end Thread Pool Initialization [ Collaborator Instantiation [
  34. 10.times do Thread.new do while data = queue.pop # ...

    end end end
  35. queue = Queue.new client = HTTPClient.new socket = UDPSocket.new 10.times

    do Thread.new do while data = queue.pop client.post("https://android.googleapis.com/gcm/send", data, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end socket.bind("0.0.0.0", 6889) while data = socket.recvfrom(4096) case data[0].split.first when "PING" socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) queue << json end end Thread Pool Initialization [ Service Request Creation and Delivery [ Collaborator Instantiation [
  36. client.post("https://android.googleapis.com/gcm/send", data, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" })

  37. queue = Queue.new client = HTTPClient.new socket = UDPSocket.new 10.times

    do Thread.new do while data = queue.pop client.post("https://android.googleapis.com/gcm/send", data, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end socket.bind("0.0.0.0", 6889) while data = socket.recvfrom(4096) case data[0].split.first when "PING" socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) queue << json end end Thread Pool Initialization [ Service Request Creation and Delivery [ [ Server Socket Setup Collaborator Instantiation [
  38. socket.bind("0.0.0.0", 6889)

  39. queue = Queue.new client = HTTPClient.new socket = UDPSocket.new 10.times

    do Thread.new do while data = queue.pop client.post("https://android.googleapis.com/gcm/send", data, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end socket.bind("0.0.0.0", 6889) while data = socket.recvfrom(4096) case data[0].split.first when "PING" socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) queue << json end end Thread Pool Initialization [ Service Request Creation and Delivery [ [ Server Socket Setup [ Command Dispatch Collaborator Instantiation [
  40. case data[0].split.first when "PING" # ... when "SEND" # ...

    end end
  41. queue = Queue.new client = HTTPClient.new socket = UDPSocket.new 10.times

    do Thread.new do while data = queue.pop client.post("https://android.googleapis.com/gcm/send", data, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end socket.bind("0.0.0.0", 6889) while data = socket.recvfrom(4096) case data[0].split.first when "PING" socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) queue << json end end Thread Pool Initialization [ Service Request Creation and Delivery [ [ Server Socket Setup [ Command Dispatch [ Parameter Extraction Collaborator Instantiation [
  42. data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" =>

    { "alert" => $2 } })
  43. queue = Queue.new client = HTTPClient.new socket = UDPSocket.new 10.times

    do Thread.new do while data = queue.pop client.post("https://android.googleapis.com/gcm/send", data, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end socket.bind("0.0.0.0", 6889) while data = socket.recvfrom(4096) case data[0].split.first when "PING" socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) queue << json end end Thread Pool Initialization [ Service Request Creation and Delivery [ [ Server Socket Setup [ Command Dispatch [ Parameter Extraction Collaborator Instantiation [
  44. Replace Method with Method Object R f r

  45. class PushDaemon end

  46. queue = Queue.new client = HTTPClient.new socket = UDPSocket.new 10.times

    do Thread.new do while data = queue.pop client.post("https://android.googleapis.com/gcm/send", data, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end socket.bind("0.0.0.0", 6889) while data = socket.recvfrom(4096) case data[0].split.first when "PING" socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) queue << json end end
  47. class PushDaemon def initialize @queue = Queue.new @client = HTTPClient.new

    @socket = UDPSocket.new end end
  48. 10.times do Thread.new do while json = queue.pop client.post("https://android.googleapis.com/gcm/send", json,

    "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" ) end end end socket.bind("0.0.0.0", 6889) while data = socket.recvfrom(4096) case data[0].split.first when "PING" socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) queue << json end end
  49. def start 10.times do Thread.new do while json = @queue.pop

    @client.post("https://android.googleapis.com/gcm/send", json, "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" ) end end end @socket.bind("0.0.0.0", 6889) while data = @socket.recvfrom(4096) case data[0].split.first when "PING" @socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @queue << json end end end
  50. Extract Method R f r

  51. def start 10.times do Thread.new do while json = @queue.pop

    @client.post("https://android.googleapis.com/gcm/send", json, "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" ) end end end @socket.bind("0.0.0.0", 6889) while data = @socket.recvfrom(4096) case data[0].split.first when "PING" @socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @queue << json end end end [ Spawn Workers [ Bind [ Loop and Process Incoming Requests
  52. def start spawn_workers bind loop { process_request } end

  53. def spawn_workers 10.times do Thread.new do while json = @queue.pop

    @client.post("https://android.googleapis.com/gcm/send", json, "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" ) end end end end
  54. def bind @socket.bind("0.0.0.0", 6889) end

  55. def process_request data = @socket.recvfrom(4096) case data[0].split.first when "PING" @socket.send("PONG",

    0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @queue << json end end
  56. class PushDaemon def initialize @queue = Queue.new @client = HTTPClient.new

    @socket = UDPSocket.new end def start spawn_workers bind loop { process_request } end private def spawn_workers; end def bind; end def process_request; end end
  57. ➡ Update authorization key ➡ Increase thread pool size ➡

    Swap HTTP client ➡ Use a different transport protocol ➡ Modify wire protocol format ➡ Add commands ➡ Add a different push notification service ➡ Move UDP port ➡ Use x-www-form-urlencoded instead of JSON ➡ Lower maximum payload size ➡ Bind to a specific interface address ➡ Update push service URL
  58. Divergent Change C S

  59. Extract Class R f r

  60. def spawn_workers 10.times do Thread.new do while json = @queue.pop

    @client.post("https://android.googleapis.com/gcm/send", json, "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" ) end end end end
  61. class Worker def initialize @queue = Queue.new @client = HTTPClient.new

    end end
  62. class PushDaemon def initialize @queue = Queue.new @client = HTTPClient.new

    @socket = UDPSocket.new end end
  63. class PushDaemon def initialize @worker = Worker.new @socket = UDPSocket.new

    end end
  64. def process_request data = @socket.recvfrom(4096) case data[0].split.first when "PING" @socket.send("PONG",

    0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @queue << json end end
  65. class Worker def initialize @queue = Queue.new @client = HTTPClient.new

    end def <<(json) @queue << json end end
  66. def process_request data = @socket.recvfrom(4096) case data[0].split.first when "PING" @socket.send("PONG",

    0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @queue << json end end
  67. def process_request data = @socket.recvfrom(4096) case data[0].split.first when "PING" @socket.send("PONG",

    0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end
  68. def spawn_workers 10.times do Thread.new do while json = @queue.pop

    @client.post("https://android.googleapis.com/gcm/send", json, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end end
  69. class Worker def initialize @queue = Queue.new @client = HTTPClient.new

    end def spawn(count) count.times do Thread.new do while json = @queue.pop @client.post("https://android.googleapis.com/gcm/send", json, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end end def <<(json) @queue << json end end
  70. class PushDaemon def initialize @worker = Worker.new @socket = UDPSocket.new

    end def start spawn_workers bind loop { process_request } end private def spawn_workers; end def bind; end def process_request; end end
  71. class PushDaemon def initialize @worker = Worker.new @socket = UDPSocket.new

    end def start @worker.spawn(10) bind loop { process_request } end private def bind; end def process_request; end end
  72. class PushDaemon def initialize @worker = Worker.new @socket = UDPSocket.new

    end def start @worker.spawn(10) bind loop { process_request } end private def bind; end def process_request; end end
  73. def bind @socket.bind("0.0.0.0", 6889) end def process_request data = @socket.recvfrom(4096)

    case data[0].split.first when "PING" @socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end [ Bind [ Receive [ Send
  74. class UDPServer def initialize @socket = UDPSocket.new end end

  75. def bind @socket.bind("0.0.0.0", 6889) end def process_request data = @socket.recvfrom(4096)

    case data[0].split.first when "PING" @socket.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end [ Bind [ Receive [ Send
  76. class UDPServer def initialize @socket = UDPSocket.new end def bind(port)

    @socket.bind("0.0.0.0", port) end def receive @socket.recvfrom(4096) end def send(message, address, port) @socket.send(message, 0, address, port) end end
  77. class PushDaemon def initialize @worker = Worker.new @socket = UDPSocket.new

    end def start @worker.spawn(10) bind loop { process_request } end private def bind; end def process_request; end end
  78. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new

    end def start @worker.spawn(10) bind loop { process_request } end private def bind; end def process_request; end end
  79. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new

    end def start @worker.spawn(10) bind loop { process_request } end private def bind; end def process_request; end end
  80. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new

    end def start @worker.spawn(10) @server.bind(6889) loop { process_request } end private def process_request; end end
  81. def process_request data = @socket.recvfrom(4096) case data[0].split.first when "PING" @socket.send("PONG",

    0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @queue << json end end
  82. def process_request data = @server.receive case data[0].split.first when "PING" @server.send("PONG",

    data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @queue << json end end
  83. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new

    end def start @worker.spawn(10) @server.bind(6889) loop { process_request } end private def process_request data = @server.receive case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  84. Inappropriate Intimacy C S

  85. PushDaemon UDPServer bind(port) receive data

  86. PushDaemon UDPServer listen(port) call(data)

  87. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new

    end def start @worker.spawn(10) @server.bind(6889) loop { process_request } end private def process_request data = @server.receive case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  88. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.bind(6889) loop { process_request } end private def process_request data = @server.receive case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  89. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.bind(6889) loop { process_request } end private def process_request data = @server.receive case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  90. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.bind(6889) @server.listen end private def process_request data = @server.receive case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  91. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.bind(6889) @server.listen end private def process_request data = @server.receive case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  92. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end private def process_request data = @server.receive case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  93. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end private def process_request data = @server.receive case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  94. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def process_request(data) case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  95. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def process_request(data) case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  96. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(data) case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  97. class UDPServer def initialize @socket = UDPSocket.new end def bind(port)

    @socket.bind("0.0.0.0", port) end def receive @socket.recvfrom(4096) end def send(message, address, port) @socket.send(message, 0, address, port) end end
  98. class UDPServer def initialize(app) @app = app @socket = UDPSocket.new

    end def bind(port) @socket.bind("0.0.0.0", port) end def receive @socket.recvfrom(4096) end def send(message, address, port) @socket.send(message, 0, address, port) end end
  99. class UDPServer def initialize(app) @app = app @socket = UDPSocket.new

    end def bind(port) @socket.bind("0.0.0.0", port) end def receive @socket.recvfrom(4096) end def send(message, address, port) @socket.send(message, 0, address, port) end end
  100. class UDPServer def initialize(app) @app = app @socket = UDPSocket.new

    end def listen(port) @socket.bind("0.0.0.0", port) loop { @app.call(receive) } end def receive @socket.recvfrom(4096) end def send(message, address, port) @socket.send(message, 0, address, port) end end
  101. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(data) case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  102. Case Statement C S

  103. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(data) case data[0].split.first when "PING" @server.send("PONG", 0, data[1][3], data[1][1]) when "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end end
  104. Replace Conditional with Polymorphism R f r

  105. def call(data) case data[0].split.first when "PING" @server.send("PONG", data[1][3], data[1][1]) when

    "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end
  106. module Jobs class Ping def initialize(data, server) @data = data

    @server = server end def run @server.send("PONG", @data[1][3], @data[1][1]) end end end
  107. def call(data) case data[0].split.first when "PING" @server.send("PONG", data[1][3], data[1][1]) when

    "SEND" data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end
  108. def call(data) case data[0].split.first when "PING" Jobs::Ping.new(data, @server).run when "SEND"

    data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end
  109. def call(data) case data[0].split.first when "PING" Jobs::Ping.new(data, @server).run when "SEND"

    data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) @worker << json end end
  110. module Jobs class Send def initialize(data, server) @data = data

    @server = server end def run @data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) end end end
  111. def call(data) case data[0].split.first when "PING" Jobs::Ping.new(data, @server).run when "SEND"

    json = Jobs::Send.new(data, @server).run @worker << json end end
  112. class Worker def initialize @queue = Queue.new @client = HTTPClient.new

    end def spawn(count) count.times do Thread.new do while json = @queue.pop @client.post("https://android.googleapis.com/gcm/send", json, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end end def <<(json) @queue << json end end
  113. class Worker def initialize @queue = Queue.new @client = HTTPClient.new

    end def spawn(count) count.times do Thread.new do while json = @queue.pop @client.post("https://android.googleapis.com/gcm/send", json, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end end def <<(json) @queue << json end end
  114. module Jobs class Send def self.client @client ||= HTTPClient.new end

    def initialize(data, server) @data = data @server = server end def run @data[0][5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) self.class.client.post("https://android.googleapis.com/gcm/send", json, { "Authorization" => "key=AIzaSyCABSTd47XeIH-ERx9rvHUSF9DIJ7DCwdk", "Content-Type" => "application/json" }) end end end
  115. class Worker def initialize @queue = Queue.new end def spawn(count)

    count.times do Thread.new { work } end end def <<(job) @queue << job end private def work while job = @queue.pop job.run end end end
  116. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(data) case data[0].split.first when "PING" Jobs::Ping.new(data, @server).run when "SEND" json = Jobs::Send.new(data, @server).run @worker << json end end end
  117. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(data) job = case data[0].split.first when "PING" Jobs::Ping.new(data, @server) when "SEND" Jobs::Send.new(data, @server) end if job @worker << job end end end
  118. Move Creation Knowledge to Factory R f r

  119. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.bind(6889) @server.listen end def call(data) job = case data[0].split.first when "PING" Jobs::Ping.new(data, @server) when "SEND" Jobs::Send.new(data, @server) end if job @worker << job end end end
  120. module Jobs def self.factory(data, server) case data[0].split.first when "PING" Jobs::Ping.new(data,

    server) when "SEND" Jobs::Send.new(data, server) end end end
  121. module Jobs JOBS = { "PING" => Ping, "SEND" =>

    Send } def self.factory(data, server) command = data[0].split.first.upcase klass = JOBS[command] if klass klass.new(data, server) end end end
  122. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.bind(6889) @server.listen end def call(data) job = case data[0].split.first when "PING" Jobs::Ping.new(data, @server) when "SEND" Jobs::Send.new(data, @server) end if job @worker << job end end end
  123. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(data) job = Jobs.factory(data, @server) if job @worker << job end end end
  124. module Jobs class Ping def initialize(data, server) @data = data

    @server = server end def run @server.send("PONG", @data[1][3], @data[1][1]) end end end
  125. Data Clumps C S

  126. ["PING", ["AF_INET", 61370, "localhost", "127.0.0.1"]] data[1][3] data[1][1] [ [

  127. class Client def initialize(sockaddr) @addrinfo = Addrinfo.new(sockaddr) end def address

    @addrinfo.ip_address end def port @addrinfo.ip_port end end
  128. class UDPServer def initialize(app) @app = app @socket = UDPSocket.new

    end def listen(port) @socket.bind("0.0.0.0", port) loop { @app.call(receive) } end def receive @socket.recvfrom(4096) end def send(message, address, port) @socket.send(message, 0, address, port) end end
  129. class UDPServer def initialize(app) @app = app @socket = UDPSocket.new

    end def listen(port) @socket.bind("0.0.0.0", port) loop do message, sockaddr = @socket.recvfrom(4096) client = Client.new(sockaddr) @app.call(client, message) end end def send(message, address, port) @socket.send(message, 0, address, port) end end
  130. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(data) job = Jobs.factory(data, @server) if job @worker << job end end end
  131. Uncommunicative Name C S

  132. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(data) job = Jobs.factory(data, @server) if job @worker << job end end end
  133. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(client, message) job = Jobs.factory(client, message, @server) if job @worker << job end end end
  134. module Jobs class Ping def initialize(client, message, server) @client =

    client @message = message @server = server end def run @server.send("PONG", @client.address, @client.port) end end end
  135. Feature Envy C S

  136. Extracted objects tend to attract behavior.

  137. class Client def initialize(sockaddr) @addrinfo = Addrinfo.new(sockaddr) end def address

    @addrinfo.ip_address end def port @addrinfo.ip_port end end
  138. class Client def initialize(sockaddr, server) @addrinfo = Addrinfo.new(sockaddr) @server =

    server end def address @addrinfo.ip_address end def port @addrinfo.ip_port end end
  139. class Client def initialize(sockaddr, server) @addrinfo = Addrinfo.new(sockaddr) @server =

    server end def send(message) @server.send(message, address, port) end def address @addrinfo.ip_address end def port @addrinfo.ip_port end end
  140. class UDPServer def initialize(app) @app = app @socket = UDPSocket.new

    end def listen @socket.bind("0.0.0.0", port) loop do message, sockaddr = @socket.recvfrom(4096) client = Client.new(sockaddr) @app.call(client, message) end end def send(message, address, port) @socket.send(message, 0, address, port) end end
  141. class UDPServer def initialize(app) @app = app @socket = UDPSocket.new

    end def listen @socket.bind("0.0.0.0", port) loop do message, sockaddr = @socket.recvfrom(4096) client = Client.new(sockaddr, self) @app.call(client, message) end end def send(message, address, port) @socket.send(message, 0, address, port) end end
  142. module Jobs class Ping def initialize(client, message, server) @client =

    client @message = message @server = server end def run @server.send("PONG", @client.address, @client.port) end end end
  143. module Jobs class Ping def initialize(client, message, server) @client =

    client @message = message @server = server end def run @client.send("PONG") end end end
  144. module Jobs class Ping def initialize(client, message, server) @client =

    client @message = message @server = server end def run @client.send("PONG") end end end
  145. module Jobs class Ping def initialize(client, message) @client = client

    @message = message end def run @client.send("PONG") end end end
  146. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(client, message) job = Jobs.factory(client, message, @server) if job @worker << job end end end
  147. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(client, message) job = Jobs.factory(client, message) if job @worker << job end end end
  148. module Jobs class Send def self.client @client ||= HTTPClient.new end

    def initialize(client, message) @client = client @message = message end def run @message[5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) self.class.client.post("https://android.googleapis.com/gcm/send", json, { "Authorization" => "key=AIzaSyCABSTd47XeIH-ERx9rvHUSF9DIJ7DCwdk", "Content-Type" => "application/json" }) end end end
  149. Same Name, Different Meaning C S

  150. module Jobs class Send def self.client @client ||= HTTPClient.new end

    def initialize(client, message) @client = client @message = message end def run @message[5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) self.class.client.post("https://android.googleapis.com/gcm/send", json, { "Authorization" => "key=AIzaSyCABSTd47XeIH-ERx9rvHUSF9DIJ7DCwdk", "Content-Type" => "application/json" }) end end end HTTP Client [ Server Client [
  151. module Jobs class Send def self.client @client ||= HTTPClient.new end

    def initialize(client, message) @client = client @message = message end def run @message[5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) json = JSON.generate({ "registration_ids" => [$1], "data" => { "alert" => $2 } }) self.class.client.post("https://android.googleapis.com/gcm/send", json, { "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" }) end end end
  152. class PushNotification def self.client @client ||= HTTPClient.new end def initialize(registration_id,

    alert) @registration_id = registration_id @alert = alert end def deliver self.class.client.post("https://android.googleapis.com/gcm/send", to_json, "Authorization" => "key=AIzaSyCABSTd47XeIH", "Content-Type" => "application/json" ) end def to_json { "registration_ids" => [@registration_id], "data" => { "alert" => @alert } }.to_json end end
  153. module Jobs class Send def initialize(client, message) @client = client

    @message = message end def run @message[5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/) PushNotification.new($1, $2).deliver end end end
  154. Primitive Obsession C S

  155. We’re using simple data types to represent complex ideas.

  156. @message[5..-1].match(/([a-zA-Z0-9_\-]*) "([^"]*)/)

  157. COMMAND [parameters]

  158. parameter “second parameter”

  159. Shellwords

  160. class Request def initialize(message) @tokens = Shellwords.split(message) end def command

    @tokens[0].to_s.upcase end def parameters Array(@tokens[1..-1]) end end
  161. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(client, message) job = Jobs.factory(client, message) if job @worker << job end end end
  162. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(client, message) request = Request.new(message) job = Jobs.factory(client, request) if job @worker << job end end end
  163. module Jobs JOBS = { "PING" => Ping, "SEND" =>

    Send } def self.factory(client, message) command = message.split.first.upcase klass = JOBS[command] if klass klass.new(client, message) end end end
  164. module Jobs JOBS = { "PING" => Ping, "SEND" =>

    Send } def self.factory(client, request) klass = JOBS[request.command] if klass klass.new(client, request) end end end
  165. module Jobs class Send def initialize(client, request) @client = client

    @request = request end def run PushNotification.new(registration_id, alert).deliver end private def registration_id @request.parameters[0] end def alert @request.parameters[1] end end end
  166. Nil Checks C S

  167. module Jobs JOBS = { "PING" => Ping, "SEND" =>

    Send } def self.factory(client, request) klass = JOBS[request.command] if klass klass.new(client, request) end end end Possible nil from hash [
  168. class PushDaemon def initialize @server = UDPServer.new(self) @worker = Worker.new

    end def start @worker.spawn(10) @server.listen(6889) end def call(client, message) request = Request.new(message) job = Jobs.factory(client, request) if job @worker << job end end end Possible nil from factory [
  169. nil communicates that an unknown command has been requested

  170. Introduce Null Object R f r

  171. module Jobs class NullJob def initialize(client, request) end def run

    end end end
  172. module Jobs JOBS = { "PING" => Ping, "SEND" =>

    Send } def self.factory(client, request) klass = JOBS[request.command] if klass klass.new(client, request) end end end
  173. module Jobs JOBS = { "PING" => Ping, "SEND" =>

    Send } JOBS.default = NullJob def self.factory(client, request) JOBS[request.command].new(client, request) end end
  174. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(client, message) request = Request.new(message) job = Jobs.factory(client, request) if job @worker << job end end end
  175. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(client, message) request = Request.new(message) job = Jobs.factory(client, request) @worker << job end end
  176. module Jobs class Ping def initialize(client, request) @client = client

    @request = request end def run @client.send("PONG") end def >>(worker) worker << self end end end
  177. module Jobs class NullJob def initialize(client, request) end def run

    end def >>(worker) end end end
  178. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(client, message) request = Request.new(message) job = Jobs.factory(client, request) @worker << job end end
  179. class PushDaemon def initialize @worker = Worker.new @server = UDPServer.new(self)

    end def start @worker.spawn(10) @server.listen(6889) end def call(client, message) request = Request.new(message) job = Jobs.factory(client, request) job >> @worker end end
  180. jpignata / mwrc

  181. Thanks! @jpignata tx.pignata.com