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!
Stephen Florentino
Full StackOverflow Developer
My $0.02: