Top Banner
Embracing the Power of Refactor REN Xiaojun
107

Embracing the-power-of-refactor

Feb 08, 2017

Download

Technology

Xiaojun REN
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Embracing the-power-of-refactor

Embracing the Power of Refactor

REN Xiaojun

Page 2: Embracing the-power-of-refactor

Code smells are heuristics for refactoring

Page 3: Embracing the-power-of-refactor

Our design communicates to us through resistance.

Page 4: Embracing the-power-of-refactor

Code is difficult to test

Page 5: Embracing the-power-of-refactor

Code is difficult to change

Page 6: Embracing the-power-of-refactor

Code is difficult to reuse

Page 7: Embracing the-power-of-refactor

This resistance is valuable feedback

Page 8: Embracing the-power-of-refactor

Code smells are hints from our software about how to reduce this resistance.

Page 9: Embracing the-power-of-refactor

This is one way our design emerges.

Page 10: Embracing the-power-of-refactor
Page 11: Embracing the-power-of-refactor

Commands

PING

responds with PONG

SEND

send messages to GCM

Page 13: Embracing the-power-of-refactor

Queue<String> queue = new LinkedBlockingQueue<>(); HttpClient httpclient = HttpClients.createDefault(); DatagramSocket socket = new DatagramSocket(6889);

Page 14: Embracing the-power-of-refactor

Runnable pusher = new Runnable() { @Override public void run() { while (true) { String json = queue.poll(); if (json == null || json.length() <= 0) { try { sleep(1000); continue; } catch (InterruptedException e) { e.printStackTrace(); } } HttpPost post = new HttpPost("https://android.googleapis.com/gcm/send"); post.setHeader("Authorization", "key=AIzaSyCABSTd47XeIH"); post.setHeader("Content-Type", "application/json"); System.out.println("posting " + json); try { HttpEntity entity = new StringEntity(json); post.setEntity(entity);

httpclient.execute(post); } catch (Exception e) { e.printStackTrace(); } } } };

Thread t = new Thread(pusher); t.start();

request creation & delivery

Page 15: Embracing the-power-of-refactor

while (true) { try { byte[] buf = new byte[4096]; DatagramPacket received = new DatagramPacket(buf, buf.length); socket.receive(received); String data = new String(received.getData()); String command = data.split("\\s")[0];

if ("PING".equals(command)) { byte[] sendData = "PONG".getBytes(); DatagramPacket sendPacket = new DatagramPacket(…); socket.send(sendPacket); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = … queue.add(json); } } } catch (IOException e) { e.printStackTrace(); } } }

command dispatch

parameter extraction

Page 16: Embracing the-power-of-refactor

smell:Long Method

Page 17: Embracing the-power-of-refactor

recipe:Replace Method With Method Object

Page 18: Embracing the-power-of-refactor

PushDaemon

Page 19: Embracing the-power-of-refactor

public class PushDaemon {

private final Queue<String> queue; private final HttpClient httpclient; private final DatagramSocket socket;

public PushDaemon() throws SocketException { queue = new LinkedBlockingQueue<>(); httpClient = HttpClients.createDefault(); socket = new DatagramSocket(6889);

} }

Page 20: Embracing the-power-of-refactor

public void start() { Runnable client = new Runnable() { @Override public void run() { while (true) { String json = queue.poll(); if (json == null || json.length() <= 0) { try { sleep(1000); continue; } catch (InterruptedException e) { e.printStackTrace(); } } HttpPost post = new HttpPost("https://android.googleapis.com/gcm/send"); post.setHeader("Authorization", "key=AIzaSyCABSTd47XeIH"); post.setHeader("Content-Type", "application/json"); System.out.println("posting " + json); try { HttpEntity entity = new StringEntity(json); post.setEntity(entity);

httpclient.execute(post); } catch (Exception e) { e.printStackTrace(); } } } };

Thread t = new Thread(client); t.start();

while (true) { try { byte[] buf = new byte[4096]; DatagramPacket received = new DatagramPacket(buf, buf.length); socket.receive(received); String data = new String(received.getData()); String command = data.split("\\s")[0];

if ("PING".equals(command)) { byte[] sendData = "PONG".getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, received.getAddress(), received.getPort()); socket.send(sendPacket); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; System.out.println(json); queue.add(json); } } } catch (IOException e) { e.printStackTrace(); } } } }

Page 21: Embracing the-power-of-refactor

recipe:Extract Method

Page 22: Embracing the-power-of-refactor

public void start() { Runnable client = new Runnable() { @Override public void run() { while (true) { String json = queue.poll(); if (json == null || json.length() <= 0) { try { sleep(1000); continue; } catch (InterruptedException e) { e.printStackTrace(); } } HttpPost post = new HttpPost("https://android.googleapis.com/gcm/send"); post.setHeader("Authorization", "key=AIzaSyCABSTd47XeIH"); post.setHeader("Content-Type", "application/json"); System.out.println("posting " + json); try { HttpEntity entity = new StringEntity(json); post.setEntity(entity);

httpclient.execute(post); } catch (Exception e) { e.printStackTrace(); } } } };

Thread t = new Thread(client); t.start();

while (true) { try { byte[] buf = new byte[4096]; DatagramPacket received = new DatagramPacket(buf, buf.length); socket.receive(received); String data = new String(received.getData()); String command = data.split("\\s")[0];

if ("PING".equals(command)) { byte[] sendData = "PONG".getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, received.getAddress(), received.getPort()); socket.send(sendPacket); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; System.out.println(json); queue.add(json); } } } catch (IOException e) { e.printStackTrace(); } } } }

spawn workers

process requests

Page 23: Embracing the-power-of-refactor

public void start() { spawnWorkers(); while(true){ processRequests(); } }

Page 24: Embracing the-power-of-refactor

private void spawnWorkers() { Runnable client = new Runnable() { @Override public void run() while (true) { String json = queue.poll(); if (json == null || json.length() <= 0) {

try { sleep(1000); continue; } catch (InterruptedException e) { e.printStackTrace(); } } HttpPost post = new HttpPost("https://android.googleapis.com/gcm/send"); post.setHeader("Authorization", "key=AIzaSyCABSTd47XeIH"); post.setHeader("Content-Type", "application/json"); try { HttpEntity entity = new StringEntity(json); post.setEntity(entity); httpclient.execute(post); } catch (Exception e) { e.printStackTrace(); } } } };

Thread t = new Thread(client); t.start(); } }

Page 25: Embracing the-power-of-refactor

private void processRequests() { try { byte[] buf = new byte[4096]; DatagramPacket received = new DatagramPacket(buf, buf.length); socket.receive(received); String data = new String(received.getData()); String command = data.split("\\s")[0];

if ("PING".equals(command)) { byte[] sendData = "PONG".getBytes();

DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, received.getAddress(), received.getPort());

socket.send(sendPacket); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; queue.add(json);

} }

} catch (IOException e) { e.printStackTrace();

} }

Page 26: Embracing the-power-of-refactor

public class PushDaemon {

private final Queue<String> queue; private final HttpClient httpclient; private final DatagramSocket socket;

public PushDaemon() throws SocketException { queue = new LinkedBlockingQueue<>(); httpclient = HttpClients.createDefault(); socket = new DatagramSocket(6889);

}

public void start() { spawnWorkers(); while(true){processRequests();} }

private void processRequests() { //… }

private void spawnWorkers() { //… }

}

Page 27: Embracing the-power-of-refactor

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-urlencoded instead of JSON

Lower maximum payload size

Bind a specific interface address

Update push service URL

Page 28: Embracing the-power-of-refactor

smell:Divergent Change

Page 29: Embracing the-power-of-refactor

recipe:Extract Class

Page 30: Embracing the-power-of-refactor

private void spawnWorkers() { Runnable client = new Runnable() { @Override public void run() while (true) { String json = queue.poll(); if (json == null || json.length() <= 0) {

try { sleep(1000); continue; } catch (InterruptedException e) { e.printStackTrace(); } } HttpPost post = new HttpPost("https://android.googleapis.com/gcm/send"); post.setHeader("Authorization", "key=AIzaSyCABSTd47XeIH"); post.setHeader("Content-Type", "application/json"); try { HttpEntity entity = new StringEntity(json); post.setEntity(entity); httpclient.execute(post); } catch (Exception e) { e.printStackTrace(); } } } };

Thread t = new Thread(client); t.start(); } }

Page 31: Embracing the-power-of-refactor

public class Worker { private final Queue<String> queue; private final HttpClient httpclient;

public Worker() { queue = new LinkedBlockingQueue<>(); httpclient = HttpClients.createDefault(); }

public void add(String json) { this.queue.add(json); }

}

Page 32: Embracing the-power-of-refactor

public class PushDaemon {

public PushDaemon() throws SocketException { queue = new LinkedBlockingQueue<>(); httpclient = HttpClients.createDefault(); socket = new DatagramSocket(6889);

} }

Page 33: Embracing the-power-of-refactor

public class PushDaemon {

public PushDaemon() throws SocketException { worker = new Worker(); socket = new DatagramSocket(6889);

} }

Page 34: Embracing the-power-of-refactor

private void processRequests() { try { byte[] buf = new byte[4096]; DatagramPacket received = new DatagramPacket(buf, buf.length); socket.receive(received); String data = new String(received.getData()); String command = data.split("\\s")[0];

if ("PING".equals(command)) { byte[] sendData = "PONG".getBytes();

DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, received.getAddress(), received.getPort());

socket.send(sendPacket); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; queue.add(json);

} }

} catch (IOException e) { e.printStackTrace();

} }

Page 35: Embracing the-power-of-refactor

private void processRequests() { try { byte[] buf = new byte[4096]; DatagramPacket received = new DatagramPacket(buf, buf.length); socket.receive(received); String data = new String(received.getData()); String command = data.split("\\s")[0];

if ("PING".equals(command)) { byte[] sendData = "PONG".getBytes();

DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, received.getAddress(), received.getPort());

socket.send(sendPacket); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; worker.add(json);

} }

} catch (IOException e) { e.printStackTrace();

} }

Page 36: Embracing the-power-of-refactor

public class PushDaemon { public void start() {

spawnWorkers(); while(true){processRequests();} } }

Page 37: Embracing the-power-of-refactor

public class PushDaemon { public void start() {

worker.spawn(); while(true){processRequests();} } }

Page 38: Embracing the-power-of-refactor

public class PushDaemon {

public PushDaemon() throws SocketException { worker = new Worker();

socket = new DatagramSocket(6889); } }

Page 39: Embracing the-power-of-refactor

private void processRequests() { while (true) { try { byte[] buf = new byte[4096]; DatagramPacket received = new DatagramPacket(buf, buf.length); socket.receive(received); String data = new String(received.getData()); String command = data.split("\\s")[0];

if ("PING".equals(command)) { byte[] sendData = "PONG".getBytes();

DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, received.getAddress(), received.getPort());

socket.send(sendPacket); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; worker.add(json);

} }

} catch (IOException e) { e.printStackTrace();

} }

}

Page 40: Embracing the-power-of-refactor

public class UDPServer {

private final DatagramSocket socket;

public UDPServer() throws SocketException { socket = new DatagramSocket(6889); } }

Page 41: Embracing the-power-of-refactor

private void processRequests() { while (true) { try { byte[] buf = new byte[4096]; DatagramPacket received = new DatagramPacket(buf, buf.length); socket.receive(received); String data = new String(received.getData()); String command = data.split("\\s")[0];

if ("PING".equals(command)) { byte[] sendData = "PONG".getBytes();

DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, received.getAddress(), received.getPort());

socket.send(sendPacket); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; worker.add(json);

} }

} catch (IOException e) { e.printStackTrace();

} }

}

receive

send

Page 42: Embracing the-power-of-refactor

public class UDPServer {

private final DatagramSocket socket;

public UDPServer() throws SocketException { socket = new DatagramSocket(6889); }

public DatagramPacket receive() throws IOException { byte[] buf = new byte[4096]; DatagramPacket received = new DatagramPacket(buf, buf.length); socket.receive(received); return received; }

public void send(String message, InetAddress address, int port) throws IOException { byte[] sendData = message.getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, port); socket.send(sendPacket); } }

Page 43: Embracing the-power-of-refactor

private void processRequests() { try { byte[] buf = new byte[4096]; DatagramPacket received = new DatagramPacket(buf, buf.length); socket.receive(received); String data = new String(received.getData()); String command = data.split("\\s")[0];

if ("PING".equals(command)) { byte[] sendData = "PONG".getBytes();

DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, received.getAddress(), received.getPort());

socket.send(sendPacket); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; worker.add(json);

} }

} catch (IOException e) { e.printStackTrace();

} }

Page 44: Embracing the-power-of-refactor

public class PushDaemon {

private final Worker worker; private final UDPServer server;

public PushDaemon() throws SocketException { worker = new Worker(); server = new UDPServer();

}

public void start() { worker.spawn(); while(true){processRequests();} }

private void processRequests() { //… } }

Page 45: Embracing the-power-of-refactor

private void processRequests() { try { DatagramPacket received = server.receive(); String data = new String(received.getData());

String command = data.split("\\s")[0]; if ("PING".equals(command)) { server.send("PONG", received.getAddress(), received.getPort());

} else if ("SEND".equals(command)) { String message = data.replace(command, "").trim();

Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; worker.add(json); } } } catch (IOException e) { e.printStackTrace(); } }

Page 46: Embracing the-power-of-refactor

smell: Inappropriate Intimacy

Page 47: Embracing the-power-of-refactor
Page 48: Embracing the-power-of-refactor
Page 49: Embracing the-power-of-refactor

public class PushDaemon {

private final Worker worker; private final UDPServer server;

public PushDaemon() throws SocketException { worker = new Worker(); server = new UDPServer();

}

public void start() { worker.spawn(); processRequests(); }

private void processRequests() { //… } }

Page 50: Embracing the-power-of-refactor

public class PushDaemon {

private final Worker worker; private final UDPServer server;

public PushDaemon() throws SocketException { worker = new Worker(); server = new UDPServer(this);

}

public void start() { worker.spawn(); processRequests(); }

private void processRequests() { //… } }

Page 51: Embracing the-power-of-refactor

public class PushDaemon {

private final Worker worker; private final UDPServer server;

public PushDaemon() throws SocketException { worker = new Worker(); server = new UDPServer(this);

}

public void start() { worker.spawn(); while(true){processRequests();} }

private void processRequests() { //… } }

Page 52: Embracing the-power-of-refactor

public class PushDaemon {

private final Worker worker; private final UDPServer server;

public PushDaemon() throws SocketException { worker = new Worker(); server = new UDPServer(this);

}

public void start() { worker.spawn(); server.listen(6889); }

private void processRequests() { //… } }

Page 53: Embracing the-power-of-refactor

private void processRequests() { try { DatagramPacket received = server.receive(); String data = new String(received.getData());

String command = data.split("\\s")[0]; if ("PING".equals(command)) { server.send("PONG", received.getAddress(), received.getPort());

} else if ("SEND".equals(command)) { String message = data.replace(command, "").trim();

Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; worker.add(json); } } } catch (IOException e) { e.printStackTrace(); } }

Page 54: Embracing the-power-of-refactor

private void processRequests(DatagramPacket received) { try { String data = new String(received.getData());

String command = data.split("\\s")[0]; if ("PING".equals(command)) { server.send("PONG", received.getAddress(), received.getPort());

} else if ("SEND".equals(command)) { String message = data.replace(command, "").trim();

Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; worker.add(json); } } } catch (IOException e) { e.printStackTrace(); } }

Page 55: Embracing the-power-of-refactor

private void call(DatagramPacket received) { try { String data = new String(received.getData());

String command = data.split("\\s")[0]; if ("PING".equals(command)) { server.send("PONG", received.getAddress(), received.getPort());

} else if ("SEND".equals(command)) { String message = data.replace(command, "").trim();

Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; worker.add(json); } } } catch (IOException e) { e.printStackTrace(); } }

Page 56: Embracing the-power-of-refactor

public class UDPServer {

public UDPServer(PushDaemon app) { this.app = app; }

public void listen(int port) throws IOException { socket = new DatagramSocket(port); while (true) { app.call(receive()); } }

public DatagramPacket receive() throws IOException { byte[] buf = new byte[4096]; DatagramPacket received = new DatagramPacket(buf, buf.length); socket.receive(received); return received; }

public void send(String message, InetAddress address, int port) throws IOException { byte[] sendData = message.getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, port); socket.send(sendPacket); } }

Page 57: Embracing the-power-of-refactor

public class PushDaemon { public PushDaemon() throws SocketException { worker = new Worker(); server = new UDPServer(this); } public void start() throws IOException { worker.spawn(); server.listen(6889); }

public void call(DatagramPacket received) { try { String data = new String(received.getData());

String command = data.split("\\s")[0]; if ("PING".equals(command)) { server.send("PONG", received.getAddress(), received.getPort()); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; System.out.println(json); worker.add(json); } } } catch (IOException e) { e.printStackTrace(); } }

}

Page 58: Embracing the-power-of-refactor

smell: Case Statement

Page 59: Embracing the-power-of-refactor

recipe: Replace Conditional with Polymorphism

Page 60: Embracing the-power-of-refactor

public void call(DatagramPacket received) { try { String data = new String(received.getData());

String command = data.split("\\s")[0]; if ("PING".equals(command)) { server.send("PONG", received.getAddress(), received.getPort()); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; System.out.println(json); worker.add(json); } } } catch (IOException e) { e.printStackTrace(); } }

}

Page 61: Embracing the-power-of-refactor

public class Ping { private UDPServer server; private DatagramPacket received;

public Ping(UDPServer server, DatagramPacket received) { this.server = server; this.received = received; }

public void run() throws IOException { server.send("PONG", received.getAddress(), received.getPort()); } }

Page 62: Embracing the-power-of-refactor

public void call(DatagramPacket received) { try { String data = new String(received.getData());

String command = data.split("\\s")[0]; if ("PING".equals(command)) { new Jobs(server, received).run(); } else if ("SEND".equals(command)) { String message = data.replace(command, "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; worker.add(json); } } } catch (IOException e) { e.printStackTrace(); } }

}

Page 63: Embracing the-power-of-refactor

public class Send {

private DatagramPacket received;

public Send(DatagramPacket received) { this.received = received; }

public String run() { String data = new String(received.getData()); String message = data.replace("SEND", "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { return "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; } return null; }

}

Page 64: Embracing the-power-of-refactor

public void call(DatagramPacket received) { try { String data = new String(received.getData());

String command = data.split("\\s")[0]; if ("PING".equals(command)) { new Ping(server, received).run(); } else if ("SEND".equals(command)) {

String json = new Send(received).run(); worker.add(json);

} } catch (IOException e) { e.printStackTrace(); } }

}

Page 65: Embracing the-power-of-refactor

public class Worker { private final Queue<String> queue; private final HttpClient httpclient;

public Worker() { queue = new LinkedBlockingQueue<>(); httpclient = HttpClients.createDefault(); }

public void add(String json) { this.queue.add(json); }

void spawn() { Runnable client = new Runnable() { @Override public void run() { while (true) { String json = queue.poll(); if (json == null || json.length() <= 0) { try { sleep(1000); continue; } catch (InterruptedException e) { e.printStackTrace(); } } HttpPost post = new HttpPost("https://android.googleapis.com/gcm/send"); post.setHeader("Authorization", "key=AIzaSyCABSTd47XeIH"); post.setHeader("Content-Type", "application/json"); System.out.println("posting " + json); try { HttpEntity entity = new StringEntity(json); post.setEntity(entity);

httpclient.execute(post); } catch (Exception e) { e.printStackTrace(); } } } };

Thread t = new Thread(client); t.start(); } }

Page 66: Embracing the-power-of-refactor

public class Send {

private DatagramPacket received; private final HttpClient httpclient;

public Send(DatagramPacket received) { this.received = received; this.httpclient = HttpClients.createDefault(); }

public void run() throws IOException { String data = new String(received.getData()); String message = data.replace("SEND", "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; HttpPost post = new HttpPost("https://android.googleapis.com/gcm/send"); post.setHeader("Authorization", "key=AIzaSyCABSTd47XeIH"); post.setHeader("Content-Type", "application/json");

HttpEntity entity = new StringEntity(json); post.setEntity(entity);

httpclient.execute(post); } } }

Page 67: Embracing the-power-of-refactor

public class Worker { public Worker() { queue = new LinkedBlockingQueue<>(); } public void accept(Job job) { this.queue.add(job); } void spawn() { Runnable client = new Runnable() { @Override public void run() { while (true) { Job job = queue.poll(); if (job != null) { try { job.run(); } catch (IOException e) { e.printStackTrace(); } } } } };

Thread t = new Thread(client); t.start(); } }

Page 68: Embracing the-power-of-refactor

public interface Job { void run() throws IOException; }

public class Ping implements Job { //… }

public class Send implements Job { //… }

Page 69: Embracing the-power-of-refactor

public class PushDaemon {

private final Worker worker; private final UDPServer server;

public PushDaemon() throws SocketException { worker = new Worker(); server = new UDPServer(this); }

public void start() throws IOException { worker.spawn(); server.listen(6889); }

public void call(DatagramPacket received) { String data = new String(received.getData()); String command = data.split("\\s")[0]; Job job = null; if ("PING".equals(command)) { job = new Ping(server, received); } else if ("SEND".equals(command)) { job = new Send(received); } if(job != null) { worker.accept(job); } }

}

Page 70: Embracing the-power-of-refactor

Recipe:Move Creation Knowledge to Factory

Page 71: Embracing the-power-of-refactor

public interface Job { public static Job create(DatagramPacket received, UDPServer server) { String data = new String(received.getData()); String command = data.split("\\s")[0]; Job job = null; if ("PING".equals(command)) { job = new Ping(server, received); } else if ("SEND".equals(command)) { job = new Send(received); } return job; } }

Page 72: Embracing the-power-of-refactor

public class PushDaemon {

private final Worker worker; private final UDPServer server;

public PushDaemon() throws SocketException { worker = new Worker(); server = new UDPServer(this); }

public void start() throws IOException { worker.spawn(); server.listen(6889); }

public void call(DatagramPacket received) { Job job = Job.create(received, server); if (job != null) { worker.accept(job); } } }

Page 73: Embracing the-power-of-refactor

public class Ping implements Job { public void run() throws IOException { server.send("PONG", received.getAddress(), received.getPort()); } }

public class Send implements Job { public void run() throws IOException { String data = new String(received.getData()); String message = data.replace("SEND", "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; HttpPost post = new HttpPost("https://android.googleapis.com/gcm/send"); post.setHeader("Authorization", "key=AIzaSyCABSTd47XeIH"); post.setHeader("Content-Type", "application/json");

HttpEntity entity = new StringEntity(json); post.setEntity(entity);

httpclient.execute(post); } } }

Page 74: Embracing the-power-of-refactor

public class Client { private final InetSocketAddress socketAddress;

public Client(InetSocketAddress socketAddress) { this.socketAddress = socketAddress; }

public int port() { return socketAddress.getPort(); }

public InetAddress address() { return socketAddress.getAddress(); } }

Page 75: Embracing the-power-of-refactor

public class UDPServer {

public UDPServer(PushDaemon app) { this.app = app; }

public void listen(int port) throws IOException { socket = new DatagramSocket(port); while (true) { app.call(receive()); } } }

Page 76: Embracing the-power-of-refactor

public class UDPServer { public void listen(int port) throws IOException { socket = new DatagramSocket(port); while (true) { DatagramPacket received = receive(); String message = messageOf(received); Client client = clientOf(received); app.call(message, client); } }

private String messageOf(DatagramPacket received) { return new String(received.getData()); }

private Client clientOf(DatagramPacket received) { return new Client((InetSocketAddress) received.getSocketAddress()); } }

Page 77: Embracing the-power-of-refactor

public interface Job { public static Job create(DatagramPacket received, UDPServer server) { String data = new String(received.getData()); String command = data.split("\\s")[0]; Job job = null; if ("PING".equals(command)) { job = new Ping(server, received); } else if ("SEND".equals(command)) { job = new Send(received); } return job; } }

Page 78: Embracing the-power-of-refactor

public interface Job { public static Job create(Client client, String message, UDPServer server) {

//… } }

Page 79: Embracing the-power-of-refactor

public class Ping implements Job { private final Client client; private final String message; private UDPServer server;

public Ping(Client client, String message, UDPServer server) { this.client = client; this.message = message; this.server = server;

}

public void run() throws IOException { server.send("PONG", client.address(), client.port()); } }

Page 80: Embracing the-power-of-refactor

smell:Feature Envy

Page 81: Embracing the-power-of-refactor

Extracted objects tend to attract behaviour.

Page 82: Embracing the-power-of-refactor

public class Client { private final InetSocketAddress socketAddress; private final UDPServer server;

public Client(InetSocketAddress socketAddress, UDPServer server) { this.socketAddress = socketAddress; this.server = server; }

public int port() { return socketAddress.getPort(); }

public InetAddress address() { return socketAddress.getAddress(); }

public void send(String message) throws IOException { server.send(message, address(), port()); }

}

Page 83: Embracing the-power-of-refactor

public class Ping implements Job { private final Client client; private final String message; private UDPServer server;

public Ping(Client client, String message, UDPServer server) { this.client = client; this.message = message; this.server = server;

}

public void run() throws IOException { server.send("PONG", client.address(), client.port()); } }

Page 84: Embracing the-power-of-refactor

public class Client { private final InetSocketAddress socketAddress; private final UDPServer server;

public Client(InetSocketAddress socketAddress, UDPServer server) { this.socketAddress = socketAddress; this.server = server; }

public int port() { return socketAddress.getPort(); }

public InetAddress address() { return socketAddress.getAddress(); }

public void send(String message) throws IOException { server.send(message, address(), port()); }

}

Page 85: Embracing the-power-of-refactor

public class Ping implements Job { public void run() throws IOException { client.send("PONG"); } }

Page 86: Embracing the-power-of-refactor

public class Send implements Job {

public Send(Client client, String message) { this.client = client; this.message = message; this.server = server; this.httpclient = HttpClients.createDefault(); }

@Override public void run() throws IOException { String message = this.message.replace("SEND", "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { String json = "{\"registration_ids\" : \"" + matcher.group(1) + "\", \"data\" : { \"alert\" : \"" + matcher.group(2) + "\"}}"; HttpPost post = new HttpPost("https://android.googleapis.com/gcm/send"); post.setHeader("Authorization", "key=AIzaSyCABSTd47XeIH"); post.setHeader("Content-Type", "application/json");

HttpEntity entity = new StringEntity(json); post.setEntity(entity);

httpclient.execute(post); } } }

Page 87: Embracing the-power-of-refactor

PushNotification

Page 88: Embracing the-power-of-refactor

public class PushNotification { public PushNotification(String registrationId, String alert) { this.registrationId = registrationId; this.alert = alert; }

public void deliver() throws IOException { HttpPost post = new HttpPost("https://android.googleapis.com/gcm/send"); post.setHeader("Authorization", "key=AIzaSyCABSTd47XeIH"); post.setHeader("Content-Type", "application/json");

HttpEntity entity = new StringEntity(toJson()); post.setEntity(entity);

httpclient.execute(post); }

private String toJson() { return "{\"registration_ids\" : \"" + registrationId + "\", \"data\" : { \"alert\" : \"" + alert + "\"}}"; } }

Page 89: Embracing the-power-of-refactor

public class Send implements Job {

private final String message;

public Send(String message) { this.message = message; }

@Override public void run() throws IOException { String message = this.message.replace("SEND", "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { new PushNotification(matcher.group(1), matcher.group(2)).deliver(); } } }

Page 90: Embracing the-power-of-refactor

smell:Primitive Obsession

Page 91: Embracing the-power-of-refactor

we’re using simple data types to represent complex ideas

Page 92: Embracing the-power-of-refactor

public class Send implements Job {

private final String message;

public Send(String message) { this.message = message; }

@Override public void run() throws IOException { String message = this.message.replace("SEND", "").trim(); Pattern p = Pattern.compile("([a-zA-Z0-9_-]*) \"([^\"]*)\""); Matcher matcher = p.matcher(message);

if (matcher.matches()) { new PushNotification(matcher.group(1), matcher.group(2)).deliver(); } } }

Page 93: Embracing the-power-of-refactor

COMMAND [parameters]

Page 94: Embracing the-power-of-refactor

parameter “second parameter”

Page 95: Embracing the-power-of-refactor

public class Request {

private static Pattern PATTERN = Pattern.compile("([SEND|PING]) ([a-zA-Z0-9_-]*) \"([^\"]*)\""); private final boolean acceptable; private final Matcher matcher;

public Request(String message) { matcher = PATTERN.matcher(message); acceptable = matcher.matches(); }

public String command() { return matcher.group(1); }

public List<String> parameters() { List<String> parameters = new ArrayList<>();

for (int i = 2; i <= matcher.groupCount(); i++) { parameters.add(matcher.group(i)); }

return parameters; }

public boolean isAcceptable() { return acceptable; } }

Page 96: Embracing the-power-of-refactor

public class Send implements Job {

private final Request request;

public Send(Request request) { this.request = request; }

@Override public void run() throws IOException { if(request.isAcceptable()) new PushNotification(registrationId(), alert()).deliver(); }

private String registrationId() { return request.parameters().get(0); }

private String alert() { return request.parameters().get(1); } }

Page 97: Embracing the-power-of-refactor

smell:Null Check

Page 98: Embracing the-power-of-refactor

public class PushDaemon {

public void call(String message, Client client) { Request request = new Request(message); Job job = Job.create(client, request); if (job != null) { worker.accept(job); } } }

Page 99: Embracing the-power-of-refactor

null communicates that an unknown command has been requested

Page 100: Embracing the-power-of-refactor

Recipe:Null Object

Page 101: Embracing the-power-of-refactor

public class NullJob implements Job { @Override public void run() throws IOException {

} }

Page 102: Embracing the-power-of-refactor

public class PushDaemon {

public void call(String message, Client client) { Request request = new Request(message); Job job = Job.create(client, request); worker.accept(job); } }

Page 103: Embracing the-power-of-refactor

public class PushDaemon {

public void call(String message, Client client) { Request request = new Request(message); Job job = Job.create(client, request); job.enqueueTo(worker); } }

Page 104: Embracing the-power-of-refactor

public interface Job { void run() throws IOException;

public static Job create(Client client, Request request) {

Job job = null; if (request.isAcceptable()) { job = new NullJob(); } if ("PING".equals(request.command())) { job = new Ping(client); } else if ("SEND".equals(request.command())) { job = new Send(request); } return job; }

default void enqueueTo(Worker worker) { worker.accept(this); } }

Page 105: Embracing the-power-of-refactor

public class NullJob implements Job { @Override public void run() throws IOException {

//noop }

@Override public void enqueueTo(Worker worker) {

//noop } }

Page 106: Embracing the-power-of-refactor

Reference

http://tx.pignata.com/2013/04/mwrc-code-smells-talk.html

https://github.com/nicholasren/refactor_push

Page 107: Embracing the-power-of-refactor

Q&A