I have defined a data structure in Java that allows to manage certain type of elements like a Queue, with the special feature of using 2 inner rows (windows) to attend elements:
import java.util.ArrayList;
public class TwoWindowsQueue<E> {
enum WindowState {CLOSED, OPEN}
enum WindowTurn {WINDOW1, WINDOW2}
private ArrayList<E> window1, window2;
private WindowState windowState1, windowState2;
private WindowTurn turn;
public TwoWindowsQueue(E[] elements) {
setWindowState1(WindowState.OPEN);
setWindowState2(WindowState.OPEN);
setTurn(WindowTurn.WINDOW1);
for (E elem : elements) {
insert(elem);
}
}
public WindowTurn getTurn() {
return turn;
}
private void setTurn(WindowTurn turn) {
this.turn = turn;
}
public void closeQueue() {
if (pendingElementsInQueue() > 0)
throw new IllegalStateException("Elements are pending");
setWindowState1(WindowState.CLOSED);
setWindowState2(WindowState.CLOSED);
}
private void setWindowState1(WindowState state) {
windowState1 = state;
}
private void setWindowState2(WindowState state) {
windowState2 = state;
}
public boolean isWindow1Open() {
return windowState1 == WindowState.OPEN;
}
public boolean isWindow2Open() {
return windowState2 == WindowState.OPEN;
}
public void closeWindow() {
if (pendingElementsInWindow1() <= pendingElementsInWindow2()) {
transfer(window1, window2, pendingElementsInWindow1());
setWindowState1(WindowState.CLOSED);
setTurn(WindowTurn.WINDOW2);
} else {
transfer(window2, window1, pendingElementsInWindow2());
setWindowState2(WindowState.CLOSED);
setTurn(WindowTurn.WINDOW1);
}
}
public void openWindow() {
if (windowState1 == WindowState.CLOSED) {
setWindowState1(WindowState.OPEN);
return;
}
if (windowState2 == WindowState.CLOSED)
setWindowState2(WindowState.OPEN);
}
public boolean isInQueue(E elem) {
return window1.contains(elem) || window2.contains(elem);
}
public int pendingElementsInQueue() {
return pendingElementsInWindow1() + pendingElementsInWindow2();
}
public int pendingElementsInWindow1() {
return window1.size();
}
public int pendingElementsInWindow2() {
return window2.size();
}
public void insert(E elem) {
if (elem == null) throw new IllegalArgumentException("null");
validateWindowsClosed();
if (isInQueue(elem))
throw new IllegalStateException("Duplicate element");
if (isWindow1Open() && isWindow2Open()) {
if (pendingElementsInWindow1() <= pendingElementsInWindow2()) {
window1.add(elem);
} else {
window2.add(elem);
}
return;
}
if (isWindow1Open()) window1.add(elem);
if (isWindow2Open()) window2.add(elem);
}
public boolean isQueueClosed() {
return !isWindow1Open() && !isWindow2Open();
}
private void validateQueueEmpty() {
if (pendingElementsInQueue() == 0)
throw new IllegalStateException("Queue is empty");
}
private void validateWindowsClosed() {
if (isQueueClosed())
throw new IllegalStateException("Both windows are closed");
}
public E elementToServe() {
validateQueueEmpty();
validateWindowsClosed();
if (getTurn() == WindowTurn.WINDOW1) {
return window1.get(0);
} else {
return window2.get(0);
}
}
public void serve() {
validateQueueEmpty();
validateWindowsClosed();
if (getTurn() == WindowTurn.WINDOW1) {
window1.remove(0);
if (isWindow2Open()) setTurn(WindowTurn.WINDOW2);
return;
} else {
window2.remove(0);
if (isWindow1Open()) setTurn(WindowTurn.WINDOW1);
}
}
public void balance() {
if (!isWindow1Open() || !isWindow2Open())
throw new IllegalStateException("Cannot balance");
int diff = pendingElementsInWindow1() - pendingElementsInWindow2();
if (diff > 1) {
transfer(window1, window2, diff / 2);
} else if (diff < -1) {
transfer(window2, window1, diff / 2);
}
}
private void transfer(ArrayList<E> source, ArrayList<E> destination, int amount) {
while (amount > 0) {
destination.add(source.get(source.size() - amount));
source.remove(source.size() - amount);
amount--;
}
}
}
As you can see, I have used enums in an attempt to make the code easier to understand and maintain. But my question is, for the windows state and turn, is there any other alternative to enums that is Clean Code friendly?
Also, if enums are the only option, is there any way to simplify its usage?