- JDK
- Maven
Run with maven command:
mvn test
Singing is a behaviour of the bird so it should be a method in Bird class (not Animal because not all Animal can sing). We have 2 possible ways to implement sing method: instance method in Bird class or separate Interface. For now, we just have only Animal and Bird, to keep everything simple, we go with instance method in Bird class.
We create Bird instance of Bird class, then we test its behaviour: walk, fly and sing.
We follow some simple rules:
- Named things clearly and consistently. The more clearly your names convey what a thing is and what it does, the easier the code is to read.
- Write unit tests. Unit tests not only test the code, but they also document what it’s expected to do.
- Leverage external tools: turn on compiler warnings, use on-the-fly/before-commit code analysis feature in IDE.
2. Now, we have 2 special kinds of birds: the Duck and the Chicken... Can you implement them to make their own special sound?
Now we have 2 more class: Duck and Chicken. Because they are still Bird so they should be extended from Bird class. And it's time to move sing behaviour to separated Interface and create a new Interface for swim behavior. Duck and Chicken will provide their own "sing" behaviour.
The rooster is a male chicken, it's still a chicken so we create a new Rooster class and it should be extended from Chicken class.
Rooster class will implement its own sing behaviour.
Since rooster is a chicken, their relationship should be modeled using class inheritance.
No, it's better to go with class inheritance.
4. Can you model a parrot? We are specifically interested in three parrots, one that lived in a house with dogs one in a house with cats, the other lived on a farm next to the rooster.
Since a parrot is a tropical bird, their relationship should be modeled using class inheritance. We create Parrot class which is extended from Bird class.
d. How do you keep the parrot maintainable? What if we need another parrot lives near a Duck? Or near a phone that rings frequently?
Since parrot has a special ability: parrot could "copy" the sing behaviour of "singable" objects, we use Decorator pattern which allows adding new behaviors to objects dynamically by placing them inside special wrapper objects. And a parrot is still a bird, in the case that parrot does not live near any singable animals, parrot still could sing from bird instinct.
Fish does not relate to Bird, but Fish is a kind of Animal so Fish should be extended from Animal, and Fish should implement its behaviour interfaces.
Shark and Clownfish are 2 different kinds of Fish so they are 2 new classes which are extended from Fish class. They share the same properties: Size and Color, so we create 2 new instance variables for these properties in Fish class then we create constructors in Shark and Clownfish to set correct defaults value for them. Making jokes and eating other fish are 2 behaviour and they should be modeled as Interfaces.
Yes, in reality, dolphin is a sea animal, not fish. So Dolphin class should be extended from Animal class and implement behaviour Interface to gain swim ability.
We always follow DRY (Don't Repeat Yourself) and KISS (Keep It Simple, Stupid).
Butterfly is an animal so we create new Butterfly class which is extended from Animal. Butterfly class should implement behaviour Interfaces.
Since butterfly object can change its behaviour when its internal state change (from caterpillar to butterfly), we use State pattern to implement Butterfly model.
The code to count animals by behaviours was put in Animal class and we could see it in action by run AnimalTest class.
