Recently I was watching Clean Code video series on Design patterns. I discovered for myself a revolutionary design pattern – Actor Model. Which allows systems with large numbers of asynchronous threads to share a common stack.
The very basic idea behind the implementation of the Actor model is that we have a loop which executes commands on each object in the stack. Threads wait by having a command check for an event that puts itself back on the list if that event has not occurred. Loop doesn’t wait for the result of the execution. With such design, we don’t need multiple threads to execute each command. Hence, we make our single-threaded application “multi-threaded”. This design pattern influenced modern frameworks/systems like Reactive Programming.
In the example, I have created 5 Button objects, put them in a loop and if Button been pressed it will trigger execution of Light. If not it will put back Button object in the stack.
Command.java
public interface Command { void execute(); }
Commands.java
import java.util.ArrayList; import java.util.List; public class Commands { private static final List<Command> commandList = new ArrayList<>(); public static void add(Command command) { commandList.add(command); } public static int size() { return commandList.size(); } public static Command get(int i) { return commandList.get(i); } public static void remove(int i) { commandList.remove(i); } }
IO.java
import java.util.HashMap; import java.util.Map; public class IO { public static final int BUTTON1_ADDRESS = 0x01; public static final int BUTTON2_ADDRESS = 0x02; public static final int BUTTON3_ADDRESS = 0x03; public static final int BUTTON4_ADDRESS = 0x04; public static final int BUTTON5_ADDRESS = 0x05; public static final int LIGHT1_ADDRESS = 0x11; public static final int LIGHT2_ADDRESS = 0x12; public static final int LIGHT3_ADDRESS = 0x13; public static final int LIGHT4_ADDRESS = 0x14; public static final int LIGHT5_ADDRESS = 0x15; public static final Map<Integer, Integer> addresses = new HashMap<>(); static { addresses.put(BUTTON1_ADDRESS, 0); addresses.put(BUTTON2_ADDRESS, 0); addresses.put(BUTTON3_ADDRESS, 0); addresses.put(BUTTON4_ADDRESS, 0); addresses.put(BUTTON5_ADDRESS, 1);//not pressed button } public static int in(int ioAddress) { return addresses.get(ioAddress); } public static void out(int ioAddress, int data) { addresses.put(ioAddress, data); } }
LightCommand.java
import java.time.Instant; public class LightCommand implements Command { private int ioAddress; public LightCommand(int ioAddress) { this.ioAddress = ioAddress; } @Override public void execute() { IO.out(ioAddress, 1); System.out.println("Light On : " + Instant.now()); System.out.println("------------------------------"); } @Override public String toString() { return "LightCommand{" + "ioAddress=" + ioAddress + '}'; } }
ButtonCommand.java
public class ButtonCommand implements Command { private int buttonAddr; private Command onPress; public ButtonCommand(int buttonAddr, Command onPress) { this.buttonAddr = buttonAddr; this.onPress = onPress; } @Override public void execute() { Commands.add(buttonHasBeenPressed() ? onPress : this); } private boolean buttonHasBeenPressed() { int in = IO.in(this.buttonAddr); return in == 0; } @Override public String toString() { return "ButtonCommand{" + "buttonAddr=" + buttonAddr + ", onPress=" + onPress + '}'; } }
MainMethod.java
public class MainMethod { public static void main(String[] args) { Commands.add(new ButtonCommand(IO.BUTTON1_ADDRESS, new LightCommand(IO.LIGHT1_ADDRESS))); Commands.add(new ButtonCommand(IO.BUTTON2_ADDRESS, new LightCommand(IO.LIGHT2_ADDRESS))); Commands.add(new ButtonCommand(IO.BUTTON3_ADDRESS, new LightCommand(IO.LIGHT3_ADDRESS))); Commands.add(new ButtonCommand(IO.BUTTON4_ADDRESS, new LightCommand(IO.LIGHT4_ADDRESS))); Commands.add(new ButtonCommand(IO.BUTTON5_ADDRESS, new LightCommand(IO.LIGHT5_ADDRESS))); new Thread(() -> { while (Commands.size() != 0) { Command cmd = Commands.get(0); cmd.execute(); Commands.remove(0); } }).start(); new Thread(() -> { try { Thread.sleep(5000); } catch (InterruptedException ignored) {} IO.addresses.put(IO.BUTTON5_ADDRESS, 0); }).start(); } }
In the class above I have BUTTON5_ADDRESS not pressed since start of application. We wait for 5 sec until BUTTON5_ADDRESS was pressed and once it was “pressed” our loop executes it and exits application.
Light On : 2018-11-08T22:22:07.696Z ------------------------------ Light On : 2018-11-08T22:22:07.713Z ------------------------------ Light On : 2018-11-08T22:22:07.713Z ------------------------------ Light On : 2018-11-08T22:22:07.713Z ------------------------------ Light On : 2018-11-08T22:22:12.699Z ------------------------------ Process finished with exit code 0