1

Let's say I have few entity classes:

class EntityA { LocalDate date1; LocalDate date2; }
class EntityB { }
class EntityC { LocalDate date1; }

and I want to pass them through some logic containing lots of conditional checks. Simplified version of something like:

public class Predicate {
 public boolean test(EntityA a, EntityB b, EntityB c) {
 boolean aDate1Valid = isDateValid(a.getDate1()); 
 boolean bExists = b != null;
 boolean cExists = c != null;
 return (aDate1Valid && bExists && cExists && isDateValid(c.getDate1()))
 || (aDate1Valid && !bExists)
 || (aDate1Valid && !cExists && isDateValid(a.getDate2()));
 }
}

currently I'm retrieving all entities in advance before executing the test() method:

public static void main(String[] args) {
 Predicate predicate = new Predicate();
 EntityA a = loadEntityAFromDB1();
 EntityB b = loadEntityBFromWebService1();
 EntityB c = loadEntityCFromWebService2();
 predicate.test(a, b, c);
}

The problem is loading all entities is quite expensive (it involves DB queries, few external API calls etc.) and my Predicate's logic doesn't necessarily need to check all of them - for example when isDateValid(a.getDate1()) is not true, then nothing else needs to be checked.

Is there any better way to design this to load required data in more lazy way and to keep my business logic clean from data access logic?

asked Jun 17, 2021 at 11:56

3 Answers 3

3

Yes, one alternative is to design your code around behavior instead of data.

What I mean is to implement some use-case specifically, without trying to be generic, or trying to re-use parts. Just implement the check where it needs to be with whatever specific tools, like webservice calls or database access, specifically tailored to the task at hand.

Generalizing data access to the point of being unable to optimize or use specific backend features is the problem. Don't generalize in advance. Do it when you need it, and only to the amount you need it.

More specifically: don't do "data" classes (you called them entities). Those by their nature try to be "generic", to fit multiple use-cases, or even be used across applications. Instead, have classes contain behavior (i.e. logic), that is not re-usable, but therefore much more easy to optimize and maintain.

answered Jun 17, 2021 at 12:06
0

I'd suggest passing a function as an argument instead of the bool, so that you can execute part a, then ONLY do parts b and c IF a evaluated to true. Something like:

public class Predicate {

public boolean test(Func<EntityA> a, Func<EntityB> b, Func<EntityB> c) {
 boolean aDate1Valid = isDateValid(a.Invoke().getDate1()); 
 if(!aDate1Valid) return false;
 boolean bExists = b.Invoke() != null;
 if(!bExists) return true; // second condition
 boolean cExists = c.Invoke() != null;
 return (aDate1Valid && bExists && cExists && isDateValid(c.getDate1()))
 //|| (aDate1Valid && !bExists) // second condition can be removed
 || (aDate1Valid && !cExists && isDateValid(a.getDate2()));
}

}

answered Jun 17, 2021 at 12:03
0

The test case is forcing you to load all entities in advance

  • You should not load all entities.
  • Load a
  • If date is valid then load b, else return
  • If b is valid then load c

If the test case is a real business requirement then wrap the entities in a class that loads them lazily. I hope the entities can be abstracted into an interface so you don't have to code multiple lazy wrappers.

answered Jun 17, 2021 at 12:21

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.