SlideShare a Scribd company logo
Embracing the Power of
Refactor
REN Xiaojun
Code smells are heuristics for
refactoring
Our design communicates to
us through resistance.
Code is difficult to test
Code is difficult to change
Code is difficult to reuse
This resistance is valuable
feedback
Code smells are hints from our software
about how to reduce this resistance.
This is one way our design
emerges.
Commands
PING
responds with PONG
SEND
send messages to GCM
The Legacy Code
Queue<String> queue = new LinkedBlockingQueue<>();
HttpClient httpclient = HttpClients.createDefault();
DatagramSocket socket = new DatagramSocket(6889);
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
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
smell:Long Method
recipe:Replace Method With
Method Object
PushDaemon
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() {
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();
}
}
}
}
recipe:Extract Method
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
public void start() {
spawnWorkers();
while(true){
processRequests();
}
}
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();
}
}
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();
}
}
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() {
//…
}
}
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
smell:Divergent Change
recipe:Extract Class
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();
}
}
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);
}
}
public class PushDaemon {
public PushDaemon() throws SocketException {
queue = new LinkedBlockingQueue<>();
httpclient = HttpClients.createDefault();
socket = new DatagramSocket(6889);
}
}
public class PushDaemon {
public PushDaemon() throws SocketException {
worker = new Worker();
socket = new DatagramSocket(6889);
}
}
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();
}
}
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();
}
}
public class PushDaemon {
public void start() {
spawnWorkers();
while(true){processRequests();}
}
}
public class PushDaemon {
public void start() {
worker.spawn();
while(true){processRequests();}
}
}
public class PushDaemon {
public PushDaemon() throws SocketException {
worker = new Worker();
socket = new DatagramSocket(6889);
}
}
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();
}
}
}
public class UDPServer {
private final DatagramSocket socket;
public UDPServer() throws SocketException {
socket = new DatagramSocket(6889);
}
}
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
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);
}
}
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();
}
}
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() {
//…
}
}
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();
}
}
smell: Inappropriate Intimacy
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() {
//…
}
}
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() {
//…
}
}
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() {
//…
}
}
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() {
//…
}
}
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();
}
}
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();
}
}
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();
}
}
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);
}
}
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();
}
}
}
smell: Case Statement
recipe: Replace Conditional
with Polymorphism
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();
}
}
}
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());
}
}
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();
}
}
}
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;
}
}
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();
}
}
}
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();
}
}
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);
}
}
}
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();
}
}
public interface Job {
void run() throws IOException;
}
public class Ping implements Job {
//…
}
public class Send implements Job {
//…
}
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);
}
}
}
Recipe:Move Creation
Knowledge to Factory
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;
}
}
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);
}
}
}
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);
}
}
}
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();
}
}
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 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());
}
}
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;
}
}
public interface Job {
public static Job create(Client client, String message, UDPServer server) {
//…
}
}
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());
}
}
smell:Feature Envy
Extracted objects tend to
attract behaviour.
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());
}
}
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());
}
}
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());
}
}
public class Ping implements Job {
public void run() throws IOException {
client.send("PONG");
}
}
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);
}
}
}
PushNotification
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 + ""}}";
}
}
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();
}
}
}
smell:Primitive Obsession
we’re using simple data types
to represent complex ideas
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();
}
}
}
COMMAND [parameters]
parameter “second parameter”
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;
}
}
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);
}
}
smell:Null Check
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);
}
}
}
null communicates that an unknown
command has been requested
Recipe:Null Object
public class NullJob implements Job {
@Override
public void run() throws IOException {
}
}
public class PushDaemon {
public void call(String message, Client client) {
Request request = new Request(message);
Job job = Job.create(client, request);
worker.accept(job);
}
}
public class PushDaemon {
public void call(String message, Client client) {
Request request = new Request(message);
Job job = Job.create(client, request);
job.enqueueTo(worker);
}
}
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);
}
}
public class NullJob implements Job {
@Override
public void run() throws IOException {
//noop
}
@Override
public void enqueueTo(Worker worker) {
//noop
}
}
Reference
http://tx.pignata.com/2013/04/mwrc-code-
smells-talk.html
https://github.com/nicholasren/refactor_push
Q&A

More Related Content

What's hot

The Ring programming language version 1.4 book - Part 18 of 30
The Ring programming language version 1.4 book - Part 18 of 30The Ring programming language version 1.4 book - Part 18 of 30
The Ring programming language version 1.4 book - Part 18 of 30
Mahmoud Samir Fayed
 
The Ring programming language version 1.5.3 book - Part 77 of 184
The Ring programming language version 1.5.3 book - Part 77 of 184The Ring programming language version 1.5.3 book - Part 77 of 184
The Ring programming language version 1.5.3 book - Part 77 of 184
Mahmoud Samir Fayed
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
MongoDB
 
The Ring programming language version 1.3 book - Part 50 of 88
The Ring programming language version 1.3 book - Part 50 of 88The Ring programming language version 1.3 book - Part 50 of 88
The Ring programming language version 1.3 book - Part 50 of 88
Mahmoud Samir Fayed
 
Distributed systems
Distributed systemsDistributed systems
Distributed systems
Sonali Parab
 
The Ring programming language version 1.6 book - Part 69 of 189
The Ring programming language version 1.6 book - Part 69 of 189The Ring programming language version 1.6 book - Part 69 of 189
The Ring programming language version 1.6 book - Part 69 of 189
Mahmoud Samir Fayed
 
Drivers APIs and Looking Forward
Drivers APIs and Looking ForwardDrivers APIs and Looking Forward
Drivers APIs and Looking Forward
MongoDB
 
NoSQL meets Microservices - Michael Hackstein
NoSQL meets Microservices - Michael HacksteinNoSQL meets Microservices - Michael Hackstein
NoSQL meets Microservices - Michael Hackstein
distributed matters
 
Dagger & rxjava & retrofit
Dagger & rxjava & retrofitDagger & rxjava & retrofit
Dagger & rxjava & retrofit
Ted Liang
 
Michael Hackstein - NoSQL meets Microservices - NoSQL matters Dublin 2015
Michael Hackstein - NoSQL meets Microservices - NoSQL matters Dublin 2015Michael Hackstein - NoSQL meets Microservices - NoSQL matters Dublin 2015
Michael Hackstein - NoSQL meets Microservices - NoSQL matters Dublin 2015
NoSQLmatters
 
JSON Android
JSON AndroidJSON Android
JSON Android
★ Raúl Laza
 
The Ring programming language version 1.5.1 book - Part 63 of 180
The Ring programming language version 1.5.1 book - Part 63 of 180The Ring programming language version 1.5.1 book - Part 63 of 180
The Ring programming language version 1.5.1 book - Part 63 of 180
Mahmoud Samir Fayed
 
Vavr Java User Group Rheinland
Vavr Java User Group RheinlandVavr Java User Group Rheinland
Vavr Java User Group Rheinland
David Schmitz
 
Descargar datos con JSON en Android
Descargar datos con JSON en AndroidDescargar datos con JSON en Android
Descargar datos con JSON en Android
★ Raúl Laza
 
Intravert Server side processing for Cassandra
Intravert Server side processing for CassandraIntravert Server side processing for Cassandra
Intravert Server side processing for Cassandra
Edward Capriolo
 
Java 7 Launch Event at LyonJUG, Lyon France. Fork / Join framework and Projec...
Java 7 Launch Event at LyonJUG, Lyon France. Fork / Join framework and Projec...Java 7 Launch Event at LyonJUG, Lyon France. Fork / Join framework and Projec...
Java 7 Launch Event at LyonJUG, Lyon France. Fork / Join framework and Projec...
julien.ponge
 
FwDays 2021: Metarhia Technology Stack for Node.js
FwDays 2021: Metarhia Technology Stack for Node.jsFwDays 2021: Metarhia Technology Stack for Node.js
FwDays 2021: Metarhia Technology Stack for Node.js
Timur Shemsedinov
 
Java 7 LavaJUG
Java 7 LavaJUGJava 7 LavaJUG
Java 7 LavaJUG
julien.ponge
 
[263] s2graph large-scale-graph-database-with-hbase-2
[263] s2graph large-scale-graph-database-with-hbase-2[263] s2graph large-scale-graph-database-with-hbase-2
[263] s2graph large-scale-graph-database-with-hbase-2
NAVER D2
 

What's hot (19)

The Ring programming language version 1.4 book - Part 18 of 30
The Ring programming language version 1.4 book - Part 18 of 30The Ring programming language version 1.4 book - Part 18 of 30
The Ring programming language version 1.4 book - Part 18 of 30
 
The Ring programming language version 1.5.3 book - Part 77 of 184
The Ring programming language version 1.5.3 book - Part 77 of 184The Ring programming language version 1.5.3 book - Part 77 of 184
The Ring programming language version 1.5.3 book - Part 77 of 184
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
The Ring programming language version 1.3 book - Part 50 of 88
The Ring programming language version 1.3 book - Part 50 of 88The Ring programming language version 1.3 book - Part 50 of 88
The Ring programming language version 1.3 book - Part 50 of 88
 
Distributed systems
Distributed systemsDistributed systems
Distributed systems
 
The Ring programming language version 1.6 book - Part 69 of 189
The Ring programming language version 1.6 book - Part 69 of 189The Ring programming language version 1.6 book - Part 69 of 189
The Ring programming language version 1.6 book - Part 69 of 189
 
Drivers APIs and Looking Forward
Drivers APIs and Looking ForwardDrivers APIs and Looking Forward
Drivers APIs and Looking Forward
 
NoSQL meets Microservices - Michael Hackstein
NoSQL meets Microservices - Michael HacksteinNoSQL meets Microservices - Michael Hackstein
NoSQL meets Microservices - Michael Hackstein
 
Dagger & rxjava & retrofit
Dagger & rxjava & retrofitDagger & rxjava & retrofit
Dagger & rxjava & retrofit
 
Michael Hackstein - NoSQL meets Microservices - NoSQL matters Dublin 2015
Michael Hackstein - NoSQL meets Microservices - NoSQL matters Dublin 2015Michael Hackstein - NoSQL meets Microservices - NoSQL matters Dublin 2015
Michael Hackstein - NoSQL meets Microservices - NoSQL matters Dublin 2015
 
JSON Android
JSON AndroidJSON Android
JSON Android
 
The Ring programming language version 1.5.1 book - Part 63 of 180
The Ring programming language version 1.5.1 book - Part 63 of 180The Ring programming language version 1.5.1 book - Part 63 of 180
The Ring programming language version 1.5.1 book - Part 63 of 180
 
Vavr Java User Group Rheinland
Vavr Java User Group RheinlandVavr Java User Group Rheinland
Vavr Java User Group Rheinland
 
Descargar datos con JSON en Android
Descargar datos con JSON en AndroidDescargar datos con JSON en Android
Descargar datos con JSON en Android
 
Intravert Server side processing for Cassandra
Intravert Server side processing for CassandraIntravert Server side processing for Cassandra
Intravert Server side processing for Cassandra
 
Java 7 Launch Event at LyonJUG, Lyon France. Fork / Join framework and Projec...
Java 7 Launch Event at LyonJUG, Lyon France. Fork / Join framework and Projec...Java 7 Launch Event at LyonJUG, Lyon France. Fork / Join framework and Projec...
Java 7 Launch Event at LyonJUG, Lyon France. Fork / Join framework and Projec...
 
FwDays 2021: Metarhia Technology Stack for Node.js
FwDays 2021: Metarhia Technology Stack for Node.jsFwDays 2021: Metarhia Technology Stack for Node.js
FwDays 2021: Metarhia Technology Stack for Node.js
 
Java 7 LavaJUG
Java 7 LavaJUGJava 7 LavaJUG
Java 7 LavaJUG
 
[263] s2graph large-scale-graph-database-with-hbase-2
[263] s2graph large-scale-graph-database-with-hbase-2[263] s2graph large-scale-graph-database-with-hbase-2
[263] s2graph large-scale-graph-database-with-hbase-2
 

Viewers also liked

Closure
ClosureClosure
Closure
Xiaojun REN
 
Microsoft Word - Preview
Microsoft Word - PreviewMicrosoft Word - Preview
Microsoft Word - PreviewAndre Triputra
 
RESUME-STACIE SPRINGFIELD
RESUME-STACIE SPRINGFIELDRESUME-STACIE SPRINGFIELD
RESUME-STACIE SPRINGFIELD
STACIE SPRINGFIELD
 
Microsoft Word - End Point Info
Microsoft Word - End Point InfoMicrosoft Word - End Point Info
Microsoft Word - End Point InfoAndre Triputra
 
rest without put
rest without putrest without put
rest without put
Xiaojun REN
 
Nikunj Tak ,Project on HTML and CSS (Farming website) ,Final Year BCA ,Dezyne...
Nikunj Tak ,Project on HTML and CSS (Farming website) ,Final Year BCA ,Dezyne...Nikunj Tak ,Project on HTML and CSS (Farming website) ,Final Year BCA ,Dezyne...
Nikunj Tak ,Project on HTML and CSS (Farming website) ,Final Year BCA ,Dezyne...
dezyneecole
 
день снега
день снегадень снега
день снега
Rodnichok
 
Matt Smull Resume
Matt Smull ResumeMatt Smull Resume
Matt Smull Resume
Matthew Smull
 
Uso de tecnología ii unidad 1 act. 1
Uso de tecnología ii unidad 1 act. 1Uso de tecnología ii unidad 1 act. 1
Uso de tecnología ii unidad 1 act. 1
viridama
 
Stateless authentication for microservices
Stateless authentication for microservicesStateless authentication for microservices
Stateless authentication for microservices
Alvaro Sanchez-Mariscal
 
Dockercon State of the Art in Microservices
Dockercon State of the Art in MicroservicesDockercon State of the Art in Microservices
Dockercon State of the Art in Microservices
Adrian Cockcroft
 
Principles of microservices velocity
Principles of microservices   velocityPrinciples of microservices   velocity
Principles of microservices velocity
Sam Newman
 

Viewers also liked (12)

Closure
ClosureClosure
Closure
 
Microsoft Word - Preview
Microsoft Word - PreviewMicrosoft Word - Preview
Microsoft Word - Preview
 
RESUME-STACIE SPRINGFIELD
RESUME-STACIE SPRINGFIELDRESUME-STACIE SPRINGFIELD
RESUME-STACIE SPRINGFIELD
 
Microsoft Word - End Point Info
Microsoft Word - End Point InfoMicrosoft Word - End Point Info
Microsoft Word - End Point Info
 
rest without put
rest without putrest without put
rest without put
 
Nikunj Tak ,Project on HTML and CSS (Farming website) ,Final Year BCA ,Dezyne...
Nikunj Tak ,Project on HTML and CSS (Farming website) ,Final Year BCA ,Dezyne...Nikunj Tak ,Project on HTML and CSS (Farming website) ,Final Year BCA ,Dezyne...
Nikunj Tak ,Project on HTML and CSS (Farming website) ,Final Year BCA ,Dezyne...
 
день снега
день снегадень снега
день снега
 
Matt Smull Resume
Matt Smull ResumeMatt Smull Resume
Matt Smull Resume
 
Uso de tecnología ii unidad 1 act. 1
Uso de tecnología ii unidad 1 act. 1Uso de tecnología ii unidad 1 act. 1
Uso de tecnología ii unidad 1 act. 1
 
Stateless authentication for microservices
Stateless authentication for microservicesStateless authentication for microservices
Stateless authentication for microservices
 
Dockercon State of the Art in Microservices
Dockercon State of the Art in MicroservicesDockercon State of the Art in Microservices
Dockercon State of the Art in Microservices
 
Principles of microservices velocity
Principles of microservices   velocityPrinciples of microservices   velocity
Principles of microservices velocity
 

Similar to Embracing the-power-of-refactor

Android Network library
Android Network libraryAndroid Network library
Android Network library
Faren faren
 
Advanced Java Practical File
Advanced Java Practical FileAdvanced Java Practical File
Advanced Java Practical File
Soumya Behera
 
Ss
SsSs
Hazelcast
HazelcastHazelcast
Hazelcast
oztalip
 
Anti patterns
Anti patternsAnti patterns
Anti patterns
Alex Tumanoff
 
help me Java projectI put problem and my own code in the linkmy .pdf
help me Java projectI put problem and my own code in the linkmy .pdfhelp me Java projectI put problem and my own code in the linkmy .pdf
help me Java projectI put problem and my own code in the linkmy .pdf
arihantmum
 
Client server part 12
Client server part 12Client server part 12
Client server part 12
fadlihulopi
 
java sockets
 java sockets java sockets
java sockets
Enam Ahmed Shahaz
 
Laporan multiclient chatting client server
Laporan multiclient chatting client serverLaporan multiclient chatting client server
Laporan multiclient chatting client server
trilestari08
 
Cascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUGCascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUG
Matthew McCullough
 
Advanced Java - Practical File
Advanced Java - Practical FileAdvanced Java - Practical File
Advanced Java - Practical File
Fahad Shaikh
 
QA Fest 2019. Saar Rachamim. Developing Tools, While Testing
QA Fest 2019. Saar Rachamim. Developing Tools, While TestingQA Fest 2019. Saar Rachamim. Developing Tools, While Testing
QA Fest 2019. Saar Rachamim. Developing Tools, While Testing
QAFest
 
Connecting to the network
Connecting to the networkConnecting to the network
Connecting to the network
Mu Chun Wang
 
Bare-knuckle web development
Bare-knuckle web developmentBare-knuckle web development
Bare-knuckle web development
Johannes Brodwall
 
Tugas 2
Tugas 2Tugas 2
Tugas 2
Novi_Wahyuni
 
Fia fabila
Fia fabilaFia fabila
Fia fabila
fiafabila
 
201913001 khairunnisa progres_harian
201913001 khairunnisa progres_harian201913001 khairunnisa progres_harian
201913001 khairunnisa progres_harian
KhairunnisaPekanbaru
 
Asynchronen Code testen
Asynchronen Code testenAsynchronen Code testen
Asynchronen Code testen
ndrssmn
 
Opa presentation at GamesJs
Opa presentation at GamesJsOpa presentation at GamesJs
Opa presentation at GamesJs
Henri Binsztok
 
Алексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhereАлексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhere
Sergey Platonov
 

Similar to Embracing the-power-of-refactor (20)

Android Network library
Android Network libraryAndroid Network library
Android Network library
 
Advanced Java Practical File
Advanced Java Practical FileAdvanced Java Practical File
Advanced Java Practical File
 
Ss
SsSs
Ss
 
Hazelcast
HazelcastHazelcast
Hazelcast
 
Anti patterns
Anti patternsAnti patterns
Anti patterns
 
help me Java projectI put problem and my own code in the linkmy .pdf
help me Java projectI put problem and my own code in the linkmy .pdfhelp me Java projectI put problem and my own code in the linkmy .pdf
help me Java projectI put problem and my own code in the linkmy .pdf
 
Client server part 12
Client server part 12Client server part 12
Client server part 12
 
java sockets
 java sockets java sockets
java sockets
 
Laporan multiclient chatting client server
Laporan multiclient chatting client serverLaporan multiclient chatting client server
Laporan multiclient chatting client server
 
Cascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUGCascading Through Hadoop for the Boulder JUG
Cascading Through Hadoop for the Boulder JUG
 
Advanced Java - Practical File
Advanced Java - Practical FileAdvanced Java - Practical File
Advanced Java - Practical File
 
QA Fest 2019. Saar Rachamim. Developing Tools, While Testing
QA Fest 2019. Saar Rachamim. Developing Tools, While TestingQA Fest 2019. Saar Rachamim. Developing Tools, While Testing
QA Fest 2019. Saar Rachamim. Developing Tools, While Testing
 
Connecting to the network
Connecting to the networkConnecting to the network
Connecting to the network
 
Bare-knuckle web development
Bare-knuckle web developmentBare-knuckle web development
Bare-knuckle web development
 
Tugas 2
Tugas 2Tugas 2
Tugas 2
 
Fia fabila
Fia fabilaFia fabila
Fia fabila
 
201913001 khairunnisa progres_harian
201913001 khairunnisa progres_harian201913001 khairunnisa progres_harian
201913001 khairunnisa progres_harian
 
Asynchronen Code testen
Asynchronen Code testenAsynchronen Code testen
Asynchronen Code testen
 
Opa presentation at GamesJs
Opa presentation at GamesJsOpa presentation at GamesJs
Opa presentation at GamesJs
 
Алексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhereАлексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhere
 

Recently uploaded

Nordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptxNordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptx
MichaelKnudsen27
 
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
saastr
 
Introduction of Cybersecurity with OSS at Code Europe 2024
Introduction of Cybersecurity with OSS  at Code Europe 2024Introduction of Cybersecurity with OSS  at Code Europe 2024
Introduction of Cybersecurity with OSS at Code Europe 2024
Hiroshi SHIBATA
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
shyamraj55
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
DanBrown980551
 
Skybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoptionSkybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoption
Tatiana Kojar
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
panagenda
 
Best 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERPBest 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERP
Pixlogix Infotech
 
Fueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte WebinarFueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte Webinar
Zilliz
 
Building Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and MilvusBuilding Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and Milvus
Zilliz
 
Ocean lotus Threat actors project by John Sitima 2024 (1).pptx
Ocean lotus Threat actors project by John Sitima 2024 (1).pptxOcean lotus Threat actors project by John Sitima 2024 (1).pptx
Ocean lotus Threat actors project by John Sitima 2024 (1).pptx
SitimaJohn
 
WeTestAthens: Postman's AI & Automation Techniques
WeTestAthens: Postman's AI & Automation TechniquesWeTestAthens: Postman's AI & Automation Techniques
WeTestAthens: Postman's AI & Automation Techniques
Postman
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
Octavian Nadolu
 
Taking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdfTaking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdf
ssuserfac0301
 
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development ProvidersYour One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
akankshawande
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
Zilliz
 
Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)
Jakub Marek
 
leewayhertz.com-AI in predictive maintenance Use cases technologies benefits ...
leewayhertz.com-AI in predictive maintenance Use cases technologies benefits ...leewayhertz.com-AI in predictive maintenance Use cases technologies benefits ...
leewayhertz.com-AI in predictive maintenance Use cases technologies benefits ...
alexjohnson7307
 
Trusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process MiningTrusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process Mining
LucaBarbaro3
 

Recently uploaded (20)

Nordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptxNordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptx
 
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
Deep Dive: AI-Powered Marketing to Get More Leads and Customers with HyperGro...
 
Introduction of Cybersecurity with OSS at Code Europe 2024
Introduction of Cybersecurity with OSS  at Code Europe 2024Introduction of Cybersecurity with OSS  at Code Europe 2024
Introduction of Cybersecurity with OSS at Code Europe 2024
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
 
Skybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoptionSkybuffer SAM4U tool for SAP license adoption
Skybuffer SAM4U tool for SAP license adoption
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
 
Best 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERPBest 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERP
 
Fueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte WebinarFueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte Webinar
 
Building Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and MilvusBuilding Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and Milvus
 
Ocean lotus Threat actors project by John Sitima 2024 (1).pptx
Ocean lotus Threat actors project by John Sitima 2024 (1).pptxOcean lotus Threat actors project by John Sitima 2024 (1).pptx
Ocean lotus Threat actors project by John Sitima 2024 (1).pptx
 
WeTestAthens: Postman's AI & Automation Techniques
WeTestAthens: Postman's AI & Automation TechniquesWeTestAthens: Postman's AI & Automation Techniques
WeTestAthens: Postman's AI & Automation Techniques
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
 
Taking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdfTaking AI to the Next Level in Manufacturing.pdf
Taking AI to the Next Level in Manufacturing.pdf
 
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development ProvidersYour One-Stop Shop for Python Success: Top 10 US Python Development Providers
Your One-Stop Shop for Python Success: Top 10 US Python Development Providers
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
 
Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)Main news related to the CCS TSI 2023 (2023/1695)
Main news related to the CCS TSI 2023 (2023/1695)
 
leewayhertz.com-AI in predictive maintenance Use cases technologies benefits ...
leewayhertz.com-AI in predictive maintenance Use cases technologies benefits ...leewayhertz.com-AI in predictive maintenance Use cases technologies benefits ...
leewayhertz.com-AI in predictive maintenance Use cases technologies benefits ...
 
Trusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process MiningTrusted Execution Environment for Decentralized Process Mining
Trusted Execution Environment for Decentralized Process Mining
 

Embracing the-power-of-refactor

  • 1. Embracing the Power of Refactor REN Xiaojun
  • 2. Code smells are heuristics for refactoring
  • 3. Our design communicates to us through resistance.
  • 5. Code is difficult to change
  • 7. This resistance is valuable feedback
  • 8. Code smells are hints from our software about how to reduce this resistance.
  • 9. This is one way our design emerges.
  • 10.
  • 13. Queue<String> queue = new LinkedBlockingQueue<>(); HttpClient httpclient = HttpClients.createDefault(); DatagramSocket socket = new DatagramSocket(6889);
  • 14. 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
  • 15. 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
  • 19. 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); } }
  • 20. 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(); } } } }
  • 22. 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
  • 23. public void start() { spawnWorkers(); while(true){ processRequests(); } }
  • 24. 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(); } }
  • 25. 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(); } }
  • 26. 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() { //… } }
  • 27. 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
  • 30. 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(); } }
  • 31. 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); } }
  • 32. public class PushDaemon { public PushDaemon() throws SocketException { queue = new LinkedBlockingQueue<>(); httpclient = HttpClients.createDefault(); socket = new DatagramSocket(6889); } }
  • 33. public class PushDaemon { public PushDaemon() throws SocketException { worker = new Worker(); socket = new DatagramSocket(6889); } }
  • 34. 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(); } }
  • 35. 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(); } }
  • 36. public class PushDaemon { public void start() { spawnWorkers(); while(true){processRequests();} } }
  • 37. public class PushDaemon { public void start() { worker.spawn(); while(true){processRequests();} } }
  • 38. public class PushDaemon { public PushDaemon() throws SocketException { worker = new Worker(); socket = new DatagramSocket(6889); } }
  • 39. 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(); } } }
  • 40. public class UDPServer { private final DatagramSocket socket; public UDPServer() throws SocketException { socket = new DatagramSocket(6889); } }
  • 41. 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
  • 42. 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); } }
  • 43. 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(); } }
  • 44. 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() { //… } }
  • 45. 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(); } }
  • 47.
  • 48.
  • 49. 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() { //… } }
  • 50. 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() { //… } }
  • 51. 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() { //… } }
  • 52. 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() { //… } }
  • 53. 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(); } }
  • 54. 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(); } }
  • 55. 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(); } }
  • 56. 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); } }
  • 57. 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(); } } }
  • 60. 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(); } } }
  • 61. 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()); } }
  • 62. 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(); } } }
  • 63. 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; } }
  • 64. 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(); } } }
  • 65. 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(); } }
  • 66. 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); } } }
  • 67. 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(); } }
  • 68. public interface Job { void run() throws IOException; } public class Ping implements Job { //… } public class Send implements Job { //… }
  • 69. 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); } } }
  • 71. 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; } }
  • 72. 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); } } }
  • 73. 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); } } }
  • 74. 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(); } }
  • 75. 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()); } } }
  • 76. 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()); } }
  • 77. 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; } }
  • 78. public interface Job { public static Job create(Client client, String message, UDPServer server) { //… } }
  • 79. 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()); } }
  • 81. Extracted objects tend to attract behaviour.
  • 82. 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()); } }
  • 83. 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()); } }
  • 84. 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()); } }
  • 85. public class Ping implements Job { public void run() throws IOException { client.send("PONG"); } }
  • 86. 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); } } }
  • 88. 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 + ""}}"; } }
  • 89. 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(); } } }
  • 91. we’re using simple data types to represent complex ideas
  • 92. 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(); } } }
  • 95. 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; } }
  • 96. 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); } }
  • 98. 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); } } }
  • 99. null communicates that an unknown command has been requested
  • 101. public class NullJob implements Job { @Override public void run() throws IOException { } }
  • 102. public class PushDaemon { public void call(String message, Client client) { Request request = new Request(message); Job job = Job.create(client, request); worker.accept(job); } }
  • 103. public class PushDaemon { public void call(String message, Client client) { Request request = new Request(message); Job job = Job.create(client, request); job.enqueueTo(worker); } }
  • 104. 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); } }
  • 105. public class NullJob implements Job { @Override public void run() throws IOException { //noop } @Override public void enqueueTo(Worker worker) { //noop } }
  • 107. Q&A