Use while loop to avoid deeply nested if statements in java - java

Hi I have written a little function like
public void foo(MyClassA paraA) {
if (paraA == null) return;
MyClassB paraB = doSomeStuff(paraA);
if (paraB == null) return;
MyClassC paraC = doMoreStuff(paraB);
if (paraC == null) return;
....
}
The above fails fast and is nice to read (i.e. the intention to return on null values is clear). But now instead of simply returning, I want to do some error logging, so I changed to
public void foo(MyClassA paraA) {
if (paraA == null) {doLog(); return;}
MyClassB paraB = doSomeStuff(paraA);
if (paraB == null) {doLog(); return;}
MyClassC paraC = doMoreStuff(paraB);
if (paraC == null) {doLog(); return;}
....
}
The above is also clean and easy to read, but I have to repeat doLog() a couple of times. So I change again to
public void foo(MyClassA paraA) {
if (paraA != null) {
MyClassB paraB = doSomeStuff(paraA);
if (paraB != null) {
MyClassC paraC = doMoreStuff(paraB);
if (paraC != null) {
....
return;
}
}
}
doLog();
}
The above calls doLog() only just once but I ended with some deeply-nested if statements, which are very ugly and hard to read. So how do I keep the same cleanliness like before and have doLog() just once? Note that returning something else rather than void for foo() is not allowed. And I also read that using try/catch as oppose to null check is an anti-pattern.
If I am to try, I want to write something like
public void foo(MyClassA paraA) {
while(true) {
if (paraA == null) break;
MyClassB paraB = doSomeStuff(paraA);
if (paraB == null) break;
MyClassC paraC = doMoreStuff(paraB);
if (paraC == null) break;
....
return;
}
doLog();
}
The above fulfills all my needs(fail fast, clean, no nested if), but is the use of the while loop here an anti-pattern as the while loop here is never meant to run more than once?

Java has a nifty labeled break construct that might help you, here.
public void foo(MyClassA paraA) {
block: {
if (paraA == null) { break block; }
MyClassB paraB = doSomeStuff(paraA);
if (paraB == null) { break block; }
MyClassC paraC = doMoreStuff(paraB);
if (paraC == null) { break block; }
...
return;
}
doLog();
}
If you used polymorphism better, you could do this:
public void foo(MyInterface para) {
while (para != null) {
para = para.doStuff();
}
doLog();
}
If you absolutely can't use polymorphism like that, use a dispatcher.
But I've seen this before, and it looks like a state machine. Give "java enum state machine" a search. I have a feeling that's what you're really trying to do.

Do you think that this is clean
public void foo(MyClassA paraA) {
MyClassB paraB = paraA != null?doSomeStuff(paraA):null;
MyClassC paraC = paraB != null?doMoreStuff(paraB):null;
if (paraC != null) {
....
}
doLog();
}

IMHO your second code snippet is what ypu should do.
Do not try to make your code short. This is an antipattern.
if (a==null) {
log("Failed in step a");
return;
}
B b = a.doSomething();
is very fast to read and understand. You save nothing by compressing this code. Zero. Nada. Leave it to Hotspot VM, and focus on making code understandable. "if null then log return" is a classic, well understood and accepted pattern.
It had become popular to try to make code "readable" with lambda antipatterns like this:
B b = ifNullLog(a, () -> a.doSomething())
where
T ifNullLog(Object guard, Function<T> func) {
if (guard == null) { doLog(); return null; }
return func.run();
}
but IMHO this is a total antipattern. In fact, it is a best practise to even require braces for every if, else, for, while to make it easy to insert such a log statement without risking to break the code.
Code like your first snippet:
if (a == null) return;
are dangerous. Look at various bugs like Apples SSL disaster
If someone adds doLog without noticing the missing brackets, the function will always return null. The apple SSL bug (or was it heartbleed?) esdentially was a
if (a==null)
return;
return;
B b = a.doSomething();
See how subtle the bug is? You Java compiler will fortunately warn you if it's about unreachable code - it will not necessarily warn you otherwise... by always using brackets and well formatted code such bugs can easily be avoided. Format code to avoid errors, not for aestetics.
It is also well acceptable to use return codes. Just don't make success default (see heartbleed again).
Code c = execute(a);
if (c != Code.SUCCESS) {
doLog(c);
return;
}
where
Code execute(A a) {
if (a == null) { return Code.FAILED_A_NULL; }
B b = a.doSomething();
if (b == null) { return Code.FAILED_B_NULL; }
...
return Code.SUCCESS;
}
A classic use case of "return", another good pattern.

Related

SonarQube Refactor this method to reduce its Cognitive Complexity

I have the below utility method and I am using multiple if statements and getting cognitive complexity issue. I went through some links, but I am not able to understand how should I change my code without affecting users of this method.
public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken){
String key=null;
boolean isValidWrapper = false;
if (wrapper != null && wrapper.length() > 7
&& wrapper.substring(0, 6).equalsIgnoreCase("XYZ"))
{
wrapper= wrapper.substring(7, wrapper.lastIndexOf('.')+1);
}
if(wrapper != null && wrapper.equalsIgnoreCase("TFR")) {
isValidWrapper=Boolean.TRUE;
}
try {
key = wrapper.getKey();
}
catch (Exception exception) {
return isValidWrapper;
}
if(key!=null) {
Date tokenExpiryTime = key.getExpiresAt();
if(tokenExpiryTime!=null) {
return isValidWrapper;
}
String algorithm=key.getAlgorithm();
if(!DESIRED_ALGO.equals(algorithm)) {
return isValidWrapper;
}
String value6=key.getType();
if(!DESIRED_TYPE.equals(value6)) {
return isValidWrapper;
}
if(key.getValue1()!=null && key.getValue2().size()>0 && key.getValue3()!=null && key.getValue4()!=null && key.getValue5()!=null) {
isValidWrapper=Boolean.TRUE;
}
}
return isValidWrapper;
}
Please share your suggestions to refactor this code.
I don't think that merging many if conditions to one or simply do a code clean up, for example by changing the order of some instructions, can solve your problem.
Your code does not match the single responsibility principle. You should refactor this big method to smaller parts. Due to this it will testable, easier to maintain and read. I spent some time and did this:
public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken) {
final WrapperClass unpackedWrapper = unpackWrapper(wrapper);
boolean wrapperValid = isUnpackedWrapperValid(unpackedWrapper);
Key key = null;
try {
key = unpackedWrapper.getKey();
} catch (final Exception exception) {
return wrapperValid;
}
if (key != null) {
if (doesKeyMeetsBasicConditions(key)) {
return wrapperValid;
}
if (doesKeyMeetsValueConditions(key)) {
return true;
}
}
return wrapperValid;
}
protected static WrapperClass unpackWrapper(final WrapperClass wrapper) {
if (wrapper != null && wrapper.length() > 7 && wrapper.substring(0, 6).equalsIgnoreCase("XYZ")) {
return wrapper.substring(7, wrapper.lastIndexOf('.') + 1);
}
return wrapper;
}
protected static boolean isUnpackedWrapperValid(final WrapperClass wrapper) {
return wrapper != null && wrapper.equalsIgnoreCase("TFR");
}
protected static boolean doesKeyMeetsBasicConditions(final Key key) {
Date tokenExpiryTime = key.getExpiresAt();
if (tokenExpiryTime != null) {
return true;
}
String algorithm = key.getAlgorithm();
if (!DESIRED_ALGO.equals(algorithm)) {
return true;
}
String value6 = key.getType();
return !DESIRED_TYPE.equals(value6);
}
protected static boolean doesKeyMeetsValueConditions(final Key key) {
return key.getValue1() != null && key.getValue2().size() > 0
&& key.getValue3() != null && key.getValue4() != null
&& key.getValue5() != null;
}
I don't know the domain logic, so some of my methods have stupid names etc. As you can see, now you have a lot of smaller methods with not many branches (if conditions) - easier to test (a static code is not nice, but you can mock it by using for example PowerMock).
A bit of rewriting delivered a simplification, that still could be improved upon.
public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken){
if (wrapper != null && wrapper.length() > 7
&& wrapper.substring(0, 6).equalsIgnoreCase("XYZ"))
{
wrapper = wrapper.substring(7, wrapper.lastIndexOf('.')+1);
}
boolean isValidWrapper = wrapper != null && wrapper.equalsIgnoreCase("TFR");
try {
String key = wrapper.getKey();
if (key != null && key.getExpiresAt() == null
&& DESIRED_ALGO.equals(key.getAlgorithm())
&& DESIRED_TYPE.equals(key.getType())
&& key.getValue1() != null && !key.getValue2().isEmpty()
&& key.getValue3() != null && key.getValue4() != null
&& key.getValue5() != null) {
isValidWrapper = true;
}
}
catch (Exception exception) {
// DO NOTHING
}
return isValidWrapper;
}
After comment: here I catch any exception for all calls.
First of all, Sonar should give you more flags: reusing the wrapper parameter is usually a bad practice, NPE where invoking wrapper.getKey because wrapper can be null, but anyway, not the point...
Try reducing the number of if statements by creating local boolean variables (or possibly 1 big if statement if you have less than 5 or 6 tests, but often less readable). Once it's done, you should only have 1 block testing these boolean variables, and have one return statement, like the example above (not necessarily accurate!):
boolean expired = tokenExpiryTime != null;
boolean desiredAlgo = DESIRED_ALGO.equals(key.getAlgorithm());
boolean desiredType = DESIRED_TYPE.equals(value6);
if (expired || !desiredAlgo || !desiredType) {
return isValidWrapper;
}
However, your Cognitive complexity level seems pretty low if this kind of algorithm triggers it...
Another big way to reduce an algorithm complexity is to turn sub-blocks of code (loops, if and try-catch) into private methods. In your example, it could be something like a checkWrapperValidity method, responsible for every test returning isValidWrapper

Best practice : get deep value of an object

Often in java I have to get a value of a property of an object which is deep in this object. For example, if I'm sure that all my sub-objects are not null, I can do that :
public function getDeepValue(A a) {
String value = a.getB().getC().getListeD().get(0).getE().getValue();
return value;
}
But in case of sub objects of the parent can be null, I have to test every object.
To do that, I see 2/3 solutions :
First, step by step :
public function getDeepValue(A a) {
if(a == null){
return null;
}
B b = a.getB();
if(b == null) {
return null;
}
C c = b.getC();
if(c == null){
return null;
}
List<D> ds = c.getListeD();
if(ds == null || ds.size() == 0){
return null;
}
D d = ds.get(0);
if(d == null) {
return null;
}
E e = d.getE()
if(e == null){
return null;
}
return e.getValue();
}
Second, test all in one if block, soooo dirty :
public function getDeepValue(A a) {
if(a != null && a.getB() != null && a.getB().getC() != null && a.getB().getC().getListeD() != null && a.getB().getC().getListeD().size() > 0 && a.getB().getC().getListeD().get(0) != null && a.getB().getC().getListeD().get(0).getE() != null){
return a.getB().getC().getListeD().get(0).getE().getValue();
}
return null;
}
Third solution, using a try catch block :
public function getDeepValue(A a) {
try {
return a.getB().getC().getListeD().get(0).getE().getValue();
} catch(NullPointerException e) {
return null;
} catch(IndexOutOfBoundsException e) {
return null;
}
}
Solution 1 seems not too bad but needs a lot of code. It is generally the solution I use.
Solution 2 is for me really dirty...
In paper, I realy like solution 3, but is it a good solution in term of performances ?
Is there any others acceptables solutions ?
Thanks for help, I hope my english is not too bad..
Regards
Solution #3 looks simple, but it can potentially hide a whole host of problems. It might be an adequate solution if you have full access to all of the classes in the chain and you know what's going on in each method and you can guarantee those methods won't cause problems with your try/catch and you're never going to change them... that's a lot of conditions to make it a worthwhile solution, but I can conceive that it's possibly a useful sufficient one.
Solution #2 looks horrid to me, especially if one or more of the get methods is a bottleneck (such as a slow database query or using a blocking network connection). The earlier in the chain such a potential bottleneck, the worse it would potentially be, as you're calling it over and over again. This of course depends on the implementation of the methods in question (even if one of them is slow, the result could be cached, for example), but you shouldn't need to know that in your client code. Even with efficient or trivial implementations, you've still got the overhead of repeated method calls you oughtn't need.
Solution #1 is the best of the three, but it's likely not the best possible. This solution takes more lines of code than the other two, but it doesn't repeat itself and it isn't going to be tripped up by the implementations of the other methods. (Note: If you do not have access to the classes in the chain for refactoring, I would use this solution.)
A better solution than #1 would be to refactor the classes so that the client code doesn't need to know about this chain at all. Something along these lines:
class Client {
public Mumble getDeepValue(A a) { return a == null ? null : a.getDeepValue(); }
}
class A {
private B b;
public Mumble getDeepValue() { return b == null ? null : b.getDeepValue(); }
}
class B {
private C c;
public Mumble getDeepValue() { return c == null ? null : c.getDeepValue(); }
}
class C {
private List<D> ds;
public Mumble getDeepValue() {
D d = ds == null || ds.size() == 0 ? null : ds.get(0);
return d == null ? null : d.getDeepValue();
}
}
class D {
private E e;
public Mumble getDeepValue() { return e == null ? null : e.getMumble(); }
}
class E {
private Mumble m;
public Mumble getMumble() { return m; }
}
As you can see, the longest chain any of these classes has is to access the public members of an element of a collection that is a private member of the class. (Essentially ds.get(0).getDeepValue()) The client code doesn't know how deep the rabbit hole goes, only that A exposes a method which returns a Mumble. Client doesn't even need to know that the classes B, C, D, E, or List exist anywhere!
Additionally, if I were designing this system from the ground up, I would take a good long look at whether it could be restructured such that the actual Mumble object wasn't so deep. If I could reasonably get away with storing the Mumble within A or B, I'd recommend doing it. Depending on the application, that may not be possible however.
in terms of performance solution 3 is the best one. In addition It is neat and easy to understand , For example looking at a loop example:
int[] b = somevalue;
for(int i=0;i<b.length;i++){
//do something
}
in this case for every iteration we execute the condition. However, there is another approach for it which uses try and catch
int[] b = somevalue;
try{
for(int i=0;;i++){
//do something
}
}catch(IndexOutOfBoundException e){
// do something
}
on the second solution,the loop keeps going until we reach the end of the loop which then it throws IndexOutOfBoundException as soon as we reach the end of the array. meaning we don't check for the condition no more. thus faster.

whether to use arraylist or some other collection while iterating

I am having two profiles say SignProfile and ValidationProfile. These profiles can be more than 1, say
SignProfile.size >1 and ValidationProfile.size>1
Now there is a piece of code which I have to execute whenever SignProfile and ValidationProfile are not null and I need to loop through these profiles too if they are more than one for both Sign and Validation.
I am using
List<SignProfile> SP = new ArrayList<>;
List<ValidationProfile> Vali = new ArrayList<>;
while SP_interator.hasnext
{
while Vali_interator.hasnext
{
// do something
}
// Piece of code
}
Now the problem I am facing is that sometimes SignProfile comes null, but ValidationProfile is not null. So as per above code, the intended piece of code is not executed.
Could someone please guide me? Shall I use some other collection?
If you're on Java 5 or higher, you can use the foreach syntax:
List<SignProfile> signProfileList = new ArrayList<>;
List<ValidationProfile> validationProfileList = new ArrayList<>;
if(signProfileList != null) {
for(SignProfile sp : signProfileList) {
// you can work with the sp variable here, which represents the current item from signProfileList
if(validationProfileList != null) {
for(ValidationProfile vp : validationProfileList) {
// you can work with vp variable here, which represents the current item from validationProfileList
}
}
}
}
Note: I've renamed some of your variables to make the code a little clearer.
By the way, are you sure you want these nested? They don't seem to relate to eachother.
Is this what you mean?
if (signProfiles != null && validationProfiles != null) {
for (SignProfile signProfile : signProfiles) {
//do something with signProfile
}
for (ValidationProfile validationProfile : validationProfiles) {
//do something with validationProfile
}
}
Or the nested version...
if (signProfiles != null && validationProfiles != null) {
for (SignProfile signProfile : signProfiles) {
//do something with signProfile
for (ValidationProfile validationProfile : validationProfiles) {
//do something with validationProfile, and signProfile?
}
}
}

My method for finding a node in a double linked list isn't working properly and I don't know why?

Here's the code
public void findDNode(String name)
{
DNode u = header;
while(u != null)
{
if(name == u.getElement())
{
System.out.println(u.getElement());
break;
}
else if (u == null)
{
System.out.println("Error: not found");
break;
}
u = u.nextNode();
}
}
For some reason when the node that I am looking for doesn't exist it's doesn't print the error: not found message.
edit: nevermind just realised when u== null the while loop won't happen
You should use equals() to compare Java strings:
if (name.equals(u.getElement()))
{
...
Comparing strings using the == operator compares the references, which in most cases isn't the right thing to do.
Also, the "not found" logic is misplaced. It should probably be placed outside the loop (with an appropriate if condition).
u never becomes null inside the loop! Perform the check after the loop.
public void findDNode(String name)
{
DNode u = header;
while(u != null)
{
if(name == u.getElement())
{
System.out.println(u.getElement());
break;
}
u = u.nextNode();
}
if (u==null)
System.out.println("Error: not found");
}
Edit: and yes, you should use equals()
Other than using equals() instead of == to compare String there is another issue.
How can you have if (u == null) inside a while(u != null) loop. That if (u == null) block will never execute since while loop will end when u == null. That is the reason Error: not found is never printed.

Simplify java if statement

Can I simplify this java if construct? It seems too verbose to me, I'd like to have it shorter.
A is persistent Object, which will be null if it's context is accessed first time. Than A is instatniated and given content, and if this fails, some backup content is given to A.
if (A == null) {
A = staticGetMethod();
if (A == null) A = new BackupAContent() { ... };
}
Update: Or you could simply remove the nesting as it will still behave the same way.
if (A == null) {
A = staticGetMethod();
}
if (A == null) {
new BackupAContent() { ... };
}
Should work:
if (A == null && (A = staticGetMethod()) == null) {
new BackupAContent() { ... };
}
Put your building logic in factory method
if (objA == null) {
objA = getAInstance();
}
encapsulate the code suggested by Charles into a method to implement Factory_method_pattern
You can use a ternary operator instead of the if statements:
a = a ? a : staticGetMethod();
a = a ? a : new BackupAContent();
That said, I'd stick with what you've got, to be honest -- except that I would add a block for the second conditional rather than putting the statement inline with it.
This is Charles Goodwin's code with a slight change:
if (A == null && (A = staticGetMethod()) == null) {
new BackupAContent() { ... };
}
I used an AND instead of an OR
I think this is the best way to do it:
if(A == null)
{
if((A = staticGetMethod()) == null) A = new BackupAContent() { ... };
}

Categories