Alright, so I'm trying to figure out the best structure for this. I would like to know what would be considered the best practice for these situations and the overall most effective.
Let's say I'm creating a little world. This world consists of different types of birds, So I create a Bird class to act as a parent for all birds.
public abstract class Bird {
//stuff all birds have in common
}
Now I want to create different types of birds. Let's create a Penguin and a Goose class and have them extend Bird.
public class Penguin extends Bird{
//penguin stuff
}
public class Goose extends Bird{
//goose stuff
}
So far so good, but now for the wrench. Let's give birds the ability to fly. Surprise, not all birds can fly - such as the penguins! So let's approach our options..
Making a fly method inside of the Bird class is out of the option (not all birds fly). Another smelly way is having multiple parent classes, such as BirdsThatFly, because this would become an enormous mess, especially when other birdly attributes are added into the mix.
One viable option that in theory sounds like the right idea would be to use interfaces. For example, I could have a Fly interface.
public interface Fly {
public void fly();
}
This would allow me to implement Fly on only Bird child classes that can fly. Example..
public class Goose extends Bird implements Fly {
@Override
public void fly() {
//fly!
}
}
This appears clean and forces a fly method onto the goose, and seems it could work really well when adding other attributes.
The thing that seems wrong, is that I would still have to create a custom fly for each type of bird, and there could be loads of birds! Would default voids help me here? I feel they could be fairly limiting as these attributes grow.
Perhaps I can be told otherwise and how to rethink this interface structure.
This involves creating attribute objects and adding them to the individual bird classes. If I'm correct with interfaces, this option may be a valid approach.
The attribute class would look something like this
public abstract class Attribute {
public abstract void execute();
}
And an attribute could look like this
public class Fly extends Attribute{
@Override
public void execute() {
//fly!
}
}
This fly attribute can now be added to a bird by having an attribute list in Bird, and populating it in the bird type classes.
public abstract class Bird {
private List<Attribute> attributes = new ArrayList<Attribute>();
protected void addAttribute(Attribute attribute){
attributes.add(attribute);
}
public List<Attribute> getAttributes(){
return attributes;
}
}
And adding it to a Goose...
public class Goose extends Bird{
public Goose(){
super.addAttribute(new Fly());
}
}
This approach to me seems very customizable, but also seems out of practice and maybe not immediately be clear what each class is able to do. It also requires a lot of work to properly set up on larger scale to be able to execute specific attributes.
Is there a way that would be considered better practice, or are either of these a reasonable approach or a good start?
If all flying birds share the same method of flying, you could use multiple levels of inheritance.
Create a main Bird object that handles the actions ALL birds can do. Then create two subclasses that extend Bird, say FlyingBird and GroundedBird. This is a more appropriate way to separate the abilities of each type of Bird.
abstract class Bird {
void eat() {
// Because all birds get hungry
}
}
abstract class FlyingBird extends Bird {
void fly() {
// Do the flight!
}
}
abstract class GroundedBird extends Bird {
void waddle() {
// They gotta get around somehow.
}
}
class Penguin extends GroundedBird;
class Goose extends FlyingBird;
EDIT
There are a couple of other options for handling more attributes. The OP also asks (in comments below) what to do if the bird can fly and swim?
At some point in the inheritance chain, you will need to implement the behavior either way. If you are looking to define multiple attributes of a Bird, this would be a case to use Interfaces instead:
class Goose extends Bird implements Flyable {
@Override
public void fly() {
// Need to override the fly() method for all flying birds.
}
}
Now, the suppose you want all flying birds to have the same action for flying. While not necessarily idea, you could create a static class called BirdAction to hold all the "verbs" a bird might be capable of.
You would still need to override the fly() method for each Bird but have them all call the same static method within BirdAction:
class BirdAction {
static void fly(Bird bird) {
// Check if this Bird implements the Flyable interface
if (bird instanceof Flyable) {
// All birds fly like this
} else {
// This bird tried to fly and failed
}
}
}
I would not say that's an ideal solution, but depending on your real-world application, it may just work. Granted, a Penguin would not have the fly() method by default, but the check is there in case you still call BirdAction.fly() from the Penguin class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With