Advanced Topic: Complex State and Decisions
If you code long enough, you know the feeling when a code fragment with decisions based on multiple state decisions just looks like spaghetti code. It starts out innocently enough as in (1*2 Decision States) :
if (isState1) {
... do A;
}
else {
... do B;
}
Then we add a second state and the code becomes (2*2 Decision States):
if (isState1) {
if (isState2) {
... do A;
}
else {
... do B;
}
else {
if (isState 2) [
... do C;
{
else {
... do D;
}
}
If you are only really looking for (isState1 && isState2) then you will be tempted to write code like:
if (isState1 && isState2) {
... Do Main Thing;
}
else if (!isState1) {
... Do Error1;
}
else if (!isState2) {
... Do Error2;
}
else {
... Do Error2 and Error 3;
}
This is still doable. But for three states there are six possible combinations (3*2). Now, how do we handle the decision tree?
One possible solution is to use binary values, masks and a switch. So if State1 is the lowest bit and State2 is the highest bit and if true is one and false is zero, we have
State1 (true) --> 01 --> 1 (int)
!State1 (false) --> 00 --> 0 (int)
State2 (true) --> 10 --> 2 (int)
!State2 (false) --> 00 --> 0 (int)
State1 (true) +State2 (true) --> 11 --> 3 (int)
!State1 (false) + State2 (true) --> 10 --> 2 (int)
State1 (true) +!State2 (false) -->01 --> 1 (int)
!State1 (false) +!State2 (false) --> 00 --> 0 (int)
An example:
// STATE MACHINE CODE HERE
public final static int EQUAL= 1; // 00000001 (bit one)
public final static int LENGTH= 2; // 000010 (bit two)
public final static int EQUAL_AND_LENGTH= 3; // 000011
public int getPasswordsEqualBitValue(String password1, String password2){ // bit one
boolean success= (String)password1 == (String)password2; // only works on type String String, works null,null
return success ? EQUAL : 0;
}
public int getPasswordLengthMinBitValue(String password, int minLength) { // bit two
boolean success= password.length() >= minLength;
return success ? LENGTH: 0;
}
Usage:
// UPDATE BUTTON HANDLER
final Button buttonUpdate= (Button)findViewById(R.id.ButtonPasswordUpdate);
buttonUpdate.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String password= editTextPasswordFirst.getText().toString();
String verify= editTextPasswordSecond.getText().toString();
// STATE MACHINE
int intPasswordsEqual= getPasswordsEqualBitValue(password,verify);
int intPasswordLengthMin= getPasswordLengthMinBitValue(password,PasswordState.MIN_PASSWORD_LENGTH);
int stateMachine= intPasswordsEqual+intPasswordLengthMin;
String err;
switch (stateMachine) {
case EQUAL_AND_LENGTH:
ResetTimeoutValues(timeoutType); // new password so calculate new timeout time
isValidKey= true;
PasswordState outPasswordState= new PasswordState(lengthKey,
timeExpire,
isValidKey,
timeoutType,
password,
isHashPassword);
Bundle b= new Bundle();
b.putSerializable("jalcomputing.confusetext.PasswordState", outPasswordState);
getIntent().putExtras(b);
setResult(RESULT_OK,getIntent()); // call home with data on success only
finish(); // go back
break;
case EQUAL: // so must be invalid length
err= "Invalid Password Length of "+
new Integer(password.length()).toString() +
". Password must be at least "+
new Integer(PasswordState.MIN_PASSWORD_LENGTH).toString()+" characters.";
editTextPasswordFirst.setError(err);
editTextPasswordSecond.setText("");
editTextPasswordFirst.requestFocus();
break;
case LENGTH: // so must be pw do not match
editTextPasswordFirst.setError("Entries did not match");
editTextPasswordSecond.setText("");
editTextPasswordFirst.requestFocus();
break;
default: // pw not equal and pw length invalid
err= "Invalid Password Length of "+
new Integer(password.length()).toString() +
". Password must be at least "+
new Integer(PasswordState.MIN_PASSWORD_LENGTH).toString()+" characters.\n"+
" Entries did not match.";
editTextPasswordFirst.setError(err);
editTextPasswordSecond.setText("");
editTextPasswordFirst.requestFocus();
break;
}
}
});
Hmmm.