Prevent coding multiple if/else statements with two changing expressions - java

I'm a beginner APEX developer (language based off of Java) and I was wondering if there is an efficient way to write conditional statements where two conditions change while the rest remains static.
For instance, in my code below, Countryy__c will change to say UK, US and Canada (besides France) and for each of these countries the Industry will change from Accounting to Medicine to Legal. Meanwhile, lead type and status will always remain as outbound and open respectively. Moreover each Country and Industry combination has a unique 'Owner ID'.
So in other words, there will be a total of 12 if/else statements with 12 different OwnerIds. Given that the code will be messy to maintain in the future if the number of countries and industries grow, is there a better way of coding this?
public static void changeOwnerToQueue(List<String> DeactivatedUserIds){
List<Lead> leadList = new List<Lead>();
List<lead> updatedQueue = new List<Lead>();
leadList = [SELECT Id, OwnerId, Countryy__c, Industry__c, Lead_Type__c, Status from lead
where OwnerId IN :DeactivatedUserIds];
for(Lead l : leadList){
if(l.Countryy__c == 'France' && l.Industry__c == 'Accounting' && l.Lead_Type__c == 'Outbound' && l.Status == 'Open'){
l.OwnerId = '00G5J000000pX41';
updatedQueue.add(l);
}
}

The most maintainable pattern for this kind of mapping in Apex is to use Custom Metadata. You'd create some Custom Metadata Type (MyOwnerMap__mdt), with fields for Country__c, Industry__c, and Owner__c. You'd create Custom Metadata records to represent all of your mappings. Then, in your code, you'd pull that data to create a Map, using a custom class as a key to represent the unique mapping of Country + Industry -> Owner:
class OwnerMapKey {
public String industry;
public String country;
public OwnerMapKey(String ind, String ctry) {
this.industry = ind;
this.country = ctry;
}
public Boolean equals(Object other) {
if (other instanceof OwnerMapKey) {
OwnerMapKey o = (OwnerMapKey)other;
return this.industry == o.industry && this.country == o.country;
}
return false;
}
public Integer hashCode() {
return (this.industry + this.country).hashCode();
}
}
List<MyOwnerMap__mdt> ownerMapValues = MyOwnerMap__mdt.getAll().values();
Map<OwnerMapKey, Id> ownerMap = new Map<OwnerMapKey, Id>();
for (MyOwnerMap__mdt eachOwnerMap: ownerMapValues) {
ownerMap.put(new OwnerMapKey(eachOwnerMap.Industry__c, eachOwnerMap.Country__c), eachOwnerMap.Owner__c);
}
Then, you can easily access the desired Owner value for any combination of Industry and Country. Note that you'll probably want to have a fallback if that entry is missing from your Custom Metadata.
someRecord.OwnerId = ownerMap.get(new OwnerMapKey(SOME_INDUSTRY, SOME_COUNTRY)) || defaultOwner;
(Disclaimer: above code written directly in Stack Overflow and untested).
The reason this pattern is valuable is that your solution then becomes admin-maintainable: you can change the mapping with no code changes and no deployment, just by altering the Custom Metadata records.

Related

How can I avoid repetitive if-else when validating fields in a DTO?

StudentDTO class having around 20 string attributes and each need to validate whether mandatory or not based on the logic given below in comments. This will make update method lengthy with too many if else's. Exception message should change based on the property evaluating. This code use Java 11.
// all fields except lastUpdated are string
public Student populateStudent(final StudentDTO studentDTO) {
Student student = new Student();
boolean dataUpdated = false;
/*
If mandatory parameter is:
1.) null : parameter is not updating
2.) empty : validate and throw an exception
3.) blank : validate and throw an exception
*/
if (isEmptyOrBlank(studentDTO.getName())) {
handleBadParam("Bad student name");
} else {
if (studentDTO.getName() != null) {
student.setName(studentDTO.getName());
dataUpdated = true;
}
}
if (isEmptyOrBlank(studentDTO.getBirthday())) {
handleBadParam("Bad student birthday");
} else {
if (studentDTO.getBirthday() != null) {
student.setBirthday(studentDTO.getBirthday());
dataUpdated = true;
}
}
// .... 20 other similar if-else statements later ....
// if atleast one parameter updated then date should update
if (dataUpdated) {
student.setLastUpdated(new Date());
}
return student;
}
private boolean isEmptyOrBlank(String name) {
return name != null && (name.isEmpty() || isBlank(name));
}
private void handleBadParam(String messgae) {
throw new IllegalArgumentException(messgae);
}
private boolean isBlank(String name) {
return (name.trim().length() == 0);
}
It seems you are validating your object.
I will not share any code example, I will just share an design opinion. By the way while designing your application, you should follow a design principle. So SOLID design principles is the commonly accepted, and you can apply these principles to your app while designing it.
You may create a class like StudentValidator so it's job must be only validating the Student object. So you realize first principle of solid's single responsibility.
And also that StudentValidator class will have methods which validations you need. And after all that implementations, you can cover in a method for each validation or you may call them when needed line.
Also there are many design patterns to avoid if-else statements via implementing patterns. Like command pattern, using enums etc.
I would strongly recommend to use the Java environment JSR 303 Bean Validation.The javax.validation packages provide developers with a standardized way of doing so. Fields that have to fulfill certain criteria receive the corresponding annotations, e.g. #NotNull, and these are then evaluated by the framework. Naturally, for checking more specific conditions, there is the possibility of creating custom annotations and validators.
You could refer to this https://dzone.com/articles/bean-validation-made-simple.

Drools decision table and app no interaction

My simple app works fine with .drl, but I wanted to check it with decision tables. Unfortunately, no tweaking and reading resolved the problem. I read that decision tables are not suitable to all kinds of tasks – are they in my case?
public static class UserInfo {
public enum Category {
var_1, var_2, var_3, var_4, var_5
}
public enum Degree {
high, medium, low;
}
}
My .drl looks like this:
item : UserInfo( variable_n == UserInfo.Category.var_1 ,
level_n == UserInfo.Degree.high) and
item2 : UserInfo( variable_n == UserInfo.Category.var_2 ,
level_n == UserInfo.Degree.high) etc...
The shortened decision table:
.

Object-Oriented design simulating a process with states [duplicate]

I have something to do for work and I need your help.
We want to implement a FSM - Finite State Machine, to identify char sequence(like: A, B, C, A, C), and tell if it accepted.
We think to implement three classes: State, Event and Machine.
The state class presents a node in the FSM, we thought to implement it with State design pattern, every node will extend from the abstract class state and every class would handle different types of events and indicate transitions to a new state. Is it good idea in your opinion?
Second thing, we don't know how to save all the transitions. Again we thought to implement it with some kind of map, that hold the starting point and gets some kind of vector with the next states, but I'm not sure thats a good idea.
I would be happy to get some ideas of how to implement it or maybe you can give me some starting points.
How should I save the FSM, meaning how should I build the tree at the beginning of the program?
I googled it and found a lot of examples but nothing that helps me.
Thanks a lot.
The heart of a state machine is the transition table, which takes a state and a symbol (what you're calling an event) to a new state. That's just a two-index array of states. For sanity and type safety, declare the states and symbols as enumerations. I always add a "length" member in some way (language-specific) for checking array bounds. When I've hand-coded FSM's, I format the code in row and column format with whitespace fiddling. The other elements of a state machine are the initial state and the set of accepting states. The most direct implementation of the set of accepting states is an array of booleans indexed by the states. In Java, however, enumerations are classes, and you can specify an argument "accepting" in the declaration for each enumerated value and initialize it in the constructor for the enumeration.
For the machine type, you can write it as a generic class. It would take two type arguments, one for the states and one for the symbols, an array argument for the transition table, a single state for the initial. The only other detail (though it's critical) is that you have to call Enum.ordinal() to get an integer suitable for indexing the transition array, since you there's no syntax for directly declaring an array with a enumeration index (though there ought to be).
To preempt one issue, EnumMap won't work for the transition table, because the key required is a pair of enumeration values, not a single one.
enum State {
Initial( false ),
Final( true ),
Error( false );
static public final Integer length = 1 + Error.ordinal();
final boolean accepting;
State( boolean accepting ) {
this.accepting = accepting;
}
}
enum Symbol {
A, B, C;
static public final Integer length = 1 + C.ordinal();
}
State transition[][] = {
// A B C
{
State.Initial, State.Final, State.Error
}, {
State.Final, State.Initial, State.Error
}
};
EasyFSM is a dynamic Java Library which can be used to implement an FSM.
You can find documentation for the same at :
Finite State Machine in Java
Also, you can download the library at :
Java FSM Library : DynamicEasyFSM
You can implement Finite State Machine in two different ways.
Option 1:
Finite State machine with a pre-defined workflow : Recommended if you know all states in advance and state machine is almost fixed without any changes in future
Identify all possible states in your application
Identify all the events in your application
Identify all the conditions in your application, which may lead state transition
Occurrence of an event may cause transitions of state
Build a finite state machine by deciding a workflow of states & transitions.
e.g If an event 1 occurs at State 1, the state will be updated and machine state may still be in state 1.
If an event 2 occurs at State 1, on some condition evaluation, the system will move from State 1 to State 2
This design is based on State and Context patterns.
Have a look at Finite State Machine prototype classes.
Option 2:
Behavioural trees: Recommended if there are frequent changes to state machine workflow. You can dynamically add new behaviour without breaking the tree.
The base Task class provides a interface for all these tasks, the leaf tasks are the ones just mentioned, and the parent tasks are the interior nodes that decide which task to execute next.
The Tasks have only the logic they need to actually do what is required of them, all the decision logic of whether a task has started or not, if it needs to update, if it has finished with success, etc. is grouped in the TaskController class, and added by composition.
The decorators are tasks that “decorate” another class by wrapping over it and giving it additional logic.
Finally, the Blackboard class is a class owned by the parent AI that every task has a reference to. It works as a knowledge database for all the leaf tasks
Have a look at this article by Jaime Barrachina Verdia for more details
Hmm, I would suggest that you use Flyweight to implement the states. Purpose: Avoid the memory overhead of a large number of small objects. State machines can get very, very big.
http://en.wikipedia.org/wiki/Flyweight_pattern
I'm not sure that I see the need to use design pattern State to implement the nodes. The nodes in a state machine are stateless. They just match the current input symbol to the available transitions from the current state. That is, unless I have entirely forgotten how they work (which is a definite possiblilty).
If I were coding it, I would do something like this:
interface FsmNode {
public boolean canConsume(Symbol sym);
public FsmNode consume(Symbol sym);
// Other methods here to identify the state we are in
}
List<Symbol> input = getSymbols();
FsmNode current = getStartState();
for (final Symbol sym : input) {
if (!current.canConsume(sym)) {
throw new RuntimeException("FSM node " + current + " can't consume symbol " + sym);
}
current = current.consume(sym);
}
System.out.println("FSM consumed all input, end state is " + current);
What would Flyweight do in this case? Well, underneath the FsmNode there would probably be something like this:
Map<Integer, Map<Symbol, Integer>> fsm; // A state is an Integer, the transitions are from symbol to state number
FsmState makeState(int stateNum) {
return new FsmState() {
public FsmState consume(final Symbol sym) {
final Map<Symbol, Integer> transitions = fsm.get(stateNum);
if (transisions == null) {
throw new RuntimeException("Illegal state number " + stateNum);
}
final Integer nextState = transitions.get(sym); // May be null if no transition
return nextState;
}
public boolean canConsume(final Symbol sym) {
return consume(sym) != null;
}
}
}
This creates the State objects on a need-to-use basis, It allows you to use a much more efficient underlying mechanism to store the actual state machine. The one I use here (Map(Integer, Map(Symbol, Integer))) is not particulary efficient.
Note that the Wikipedia page focuses on the cases where many somewhat similar objects share the similar data, as is the case in the String implementation in Java. In my opinion, Flyweight is a tad more general, and covers any on-demand creation of objects with a short life span (use more CPU to save on a more efficient underlying data structure).
Consider the easy, lightweight Java library EasyFlow. From their docs:
With EasyFlow you can:
implement complex logic but keep your code simple and clean
handle asynchronous calls with ease and elegance
avoid concurrency by using event-driven programming approach
avoid StackOverflow error by avoiding recursion
simplify design, programming and testing of complex java applications
I design & implemented a simple finite state machine example with java.
IFiniteStateMachine: The public interface to manage the finite state machine
such as add new states to the finite state machine or transit to next states by specific actions.
interface IFiniteStateMachine {
void setStartState(IState startState);
void setEndState(IState endState);
void addState(IState startState, IState newState, Action action);
void removeState(String targetStateDesc);
IState getCurrentState();
IState getStartState();
IState getEndState();
void transit(Action action);
}
IState: The public interface to get state related info
such as state name and mappings to connected states.
interface IState {
// Returns the mapping for which one action will lead to another state
Map<String, IState> getAdjacentStates();
String getStateDesc();
void addTransit(Action action, IState nextState);
void removeTransit(String targetStateDesc);
}
Action: the class which will cause the transition of states.
public class Action {
private String mActionName;
public Action(String actionName) {
mActionName = actionName;
}
String getActionName() {
return mActionName;
}
#Override
public String toString() {
return mActionName;
}
}
StateImpl: the implementation of IState. I applied data structure such as HashMap to keep Action-State mappings.
public class StateImpl implements IState {
private HashMap<String, IState> mMapping = new HashMap<>();
private String mStateName;
public StateImpl(String stateName) {
mStateName = stateName;
}
#Override
public Map<String, IState> getAdjacentStates() {
return mMapping;
}
#Override
public String getStateDesc() {
return mStateName;
}
#Override
public void addTransit(Action action, IState state) {
mMapping.put(action.toString(), state);
}
#Override
public void removeTransit(String targetStateDesc) {
// get action which directs to target state
String targetAction = null;
for (Map.Entry<String, IState> entry : mMapping.entrySet()) {
IState state = entry.getValue();
if (state.getStateDesc().equals(targetStateDesc)) {
targetAction = entry.getKey();
}
}
mMapping.remove(targetAction);
}
}
FiniteStateMachineImpl: Implementation of IFiniteStateMachine. I use ArrayList to keep all the states.
public class FiniteStateMachineImpl implements IFiniteStateMachine {
private IState mStartState;
private IState mEndState;
private IState mCurrentState;
private ArrayList<IState> mAllStates = new ArrayList<>();
private HashMap<String, ArrayList<IState>> mMapForAllStates = new HashMap<>();
public FiniteStateMachineImpl(){}
#Override
public void setStartState(IState startState) {
mStartState = startState;
mCurrentState = startState;
mAllStates.add(startState);
// todo: might have some value
mMapForAllStates.put(startState.getStateDesc(), new ArrayList<IState>());
}
#Override
public void setEndState(IState endState) {
mEndState = endState;
mAllStates.add(endState);
mMapForAllStates.put(endState.getStateDesc(), new ArrayList<IState>());
}
#Override
public void addState(IState startState, IState newState, Action action) {
// validate startState, newState and action
// update mapping in finite state machine
mAllStates.add(newState);
final String startStateDesc = startState.getStateDesc();
final String newStateDesc = newState.getStateDesc();
mMapForAllStates.put(newStateDesc, new ArrayList<IState>());
ArrayList<IState> adjacentStateList = null;
if (mMapForAllStates.containsKey(startStateDesc)) {
adjacentStateList = mMapForAllStates.get(startStateDesc);
adjacentStateList.add(newState);
} else {
mAllStates.add(startState);
adjacentStateList = new ArrayList<>();
adjacentStateList.add(newState);
}
mMapForAllStates.put(startStateDesc, adjacentStateList);
// update mapping in startState
for (IState state : mAllStates) {
boolean isStartState = state.getStateDesc().equals(startState.getStateDesc());
if (isStartState) {
startState.addTransit(action, newState);
}
}
}
#Override
public void removeState(String targetStateDesc) {
// validate state
if (!mMapForAllStates.containsKey(targetStateDesc)) {
throw new RuntimeException("Don't have state: " + targetStateDesc);
} else {
// remove from mapping
mMapForAllStates.remove(targetStateDesc);
}
// update all state
IState targetState = null;
for (IState state : mAllStates) {
if (state.getStateDesc().equals(targetStateDesc)) {
targetState = state;
} else {
state.removeTransit(targetStateDesc);
}
}
mAllStates.remove(targetState);
}
#Override
public IState getCurrentState() {
return mCurrentState;
}
#Override
public void transit(Action action) {
if (mCurrentState == null) {
throw new RuntimeException("Please setup start state");
}
Map<String, IState> localMapping = mCurrentState.getAdjacentStates();
if (localMapping.containsKey(action.toString())) {
mCurrentState = localMapping.get(action.toString());
} else {
throw new RuntimeException("No action start from current state");
}
}
#Override
public IState getStartState() {
return mStartState;
}
#Override
public IState getEndState() {
return mEndState;
}
}
example:
public class example {
public static void main(String[] args) {
System.out.println("Finite state machine!!!");
IState startState = new StateImpl("start");
IState endState = new StateImpl("end");
IFiniteStateMachine fsm = new FiniteStateMachineImpl();
fsm.setStartState(startState);
fsm.setEndState(endState);
IState middle1 = new StateImpl("middle1");
middle1.addTransit(new Action("path1"), endState);
fsm.addState(startState, middle1, new Action("path1"));
System.out.println(fsm.getCurrentState().getStateDesc());
fsm.transit(new Action(("path1")));
System.out.println(fsm.getCurrentState().getStateDesc());
fsm.addState(middle1, endState, new Action("path1-end"));
fsm.transit(new Action(("path1-end")));
System.out.println(fsm.getCurrentState().getStateDesc());
fsm.addState(endState, middle1, new Action("path1-end"));
}
}
Full example on Github
Well this is an old question but while nobody mentioned here, I will advice to check two existing frameworks before you implement you own State Machines.
One is Spring State Machine most of you are familiar with Spring framework, which allow us to use several features of Spring like dependency injection and everything else that Spring can offer.
It is really great for modelling the lifecycle of an Apparat, with states like INITIALIZING, STARTED, ERROR, RECOVERING, SHUTTINGDOWN, etc.. but I see lots of people are trying to model a Shopping Chart, a Reservation System with it, the memory footprint a Spring State Machine is relatively big to model millions of Shopping Charts or Reservations.
One other disadvantage, Spring State Machine, while has a capability to persist itself for long running processes but it does not have any mechanism to adapt to changes in these processes, if you persist a process and you have to recover it lets say 10 days later with a change occurred in your business process because of new software release / requirement, you have no built in means to deal with it.
I have several blogs, blog1 blog2, demonstrating how you can program Spring State Machine, specially model driven way, if you want to check it.
Mainly because the disadvantages I mentioned, I advice you to look another framework first, Akka FSM (Finite State Machine) which is more fitting with its low memory footprint to have millions and millions of instances and has a capability to adapt changing long running processes.
Now you can develop with Akka framework with Java but believe me because of some missing language elements, you don't want to read the produced code, Scala is a much more fitting language to develop with Akka. Now I hear you saying Scala is too complex, I can't convince my project leads to develop with Scala, to convince you all this is an option, I developed a Proof of Concept application using a Java/Scala hybrid with all Scala Akka Finite State Machine code generated from an UML model, if you want to check it out here the links to the blogs, blog3 blog4.
I hope this information would help you.
Here is a SUPER SIMPLE implementation/example of a FSM using just "if-else"s which avoids all of the above subclassing answers (taken from Using Finite State Machines for Pattern Matching in Java, where he is looking for a string which ends with "#" followed by numbers followed by "#"--see state graph here):
public static void main(String[] args) {
String s = "A1#312#";
String digits = "0123456789";
int state = 0;
for (int ind = 0; ind < s.length(); ind++) {
if (state == 0) {
if (s.charAt(ind) == '#')
state = 1;
} else {
boolean isNumber = digits.indexOf(s.charAt(ind)) != -1;
if (state == 1) {
if (isNumber)
state = 2;
else if (s.charAt(ind) == '#')
state = 1;
else
state = 0;
} else if (state == 2) {
if (s.charAt(ind) == '#') {
state = 3;
} else if (isNumber) {
state = 2;
} else if (s.charAt(ind) == '#')
state = 1;
else
state = 0;
} else if (state == 3) {
if (s.charAt(ind) == '#')
state = 1;
else
state = 0;
}
}
} //end for loop
if (state == 3)
System.out.println("It matches");
else
System.out.println("It does not match");
}
P.S: Does not answer your question directly, but shows you how to implement a FSM very easily in Java.

Advice on Java program

My java project required that I create an array of objects(items), populate the array of items, and then create a main method that asks a user to enter the item code which spits back the corresponding item.
It took me a while to figure out, but I ended up "cheating" by using a public variable to avoid passing/referencing the object between classes.
Please help me properly pass the object back.
This is the class with most of my methods including insert and the find method.
public class Catalog {
private Item[] itemlist;
private int size;
private int nextInsert;
public Item queriedItem;
public Catalog (int max) {
itemlist = new Item[max];
size = 0;
}
public void insert (Item item) {
itemlist[nextInsert] = item;
++nextInsert;
++size;
}
public Item find (int key) {
queriedItem = null;
for (int posn = 0; posn < size; ++posn) {
if (itemlist[posn].getKey() == key) queriedItem = itemlist[posn];
}{
return queriedItem;
}
}
}
This is my main class:
import java.util.*;
public class Program {
public static void main (String[] args) {
Scanner kbd = new Scanner (System.in);
Catalog store;
int key = 1;
store = new Catalog (8);
store.insert(new Item(10, "food", 2.00));
store.insert(new Item(20, "drink", 1.00));
while (key != 0) {
System.out.printf("Item number (0 to quit) ?%n");
key = kbd.nextInt();
if (key == 0) {
System.out.printf("Exiting program now!");
System.exit(0);
}
store.find(key);
if (store.queriedItem != null) {
store.queriedItem.print();
}
else System.out.printf("No Item found for %d%n", key);
}
}
}
Thanks I appreciate the help!!!!!!
store.find(key); returns an Item you should use it and delete the public field from Catalog
public Item find (int key) {
Item queriedItem = null;
//....
}
Item searched = store.find(key);
if (searched != null)
searched.print();
else
System.out.printf("No Item found for %d%n", key);
Remove your use of queriedItem entirely and just return the item from find: Replace
store.find(key);
if (store.queriedItem != null){store.queriedItem.print();}else System.out.printf("No Item found for %d%n", key);
With
Item foundItem = store.find(key);
if (foundItem != null) {
foundItem.print();
} else System.out.printf("No Item found for %d%n", key);
Well, here are some suggesetions (choose complexity at your own discretion, but all of them is highly recommended):
Research Properties, for example here. Or XML. You could populate the array with values from a configuration file for greater flexibility.
Use constanst for literals in your code (where they are necessary).
Create an ApplicationFactory the initializes the whole application for you. Things like this need to be separated from your domain logic.
Create a UserInputProvider interface so you can easily change the way the input of the user is read without affecting anything else. Implement it with a ConsoleInputProvider class for example.
In general, try using interfaces for everything that's not a pure domain object (here, the only one you have is probably Item).
Try to keep your methods as short as possible. Instead of doing many things in a method, have it invoke other methods (grouping related logic) named appropriately to tell what it is doing.
If you're not allowed to cheat and use List or a Map, devise your own implementation of one, separating data structure and handling from the logic represented by Catalog (i.e. Catalog in turn will delegate to, for example, Map.get or equivalent method of your data structure implementation)
Your main should basically just have ApplicationFactory (or an IoC framework) to build and initialize your application, invoke the UserInputProvider (it should not know the exact implementation it is using) to get user input, validate and convert the data as required, invoke Catalog to find the appropriate Item and then (similarly to the input interface) send the result (the exact data it got, not some string or alike) to some implementation of a SearchResultView interface that decides how to display this result (in this case it will be a console-based implementation, that prints a string representing the Item it got).
Generally, the higher the level of decoupling you can achieve, the better your program will be.
The Single Responsibility Principle states: " every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class". This is also true for methods: they should have one and only one well defined task without any side effects.

Chaining order in Guava

I'm a bit new to Guava and it's style. I'm definitely digging it, but one thing I keep tripping over is the order of chained methods. Where I seem to have this problem the most is when using compound Orderings. I have to keep asking myself questions like:
Where does the natural go?
Where does the nullFirst (or last) go?
Which nullsFirst does what? (In the example below, one for host, one for last name, one for first name?)
Here's an example of one that I was just working on. It looks cumbersome, and I'm just not sure if I put it all together right. I have some JUnits to test it, and it seems okay, but there are always those quirky boundary cases.
Ordering<Host> lastNameThenFirstNameOrdering = Ordering.natural().nullsFirst().onResultOf(new Function<Host, String>() {
public String apply(Host host) {
return host.getLastName();
}}).compound(Ordering.natural().nullsFirst().onResultOf(new Function<Host, String>() {
public String apply(Host host) {
return host.getFirstName();
}})).nullsFirst();
As for an actual question: Is there a well-defined rule for how these things get executed? It seems to be last-to-first, but I'm having trouble telling that.
edit: Just wanted to point out the large, ugly code I was trying to replace:
Ordering<Host> ordering2 = new Ordering<Host>() {
public int compare(Host host1, Host host2) {
if (host1 == null || host2 == null) {
return host1 == host2 ? 0 : ((host1 == null) ? -1 : 1);
}
if(host1.getLastName() != null || host2.getLastName() != null){
if (host1.getLastName() == null) {
return -1;
} else if (host2.getLastName() == null) {
return 1;
}
if (host1.getLastName().compareTo(host2.getLastName()) != 0) {
return host1.getLastName().compareTo(host2.getLastName());
}
}
if (host1.getFirstName() == null) {
return -1;
} else if (host2.getFirstName() == null) {
return 1;
}
return host1.getFirstName().compareTo(host2.getFirstName());
}};
I think what you do is correct, but awfully ugly. Try this for readability:
Use an Enum
Move the functions to an enum that implements Function<Host, String>. Each of the enum items can provide it's own implementation.
enum HostFunctions implements Function<Host, String>{
GETFIRSTNAME{
#Override
public String apply(final Host host){
return host.getFirstName();
}
},
GETLASTNAME{
#Override
public String apply(final Host host){
return host.getLastName();
}
}
}
Indent your Code
Now reference those enum functions and indent your code properly. This is what it will look like:
final Ordering<Host> orderingByLastAndFirstName =
Ordering
.natural()
.nullsFirst()
.onResultOf(HostFunctions.GETLASTNAME)
.compound(
Ordering
.natural()
.nullsFirst()
.onResultOf(HostFunctions.GETFIRSTNAME))
.nullsFirst();
I'd say that makes everything much more understandable.
IDE Configuration
Regarding proper indentation (at least if you use Eclipse), see this question:
How to indent the fluent interface
pattern “correctly” with eclipse?
Enums as Functions
Regarding the enum: this is called the enum singleton pattern. The Guava guys use it all over their code base. Read about it on wikipedia or in Effective Java, Item 3. Although those sources both talk about single-item enums, the approach is almost the same here.
Each chaining call is "wrapping" the previous ordering into a new one, so you're right, the execution order can be thought of as "backwards".
I wrote and reviewed the Ordering class and I still regularly have to stop and scratch my head over the correct interleaving of nullsFirst(), and onResultOf() and reverse()!
The following would be my preference for doing this, assuming you must be able to handle null hosts, first names and last names. To me, it seems like a non-null first name and last name ought to be a requirement of the Host class. And you should generally try to avoid allowing collections to contain null objects.
Ordering<Host> lastNameFirstNameOrdering = new Ordering<Host>() {
#Override public int compare(Host left, Host right) {
return ComparisonChain.start()
.compare(left.getLastName(), right.getLastName(), Ordering.natural().nullsFirst())
.compare(left.getFirstName(), right.getFirstName(), Ordering.natural().nullsFirst())
.result();
}
}.nullsFirst();
Alternatively, I'd take an approach similar to Sean's but break things down for readability.
Ordering<Host> lastNameOrder = Ordering.natural().nullsFirst()
.onResultOf(Host.LAST_NAME);
Ordering<Host> firstNameOrder = Ordering.natural().nullsFirst()
.onResultOf(Host.FIRST_NAME);
Ordering<Host> orderingByLastAndFirstName =
lastNameOrder.compound(firstNameOrder).nullsFirst();
Keep in mind that you could also make these individual orderings static final fields of the class, allowing you to easily use them anywhere when sorting like Host.LAST_NAME_ORDER.

Categories

Resources