0

I'm looking for the correct design pattern to decouple some of the logic on some abstract classes I've got. First we have a base class Fruit and child classes apple, orange, and mango. Then I have some classes that perform some logic on the fruit. These classes are the chopping, cooking, and peeling classes. Now much of the logic for chopping is identical for each fruit, as well as cooking and peeling, however there is some specific differences that occur for each type of fruit in all the working classes. I don't particularly want to give the fruit classes a lot of information about what it takes to chop, cook, or peel since that logic is quite complicated, yet I'm not fond of the chop/cook/peel class having so much code related to specific fruits.

So to sum it all up which design pattern correctly models said pattern of fruits and actions on fruits. Thank you for all input.

Medran
  • 43

2 Answers2

4

This is actually a general conundrum known as the expression problem.

The gist is this: you have some types, and you have some functions that act on those types, and you need to specify the action of each function on each type. Conceptually, you may think of a two-dimensional table where the rows are labeled by types and the columns by functions. The cell (i, j) specifies the behavior of function j on type i.

Thus, if we just view a row of this table, we get all the functions acting on a specific type, which is more or less an object. If we instead take a column-oriented view, we get a single function acting on all the different types, which is function by case analysis. Neither of the two views is unilaterally superior, so I will cover them both.

Object-oriented (row-based) approach

Create an abstract base class Fruit with methods slice, cut, peel, etc. Create classes Pear, Apple, etc that inherit from Fruit. The shared logic goes in the base class, the specific logic in the subclasses.

Note that most programmers (including myself) consider inheritance bad practice. It is better to make Fruit an interface that the other classes implement. For code reuse, use composition (that is, create a function which does what you want and use it in each class).

Functional (column-oriented) approach

Create a sum type Fruit with injections Apple, Pear, etc. Create functions slice, cut, that case on the instance type of their argument, e.g.

fun slice (fruit: Fruit) = case fruit of Pear -> ... | Apple -> ...

There are of course other approaches (e.g. using modules and signatures), but these are two of the common ones.

gardenhead
  • 4,757
1

You're asking for a design pattern to implement double dispatch. The logic for performing an action on a fruit depends on both the type of action, and the type of fruit.

I can't give a more specific answer because fruits and cooking actions are not real OOP objects. The appropriate design pattern to use will depend on what your objects really are.

Sophie Swett
  • 1,349