From wiki (definition + examples):
“An interface in the Java programming language is an abstract type that is used to declare a behavior that classes must implement”
interface Bounceable {
void setBounce();
}
public interface Predator {
boolean chasePrey(Prey p);
void eatPrey(Prey p);
}
Looks quite normal. Nothing unusual. But let’s look under the hood.
The interface is defined sometimes as “is a” relationship – “is a” Predator. I see in projects a lot of/all interfaces called as “is a”/noun eg. AddressService.
Let’s think a little is it a good approach?
The interface definition (at the beginning) clearly stated: “interface … is used to declare a behavior”! So, the interface should be “behave as”. I very like to call interface using verbs, gerunds, or adjectives as interface declare behavior and actions.
And why behavior is the most important, in business software?
Because behaviors – business rules increase/impact solution complexity, not the CRUD operations on things.
Let’s see the comparison between “is a” vs “behave as”
“is a” Car vs “behave as” Car
“is a” Car – we are focusing on the thing and its properties, but “behave as” Car we are focusing on behavior. These two perspectives make a huge difference in how we perceive it, and what will be reflected in code. This is a very interesting topic, that I will cover in another post.
“is a” Predator vs “behave as” Predator
I do not like the name of the second interface – Predator. Since we declare a behavior of predator, it should be called Predatorable, BeingPredator, etc., or even better PreyChasingAndEating. I know that names are a little strange and long, but better reflect the declaration of behavior.
Interfaces reflect behaviors that are modeled in code. That behavior coming from requirements. But, the requirements always change, so from that perspective, it is better to have many fine-grained, specific interfaces, to compose desired behaviors. This approach is compliant with the SRP and ISP principle. If we consider this the Predator example will look like this:
interface ChasePrey {
boolean chasePrey(Prey p);
}
interface EatPrey {
void eatPrey(Prey p);
}
//the class
class Predator implements ChasePrey, EatPrey
It doesn’t mean that you should have a lot of tiny interfaces. No, the interface should be cohesive and specialized.
Now, if we would like to declare the future requirements: ChildPredator, that can eat, but not chase is very easy:
class ChildPredator implements EatPrey
If we take the initial interface Predator the implementation will be not such clear
class ChildPredator implements Predator {
boolean chasePrey(Prey p) { throw UnsupportedOperationException() }
void eatPrey(Prey p) { ... }
}
Not good, as you see in the next post.