SlideShare ist ein Scribd-Unternehmen logo
1 von 107
Downloaden Sie, um offline zu lesen
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

Weitere ähnliche Inhalte

Was ist angesagt?

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
 

Was ist angesagt? (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
 

Andere mochten auch

Microsoft Word - Preview
Microsoft Word - PreviewMicrosoft Word - Preview
Microsoft Word - Preview
Andre Triputra
 
Microsoft Word - End Point Info
Microsoft Word - End Point InfoMicrosoft Word - End Point Info
Microsoft Word - End Point Info
Andre Triputra
 
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
 
Principles of microservices velocity
Principles of microservices   velocityPrinciples of microservices   velocity
Principles of microservices velocity
Sam Newman
 

Andere mochten auch (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
 

Ähnlich wie Embracing the-power-of-refactor

Advanced Java Practical File
Advanced Java Practical FileAdvanced Java Practical File
Advanced Java Practical File
Soumya Behera
 
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
 
Laporan multiclient chatting client server
Laporan multiclient chatting client serverLaporan multiclient chatting client server
Laporan multiclient chatting client server
trilestari08
 
Connecting to the network
Connecting to the networkConnecting to the network
Connecting to the network
Mu Chun Wang
 
Opa presentation at GamesJs
Opa presentation at GamesJsOpa presentation at GamesJs
Opa presentation at GamesJs
Henri Binsztok
 

Ähnlich wie 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
 
201913001 khairunnisa progres_harian
201913001 khairunnisa progres_harian201913001 khairunnisa progres_harian
201913001 khairunnisa progres_harian
 
Fia fabila
Fia fabilaFia fabila
Fia fabila
 
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
 

Kürzlich hochgeladen

CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
giselly40
 

Kürzlich hochgeladen (20)

Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Evaluating the top large language models.pdf
Evaluating the top large language models.pdfEvaluating the top large language models.pdf
Evaluating the top large language models.pdf
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 

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