Animal Inheritance System with Advanced Features
Question
Design an object-oriented system in Java that implements an animal hierarchy with the following requirements:
1. Create an abstract class Animal with:
- Abstract method makeSound()
- Abstract method move()
- Concrete method getAge()
- Protected field for age
2. Create interfaces Pet and Wild with appropriate methods
3. Implement Dog, Cat, and Bird classes that extend Animal
4. Create Puppy and Kitten classes that extend Dog and Cat respectively
5. Use a Zoo class to manage animals using a Map<String, Animal>
6. Implement a Visitor pattern for animal interactions
7. Add support for animal training and tricks
8. Include proper exception handling and validation
Answer
Here's a implementation of the animal inheritance system:
Core Classes
-
Animal Abstract Class
public abstract class Animal { protected int age; protected String name; protected List<String> tricks; public Animal(String name, int age) { if (age < 0) { throw new IllegalArgumentException("Age cannot be negative"); } if (name == null || name.trim().isEmpty()) { throw new IllegalArgumentException("Name cannot be null or empty"); } this.name = name; this.age = age; this.tricks = new ArrayList<>(); } public abstract String makeSound(); public abstract void move(); public int getAge() { return age; } public String getName() { return name; } public void addTrick(String trick) { if (trick == null || trick.trim().isEmpty()) { throw new IllegalArgumentException("Trick cannot be null or empty"); } tricks.add(trick); } public List<String> getTricks() { return new ArrayList<>(tricks); } public void accept(AnimalVisitor visitor) { visitor.visit(this); } } -
Interfaces
public interface Pet { void play(); void feed(); void groom(); } public interface Wild { void hunt(); void defend(); void markTerritory(); } -
Animal Classes
public class Dog extends Animal implements Pet { private String breed; public Dog(String name, int age, String breed) { super(name, age); this.breed = breed; } @Override public String makeSound() { return "Woof!"; } @Override public void move() { System.out.println(name + " runs on four legs"); } @Override public void play() { System.out.println(name + " plays fetch"); } @Override public void feed() { System.out.println(name + " eats dog food"); } @Override public void groom() { System.out.println(name + " gets brushed"); } public void fetch() { System.out.println(name + " fetches the ball"); } } public class Cat extends Animal implements Pet { private boolean isIndoor; public Cat(String name, int age, boolean isIndoor) { super(name, age); this.isIndoor = isIndoor; } @Override public String makeSound() { return "Meow!"; } @Override public void move() { System.out.println(name + " walks gracefully"); } @Override public void play() { System.out.println(name + " plays with yarn"); } @Override public void feed() { System.out.println(name + " eats cat food"); } @Override public void groom() { System.out.println(name + " grooms itself"); } public void climb() { System.out.println(name + " climbs the tree"); } } public class Bird extends Animal implements Wild { private String species; private boolean canFly; public Bird(String name, int age, String species, boolean canFly) { super(name, age); this.species = species; this.canFly = canFly; } @Override public String makeSound() { return "Tweet!"; } @Override public void move() { if (canFly) { System.out.println(name + " flies through the air"); } else { System.out.println(name + " hops on the ground"); } } @Override public void hunt() { System.out.println(name + " hunts for insects"); } @Override public void defend() { System.out.println(name + " defends its nest"); } @Override public void markTerritory() { System.out.println(name + " sings to mark territory"); } public void buildNest() { System.out.println(name + " builds a nest"); } } -
Subclasses
public class Puppy extends Dog { private boolean isHouseTrained; public Puppy(String name, int age, String breed, boolean isHouseTrained) { super(name, age, breed); this.isHouseTrained = isHouseTrained; } @Override public String makeSound() { return "Yip!"; } @Override public void move() { System.out.println(name + " runs and stumbles"); } public void learnTrick(String trick) { addTrick(trick); System.out.println(name + " learned to " + trick); } } public class Kitten extends Cat { private boolean isLitterTrained; public Kitten(String name, int age, boolean isIndoor, boolean isLitterTrained) { super(name, age, isIndoor); this.isLitterTrained = isLitterTrained; } @Override public String makeSound() { return "Mew!"; } @Override public void move() { System.out.println(name + " runs and pounces"); } public void learnTrick(String trick) { addTrick(trick); System.out.println(name + " learned to " + trick); } }
Zoo Management
- Zoo Class
public class Zoo { private final Map<String, Animal> animals; public Zoo() { this.animals = new HashMap<>(); } public void addAnimal(Animal animal) { if (animal == null) { throw new IllegalArgumentException("Animal cannot be null"); } if (animals.containsKey(animal.getName())) { throw new IllegalStateException("Animal with name " + animal.getName() + " already exists"); } animals.put(animal.getName(), animal); } public Animal getAnimal(String name) { return animals.get(name); } public void removeAnimal(String name) { animals.remove(name); } public void makeAllAnimalsSound() { animals.values().forEach(animal -> System.out.println(animal.getName() + ": " + animal.makeSound())); } public void trainAnimal(String name, String trick) { Animal animal = getAnimal(name); if (animal == null) { throw new IllegalArgumentException("Animal not found: " + name); } animal.addTrick(trick); } public List<Animal> getAnimalsByType(Class<? extends Animal> type) { return animals.values().stream() .filter(type::isInstance) .collect(Collectors.toList()); } }
Visitor Pattern
- Animal Visitor
public interface AnimalVisitor { void visit(Dog dog); void visit(Cat cat); void visit(Bird bird); void visit(Puppy puppy); void visit(Kitten kitten); } public class FeedingVisitor implements AnimalVisitor { @Override public void visit(Dog dog) { dog.feed(); } @Override public void visit(Cat cat) { cat.feed(); } @Override public void visit(Bird bird) { System.out.println(bird.getName() + " eats seeds"); } @Override public void visit(Puppy puppy) { puppy.feed(); } @Override public void visit(Kitten kitten) { kitten.feed(); } } public class TrainingVisitor implements AnimalVisitor { @Override public void visit(Dog dog) { dog.fetch(); } @Override public void visit(Cat cat) { cat.climb(); } @Override public void visit(Bird bird) { bird.buildNest(); } @Override public void visit(Puppy puppy) { puppy.learnTrick("sit"); } @Override public void visit(Kitten kitten) { kitten.learnTrick("come"); } }
Testing
- Zoo Tests
@Test public void testZooOperations() { Zoo zoo = new Zoo(); // Add animals Dog dog = new Dog("Rex", 3, "German Shepherd"); Cat cat = new Cat("Whiskers", 2, true); Bird bird = new Bird("Tweety", 1, "Canary", true); Puppy puppy = new Puppy("Max", 1, "Labrador", false); Kitten kitten = new Kitten("Luna", 1, true, true); zoo.addAnimal(dog); zoo.addAnimal(cat); zoo.addAnimal(bird); zoo.addAnimal(puppy); zoo.addAnimal(kitten); // Test animal sounds assertEquals("Woof!", zoo.getAnimal("Rex").makeSound()); assertEquals("Meow!", zoo.getAnimal("Whiskers").makeSound()); assertEquals("Tweet!", zoo.getAnimal("Tweety").makeSound()); // Test training zoo.trainAnimal("Max", "sit"); assertTrue(zoo.getAnimal("Max").getTricks().contains("sit")); // Test visitor pattern AnimalVisitor feedingVisitor = new FeedingVisitor(); zoo.getAnimal("Rex").accept(feedingVisitor); }
Usage Example
```java public class Main { public static void main(String[] args) { Zoo zoo = new Zoo();
// Create animals
Dog dog = new Dog("Rex", 3, "German Shepherd");
Cat cat = new Cat("Whiskers", 2, true);
Bird bird = new Bird("Tweety", 1, "Canary", true);
Puppy puppy = new Puppy("Max", 1, "Labrador", false);
Kitten kitten = new Kitten("Luna", 1, true, true);
// Add to zoo
zoo.addAnimal(dog);
zoo.addAnimal(cat);
zoo.addAnimal(bird);
zoo.addAnimal(puppy);
zoo.addAnimal(kitten);
// Make all animals sound
zoo.makeAllAnimalsSound();
// Train animals
zoo.trainAnimal("Max", "sit");
zoo.trainAnimal("Luna", "come");
// Use visitor pattern
AnimalVisitor feedingVisitor = new FeedingVisitor();
AnimalVisitor trainingVisitor = new TrainingVisitor();
for (Animal animal : zoo.getAnimalsByType(Animal.class)) {
animal.accept(feedingVisitor);
animal.accept(trainingVisitor);
}
}
}