When developing in Java, I often find that my code doesn't match existing code. I think I may be doing object oriented stuff wrong?
class SaladMaker {
private List<Fruit> fruitSalad;
/**
* Given a recipe, create a fruit salad in {@link fruitSalad}.
*
* @param recipe Describe what does in this fruit salad.
*/
public void makeFruitSalad(Recipe recipe) {
// Stuff to determines which fruits go in the salad.
fruitSalad = new ArrayList<>();
for (Fruit fruit : recipe) {
if (!addFruitToSalad(fruit)) {
System.out.println("skipped fruit: " + fruit);
}
}
// There's more stuff here, dressings and such.
}
/**
* Prepare a fruit and add it to the salad.
*
* @return Return code indicates success/failure.
*/
private boolean addFruitToSalad(Fruit fruit) {
fruit.wash();
fruit.cut();
// Let's pretend there's enough stuff here to make a method.
// And something can go wrong that returns false.
fruitSalad.add(fruit);
return true;
}
public List<Fruit> getSalad() {
return fruitSalad;
}
}
// Used like this:
SaladMaker saladMaker = new SaladMaker();
saladMaker.makeFruitSalad(new Recipe(/* ... */));
saladMaker.getSalad();
Obviously it is not a real example. Some things to note:
In the real example, there may be more methods that are called in a specific order to manipulate those fields.
What I feel tempted to write is more like this:
class SaladMaker {
/**
* Given a recipe, create a fruit salad.
*
* @param recipe Describe what does in this fruit salad.
* @return A list of the salad components (mix well!).
*/
public List<Fruit> makeFruitSalad(Recipe recipe) {
// Stuff to determines which fruits go in the salad.
List<Fruit> fruitSalad = new ArrayList<>();
for (Fruit fruit : recipe) {
Optional<Fruit> prepared = prepareFruitForSalad(fruit);
if (prepared.isPresent()) {
fruitSalad.add(prepared.get());
} else {
System.out.println("skipped fruit: " + fruit);
}
}
// There's more stuff here, dressings and such.
return fruitSalad;
}
/**
* Prepare a fruit.
*
* @return The fruit if successful, empty if not.
*/
private Optional<Fruit> prepareFruitForSalad(Fruit fruit) {
// Perhaps clone the fruit.
fruit.wash();
fruit.cut();
// Let's pretend there's enough stuff here to make a method.
// And something can go wrong that returns Optional.empty().
return Optional.of(fruit);
}
}
// Used like this:
SaladMaker saladMaker = new SaladMaker();
List<Fruit> salad = saladMaker.makeFruitSalad(new Recipe(/* ... */));
Which is somewhat different:
Note that with more data to be communicated, a lot of boilerplate appears, since there is no good multiple-return in Java (Tuple/Triple, but meh).
I like my code for some reasons (obviously, or I wouldn't write it):
(It's actually not because I'm new; I used to write first style, but I'm liking return values more and more).
I feel like we have fewer bugs this way. But there are things wrong with it:
What is your opinion? Am I doing object oriented programming wrong by trying to be functional? Am I doing programming wrong all together? Is there a third way?
Please share your wisdom!
Thanks, good advice.
In this case, "make OO work for you" would imply "work around OO" or "do not use OO for this". Which may well be the correct answer.
I intentionally did not do that to make it similar in spirit to the original code, but that would indeed be better.
Stephen Florentino
Full StackOverflow Developer
My $0.02: