I designed the following for a problem:
class Animal {
// ...
}
class Guppy extends Animal { ... }
class Pigeon extends Animal { ... }
class TailedAnimal extends Animal {
// ...
}
class Dog extends TailedAnimal { ... }
class Cat extends TailedAnimal { ... }
class HornedAnimal extends Animal {
// ...
}
class Ram extends HornedAnimal { ... }
public static void main(String[] args) {
Animal a = getSomeAnimal();
a.doSomething();
if (a instanceof TailedAnimal) {
// do something
}
if (a instanceof HornedAnimal) {
// do something else
}
}
Animal, HornedAnimal and TailedAnimal are used mainly as data models.
Since Java does not support multiple inheritance, I have trouble creating Rhinoceros which is a Horned and Tailed animal. After asking around, someone recommended using composition and interfaces. I came up with the following:
class Animal {
// ...
}
class Guppy extends Animal { ... }
class Pigeon extends Animal { ... }
class Ram extends Animal implements IHorned { ... }
class Cat extends Animal implements ITailed { ... }
class Dog extends Animal implements ITailed {
BasicTail t = new BasicTail();
public Object getTail() {
return t.getTail();
}
public void setTail(Object in) {
t.setTail(in);
}
}
interface ITailed {
public Object getTail();
public void setTail(Object in);
//...
}
class BasicTail implements ITailed {
Object myTail;
public Object getTail() { return myTail; }
public void setTail(Object t) { myTail = t; }
}
interface IHorned {
// getters and setters
}
public static void main(String[] args) {
Animal a = getSomeAnimal();
a.doSomething();
// how do I check if a is horned or tailed?
}
My interface has getters and setters. Is there any way to avoid this? Assuming that there is currently no way to abstract the behaviour of Tails and Horns, and they're are being used mainly as data holders. How do I determine if my Animal is Horned or Tailed?
I'd suggest strategy pattern here. In short:
interface TailedAnimal {
void moveTail();
}
interface HornedAnimal {
void hitWithHorn();
}
class Rhinoceros() implements TailedAnimal, HornedAnimal {
private TailedAnimal tail; //Instantiate it somehow e.g. constructor, setter
private HornedAnimal horn; //Instantiate it somehow e.g. constructor, setter
public void moveTail() {
tail.moveTail();
}
public void hitWithHorn() {
horn.hitWithHorn();
}
}
By using this you encapsulate behavior in a concrete implementation of the interfaces, and may easily share exactly the same behavior for a few animals, as well as change it at run-time.
I think you must avoid setters in general. If you can, use immutable objects, and initialize its private data into its constructor.
To distinguish animals, I used another pattern, the visitor one. It's verbose, but you don't have to test directly what animal you're processing.
public class Animals {
private Animals() {
}
interface Animal {
void accept(final AnimalProcessor visitor);
}
interface AnimalProcessor {
void visitTailed(final TailedAnimal tailedAnimal);
void visitHorned(final HornedAnimal hornedAnimal);
}
interface TailedAnimal extends Animal {
void moveTail();
}
interface HornedAnimal extends Animal {
void hitWithHorns();
}
static class Dog implements TailedAnimal {
public void moveTail() {
//To change body of implemented methods use File | Settings | File Templates.
}
public void accept(final AnimalProcessor visitor) {
visitor.visitTailed(this);
}
}
static class Cat implements TailedAnimal {
public void moveTail() {
//To change body of implemented methods use File | Settings | File Templates.
}
public void accept(final AnimalProcessor visitor) {
visitor.visitTailed(this);
}
}
static class Ram implements HornedAnimal {
public void hitWithHorns() {
//To change body of implemented methods use File | Settings | File Templates.
}
public void accept(final AnimalProcessor visitor) {
visitor.visitHorned(this);
}
}
static class Rhinoceros implements HornedAnimal, TailedAnimal {
public void hitWithHorns() {
//To change body of implemented methods use File | Settings | File Templates.
}
public void moveTail() {
//To change body of implemented methods use File | Settings | File Templates.
}
public void accept(final AnimalProcessor visitor) {
visitor.visitTailed(this);
visitor.visitHorned(this);
}
}
public static void main(String[] args) {
Collection<Animal> animals = new ArrayList<Animal>(Arrays.asList(new Dog(), new Cat(), new Rhinoceros()));
for (final Animal animal : animals) {
animal.accept(new AnimalProcessor() {
public void visitTailed(final TailedAnimal tailedAnimal) {
// you do what you want when it's a tailed animal
}
public void visitHorned(final HornedAnimal hornedAnimal) {
// you do what you want when it's a horned animal
}
});
}
}
}
I've edited out my previous answer. I thought of something much better. See the revision for this post if you're curious.
Make use of the Specification pattern. It very much fits the bill here - way more than Decorator. You asked to "check" if an Animal was horned. Decorator pattern delivers transparency, while in this situation you appear to be asking for discrimination.
The Specification pattern encapsulates knowledge of how to evaluate some criteria. In our case, we'd want something like:
public interface Specification {
public boolean isSatisfiedBy(Animal aCriteria);
}
public class HornedAnimalSpecification implements Specification {
#Override
public boolean isSatisfiedBy(Animal aCriteria) {
//Right here is where the heart of your problem
//can be solved.
//
//Reserved conquering grounds.
}
}
Now you can define your Animal hierarchy however you want. The only thing you now need to do is figure out what makes an animal horned. Your answer to that question goes into the Specification class. Then your main function is easy as pie.
public class Zoo {
public static void main(String[] args) {
Animal ram = getHornedAnimal(); //Instantiate however you'd like.
Specification specification = new HornedAnimalSpecification();
if (specification.isSatisfiedBy(ram)) {
//Bingo, it's horned.
} else {
//Not horned!
}
}
}
Related
I have a collection of Animal objects.
My core code wants to treat all of these as Animals, all the same. Each Animal needs to be processed in some way. The nature of the processing depends on the sub-type of the Animal (bird, mammal, etc).
My code currently looks as follows.
public interface Animal {
public String getTaxonomyClass();
}
public abstract class Bird implements Animal {
#Override
public String getTaxonomyClass() {
return "aves";
}
// Specific to birds
public abstract float getWingspan();
}
public abstract class Mammal implements Animal {
#Override
public String getTaxonomyClass() {
return "mammalia";
}
// Specific to mammals
public abstract int getToothCount();
}
public interface AnimalProcessor {
public String getSupportedTaxonomyClass();
public void process(Animal a);
}
public class MammalProcessor implements AnimalProcessor {
#Override
public String getSupportedTaxonomyClass() {
return "mammalia";
}
#Override
public void process(Animal a) {
System.out.println("Tooth count is " + ((Mammal)a).getToothCount());
}
}
public class BirdProcessor implements AnimalProcessor {
#Override
public String getSupportedTaxonomyClass() {
return "aves";
}
#Override
public void process(Animal a) {
System.out.print("Wingspan is " + ((Bird)a).getWingspan());
}
}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ZooKeeper {
Map<String, AnimalProcessor> registry = new HashMap<String, AnimalProcessor>();
public void registerProcessor(AnimalProcessor ap)
{
registry.put(ap.getSupportedTaxonomyClass(), ap);
}
public void processNewAnimals(List<Animal> newcomers)
{
for(Animal critter : newcomers)
{
String taxonomy = critter.getTaxonomyClass();
if(registry.containsKey(taxonomy))
{
// if I can process the animal, I will
AnimalProcessor ap = registry.get(taxonomy);
ap.process(critter);
}
}
}
}
import java.util.LinkedList;
import java.util.List;
public class MainClass {
public static void main(String[] args) {
ZooKeeper keeper = new ZooKeeper();
keeper.registerProcessor(new MammalProcessor());
keeper.registerProcessor(new BirdProcessor());
List<Animal> animals = new LinkedList<Animal>();
animals.add(new Mammal() { // badger
#Override
public int getToothCount() {
return 40;
} }
);
animals.add(new Bird() { // condor
#Override
public float getWingspan() {
return 2.9f;
} }
);
keeper.processNewAnimals(animals);
}
}
Generally this is easy to understand and works nicely! I can add plug-in new processors and animal types at my leisure without changing the ZooKeeper class or any of the interfaces. You can imagine a more advanced main class, loading the Animals from a database, and processing them all in turn.
However, I worry about the downcasts inside the AnimalProcessor subclasses! This strikes me as something which should not be there, and may be a violation of OO principles. After all, at the moment I can pass a Bird to a MammalProcessor's process() method, and there will be a ClassCastException.
Can anyone suggest a design pattern to solve this? I looked at the Visitor pattern, but couldn't quite figure out how to apply it in this case! The key is to make the core code (ZooKeeper) treat all animals the same, and make it so that support for new Animals can be added trivially. Thanks!
I would suggest the following:
public interface Animal {
public AnimalProcessor<? extends Animal> getProcessor();
}
so each animal will return it's matching processor.
public interface AnimalProcessor<T extends Animal> {
public void process(T a);
}
so the processors will be typed with their matching type it's should process.
so the implantation will be like this:
public abstract class Bird implements Animal {
private BirdProcessor processor = new BirdProcessor();
public abstract float getWingspan();
#Override
public AnimalProcessor<Bird> getProcessor() {
return processor;
}
}
public class BirdProcessor implements AnimalProcessor<Bird> {
#Override
public void process(Bird b) {
System.out.print("Wingspan is " + b.getWingspan());
}
}
This is where generics work great.
First, you need to make AnimalProcessor generic:
public interface AnimalProcessor <T extends Animal> {
public String getSupportedTaxonomyClass();
public void process(T a);
}
Next, in your specific processors, you specify the generic type - eg for mammals:
public class MammalProcessor implements AnimalProcessor<Mammal> {
public String getSupportedTaxonomyClass() {
return "mammalia";
}
public void process(Mammal a) {
System.out.println("Tooth count is " + a.getToothCount());
}
}
Now, the process method only accepts Mammal objects, no birds here.
I suggest the following :
public interface Animal {
public String getTaxonomyClass();
public void process();
}
Now each animal class implementing Animal should implement its own processing logic.
For example :
public class Bird implements Animal {
public Bird(float wingSpan) {
this.wingSpan = wingSpan;
}
#Override
public String getTaxonomyClass() {
return "aves";
}
#Override
public void process() {
System.out.print("Wingspan is " + wingSpan);
}
// Specific to birds
private float wingspan;
}
Now you can have only one AnimalProcessor which processes as follows :
public void process(Animal a) {
a.process();
}
Make you AnimalProcessor generic;
public interface AnimalProcessor<T extends Animal> {
public String getSupportedTaxonomyClass();
public void process(T a);
}
public class MammalProcessor implements AnimalProcessor<Mammal> {
#Override
public String getSupportedTaxonomyClass() {
return "mammalia";
}
#Override
public void process(Mammal a) {
System.out.println("Tooth count is " + a.getToothCount());
}
}
So you've got a class like this...
public abstract class Bird implements Animal {
#Override
public String getTaxonomyClass() {
return "aves";
}
// Specific to birds
public abstract float getWingspan();
}
All Birds will have a wingspan, even if the wingspan is 0. So, why don't you change the class to something like this...
public class Bird implements Animal {
float wingspan = 0.0f;
public Bird(float wingspan){
this.wingspan = wingspan;
}
#Override
public String getTaxonomyClass() {
return "aves";
}
// Specific to birds
public float getWingspan(){
return wingspan;
}
}
So, to create a new Bird, instead of doing this...
animals.add(new Bird() { // condor
#Override
public float getWingspan() {
return 2.9f;
} }
);
You would just do this...
animals.add(new Bird(2.9f)); // condor
This would seem to make the whole thing a lot simpler and nicer for your purposes. You would do a similar change for your Mammal class too.
Now, for the processing of animals... if all Animals are going to be processed, you could just implement process() in Bird rather than needing a separate BirdProcessor class. To do this, in Animal, declare a method public void process();. Your Bird would implement it like this...
public void process() {
System.out.print("Wingspan is " + getWingspan());
}
and you would change your AnimalProcessor to simply do this (note: no longer an interface)...
public class AnimalProcessor {
public void process(Animal a) {
a.process();
}
}
Your AnimalProcessor class would then be able to handle all Animals.
Alternatively, if you want to leave AnimalProcessor as it is, it would probably be good to change the following though, to avoid the ClassCastException (this code here is for the BirdProcessor)...
public void process(Animal a) {
if (a instanceof Bird){
System.out.print("Wingspan is " + ((Bird)a).getWingspan());
}
}
Is this kinda what you were looking for?
Your problem are methods such as
public abstract int getToothCount();
...aren't defined in Animal. Instead, they are defined in specific subclasses of Animal. This means you can't treat Animals generically since they are fundamentally different.
To overcome this, the one approach would be to create abstract methods for all these in the Animal class.
Bird might respond to getToothCount() with "0".
Since all animals could respond to getWingspan(), getTootCount(), etc, you would not have to perform any type-specific checking. If this isn't good enough, create abstract implementations of "boolean hasWings()", "boolean hasTeeth()" etc etc in Animal.
Now you could say, for some animal a:
if (a.hasWings()) System.out.println("My wingspan is "+a.getWingSpan());
which would work for any animal. Of course, each subclass of Animal would have to implement all the various methods.
Another option is to add non-abstract methods to Animal. These methods would supply default answers. For example, getWingSpan() would return 0, getToothCount() would return 0, etc. Shark would override getToothCount(). Eagle would override getWingSpan()...
Then your subclasses would only have to override (or even know about) methods related directly to them.
So the compiler complains when ever i do a explicit cast. I can prevent this by using a #SuppressWarnings annotation.
At this point i would have this annotation a lot in my code which lets me suspect that there is another way i'm just not aware of.
Lets have a look at this example
class CutePet
{
public void pet()
{
System.out.println( "The cute pet gets some pets" );
}
}
class Cat extends CutePet
{
public void letOutside()
{
System.out.println( "The cat goes outside" );
}
public void letInside()
{
System.out.println( "The cat comes inside" );
}
public void removeTick()
{
System.out.println( "The cat looses all ticks" );
}
}
class Dog extends CutePet
{
public void goForAWalk()
{
System.out.println( "The Dog goes for a walk" );
}
public void tellHimWhatHeIs()
{
System.out.println( "The Dog is a good boy" );
}
}
class caretaker
{
public void takeCare( CutePet pet )
{
if( pet instanceof Cat )
{
pet.pet();
((Cat)pet).letOutside();
((Cat)pet).letInside();
((Cat)pet).removeTick();
}
else if( pet instanceof Dog )
{
pet.pet();
((Dog)pet).goForAWalk();
((Dog)pet).tellHimWhatHeIs();
}
}
}
The Caretaker does not know what kind of Pet he will get in advance and he my has several pets of different kinds.
I tried to give the Cute pet class a getType() method which returns a enum. With this enum i can remove the "instanceof" but the cast is still there.
Am i missing something?
If this were a real world problem, the caretaker would recognize which kind of pet he has based on the pet's appearance. While "instance of" is one way of looking at it, you might want to consider overloading the takeCare method directly with the subtypes as required. For example:
class Caretaker {
public void takeCare(Cat pet) {
pet.pet();
pet.letOutside();
pet.letInside();
pet.removeTick();
}
public void takeCare(Dog pet) {
pet.pet();
pet.goForAWalk();
pet.tellHimWhatHeIs();
}
}
in other words, the caretaker knows what to do (has methods already in place) for the kind of pet he receives.
EDIT
In response to some of the comments, yes, the original example shifts the problem further up. If you have an array or a list of generic pets then you still have to figure out what kinds of pets you have to give them to the caretaker. Conceptually it seems strange that the pet should be able to pet itself, take itself for a walk, etc. (these methods are part of the pet class when it should be the caretaker doing these actions ON the pet).
I've since rewritten the code with a full working example below with a Job class that has a perform method. This method will return the appropriate job based on the type of animal the caretaker has. The caretaker can then perform the job on the pet in question. See below.
Doing things this way avoids instanceof. While it is debatable how good/bad instanceof actually is, where possible it should be the object itself to tell me what it needs, otherwise the whole polymorphism concept can get pretty hairy pretty quick.
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Arrays.asList(
new Cat("Cat1"),
new Cat("Cat2"),
new Dog("Dog1")
).forEach(caretaker::takeCare);
}
interface CutePet {
String whoAmI();
Job whatINeed();
}
abstract static class NamedCutePet implements CutePet {
private final String name;
public NamedCutePet(String name) {
this.name = name;
}
public String whoAmI() {
return this.name;
}
}
static class Cat extends NamedCutePet {
public Cat(String name) {
super(name);
}
#Override
public Job whatINeed() {
return new CatJob(this);
}
}
static class Dog extends NamedCutePet {
public Dog(String name) {
super(name);
}
#Override
public Job whatINeed() {
return new DogJob(this);
}
}
static class Caretaker {
void takeCare(CutePet pet) {
pet.whatINeed().perform();
}
}
static abstract class BaseJob implements Job {
void pet(CutePet pet) {
System.out.println(String.format("The cute pet %s gets some pets", pet.whoAmI()));
}
}
static class DogJob extends BaseJob {
private final Dog dog;
public DogJob(Dog dog) {
this.dog = dog;
}
#Override
public void perform() {
pet(dog);
takeDogFarAWalk(dog);
tellHimWhatHeIs(dog);
}
private void takeDogFarAWalk(Dog dog) {
System.out.println(String.format("The dog %s goes for a walk", dog.whoAmI()));
}
private void tellHimWhatHeIs(Dog dog) {
System.out.println(String.format("The dog %s is a good boy", dog.whoAmI()));
}
}
static class CatJob extends BaseJob {
private final Cat cat;
public CatJob(Cat cat) {
this.cat = cat;
}
#Override
public void perform() {
pet(cat);
letOutside(cat);
letInside(cat);
removeTick(cat);
}
private void letOutside(Cat cat) {
System.out.println(String.format("The cat %s goes outside", cat.whoAmI()));
}
private void letInside(Cat cat) {
System.out.println(String.format("The cat %s comes inside", cat.whoAmI()));
}
private void removeTick(Cat cat) {
System.out.println(String.format("The cat %s loses all ticks", cat.whoAmI()));
}
}
interface Job {
void perform();
}
}
Let's make it clear: you can't call subclass specific methods without typecasting to subclass type.
Now, let me suggest an alternate way. Define a method takeCare() in the superclass and let the subclasses implement it by calling several specific methods specific to subclasses. Then from CareTaker#takeCare(), call only takeCare() method without typecasting.
Several other alternate approaches can be used to solve the situation.
Here is how you would do it with interfaces and reflection. Note that only the interface methods are called for each pet type. It could also be extended to call other methods.
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class PetProblem {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Dog dog = new Dog();
caretaker.takeCare(dog);
System.out.println("\nNow do it for the cat\n");
Cat cat = new Cat();
caretaker.takeCare(cat);
}
}
interface CuteCat {
void letOutside();
void letInside();
void removeTick();
}
interface CuteDog {
void goForAWalk();
void tellHimWhatHeIs();
}
interface CutePet {
default void pet() {
System.out.println("The cute pet gets some pets");
}
}
class Cat implements CutePet, CuteCat {
public void letOutside() {
System.out.println("The cat goes outside");
}
public void letInside() {
System.out.println("The cat comes inside");
}
public void removeTick() {
System.out.println("The cat looses all ticks");
}
}
class Dog implements CutePet, CuteDog {
public void goForAWalk() {
System.out.println("The Dog goes for a walk");
}
public void tellHimWhatHeIs() {
System.out.println("The Dog is a good boy");
}
}
class Caretaker {
public void takeCare(Object pet) {
Class<?>[] ifss = pet.getClass().getInterfaces();
for (Class<?> ifs : ifss) {
Method[] methods = ifs.getDeclaredMethods();
for (Method m : methods) {
try {
m.invoke(pet);
}
catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
Note however, that using interfaces and having a method so named that it can be used for all pets is easier. Here is an example. Since both dogs and cats need to eat, a common method feedMe() can be implemented for each.
public class AnimalShelter {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Dog dog = new Dog();
Cat cat = new Cat();
caretaker.feedThePets(dog);
caretaker.feedThePets(cat);
}
}
interface SupperTime {
void feedMe();
}
class Caretaker {
public void feedThePets(SupperTime pet) {
pet.feedMe();
}
}
class Dog implements SupperTime {
public void feedMe() {
System.out.println("Oh boy, Kibbles n' Bits");
}
}
class Cat implements SupperTime {
public void feedMe() {
System.out.println("Yum. Purina Cat Chow");
}
}
public abstract class CommonClass {
abstract void send(<what should i put here???>) {}
}
public class ClassA extends CommonClass {
void send(List<Comments> commentsList) {
// do stuff
}
}
public class ClassB extends CommonClass {
void send(List<Post> postList) {
// do stuff
}
}
I am new to OODP, I am trying to have a method that is able to take in any kind of List data so that I can abstract things out. How can i do this?
You could make it generic on some type T. Like,
public abstract class CommonClass<T> {
abstract void send(List<T> al);
}
And then, to implement it - use the generic. Like,
public class ClassA extends CommonClass<Comments> {
#Override
void send(List<Comments> commentsList) {
// do stuff
}
}
public class ClassB extends CommonClass<Post> {
#Override
void send(List<Post> postList) {
// do stuff
}
}
Also, as discussed in the comments, your class names could be improved to be more intuitive; something like,
public abstract class AbstractSender<T> {
abstract void send(List<T> al);
}
and then
public class CommentSender extends AbstractSender<Comment> {
#Override
void send(List<Comment> commentsList) {
// do stuff
}
}
public class PostSender extends AbstractSender<Post> {
#Override
void send(List<Post> postList) {
// do stuff
}
}
That has the advantage(s) of being more readable and easier to reason about (I can tell what a PostSender does by reading the name, ClassB not so much).
Finally, this looks like a case where an interface would work since your abstract class is purely virtual (and should be preferred since you can implement multiple interface, but can only extend from a single parent class);
public interface ISender<T> {
void send(List<T> al);
}
public class CommentSender implements ISender<Comment> {
#Override
void send(List<Comment> commentsList) {
// do stuff
}
}
public class PostSender implements ISender<Post> {
#Override
void send(List<Post> postList) {
// do stuff
}
}
In order to achieve this, you can take multiple approaches, I would suggest looking into Generics: https://docs.oracle.com/javase/tutorial/java/generics/index.html
With that said, there is one approach that is the most elegant and simple: you can supply a List<T> where T is a generic type.
public abstract class CommonClass<T> {
abstract void send(List<T>) {}
}
public class ClassA extends CommonClass<Comment> {
void send(List<Comments> commentsList) {
// do stuff
}
}
public class ClassB extends CommonClass<Post> {
void send(List<Post> postList) {
// do stuff
}
}
You can do that with the help of generics. https://www.tutorialspoint.com/java/java_generics.htm
Example
The abstract class
public abstract class CommonClass {
public abstract <T> void send(List<T> data);
}
Its child
public class Child extends CommonClass {
public <T> void send(List<T> data) {
// code here
}
}
Retrieving the list's contents
Retrieving the generified list's contents is similar to retrieving any list's contents. In the scope of the method, "T" is a type of object contained in the list.
for (T t : data) {
// to check if t is a string
if (t instanceof String) {
// code
}
}
You can also use lambdas to retrieve every element in the list.
Code base is littered with code like this:
BaseRecord record = // some BaseRecord
switch(record.source()) {
case FOO:
return process((FooRecord)record);
case BAR:
return process((BarRecord)record);
case QUUX:
return process((QuuxRecord)record);
.
. // ~25 more cases
.
}
and then
private SomeClass process(BarRecord record) { }
private SomeClass process(FooRecord record) { }
private SomeClass process(QuuxRecord record) { }
It makes me terribly sad. Then, every time a new class is derived from BaseRecord, we have to chase all over our code base updating these case statements and adding new process methods. This kind of logic is repeated everywhere, I think too many to add a method for each and override in the classes. How can I improve this?
First solution: good old polymorphism.
Simply add an abstract process() method to the BaseRecord class, and override it in every subclass. The code will thus become:
BaseRecord record = ...;
record.process();
If you can't add the process() method into the BaseRecord class (and its subclasses), then implement the visitor pattern. It will leave the process method outside of the BaseRecord class, but each time you add a new subclass, you'll be forced to modify the Visitor interface, and all its implementations. The compiler will thus check for you that you haven't forgotten a case somwhere in a switch.
public interface RecordVisitor<T> {
T visitFoo(FooRecord foo);
T visitBar(BarRecord foo);
...
}
public abstract class BaseRecord {
public abstract <T> T accept(RecordVisitor<T> visitor);
}
public class FooRecord extends BaseRecord {
#Override
public <T> T accept(RecordVisitor<T> visitor) {
return visitor.visitFoo(this);
}
}
public class BarRecord extends BaseRecord {
#Override
public <T> T accept(RecordVisitor<T> visitor) {
return visitor.visitBar(this);
}
}
Now you simply have to implement RecordVisitor for each block of logic described in the question:
RecordVisitor<Void> visitor = new ProcessRecordVisitor();
record.accept(visitor);
Both Visitor Pattern and Strategy pattern can be put in use here. http://en.wikipedia.org/wiki/Strategy_pattern and http://en.wikipedia.org/wiki/Visitor_pattern
I think this is instructive:
package classplay;
public class ClassPlay
{
public void say(String msg) { System.out.println(msg); }
public static void main(String[] args)
{
ClassPlay cp = new ClassPlay();
cp.go();
}
public void go()
{
A someClass = new C();
say("calling process with double dispatch");
someClass.dueProcess(this);
say("now calling process directly");
process(someClass);
}
public void process(A a)
{
say("processing A");
a.id();
}
public void process(B b)
{
say("processing B");
b.id();
}
public void process(C c)
{
say("processing C");
c.id();
}
abstract class A
{
abstract public void id(); // { System.out.println("Class A"); }
public void dueProcess(ClassPlay cp) { cp.process(this); }
}
class B extends A
{
public void id() { System.out.println("Class B"); }
public void dueProcess(ClassPlay cp) { cp.process(this); }
}
class C extends A
{
public void id() { System.out.println("class C"); }
public void dueProcess(ClassPlay cp) { cp.process(this); }
}
}
I have a collection of Animal objects.
My core code wants to treat all of these as Animals, all the same. Each Animal needs to be processed in some way. The nature of the processing depends on the sub-type of the Animal (bird, mammal, etc).
My code currently looks as follows.
public interface Animal {
public String getTaxonomyClass();
}
public abstract class Bird implements Animal {
#Override
public String getTaxonomyClass() {
return "aves";
}
// Specific to birds
public abstract float getWingspan();
}
public abstract class Mammal implements Animal {
#Override
public String getTaxonomyClass() {
return "mammalia";
}
// Specific to mammals
public abstract int getToothCount();
}
public interface AnimalProcessor {
public String getSupportedTaxonomyClass();
public void process(Animal a);
}
public class MammalProcessor implements AnimalProcessor {
#Override
public String getSupportedTaxonomyClass() {
return "mammalia";
}
#Override
public void process(Animal a) {
System.out.println("Tooth count is " + ((Mammal)a).getToothCount());
}
}
public class BirdProcessor implements AnimalProcessor {
#Override
public String getSupportedTaxonomyClass() {
return "aves";
}
#Override
public void process(Animal a) {
System.out.print("Wingspan is " + ((Bird)a).getWingspan());
}
}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ZooKeeper {
Map<String, AnimalProcessor> registry = new HashMap<String, AnimalProcessor>();
public void registerProcessor(AnimalProcessor ap)
{
registry.put(ap.getSupportedTaxonomyClass(), ap);
}
public void processNewAnimals(List<Animal> newcomers)
{
for(Animal critter : newcomers)
{
String taxonomy = critter.getTaxonomyClass();
if(registry.containsKey(taxonomy))
{
// if I can process the animal, I will
AnimalProcessor ap = registry.get(taxonomy);
ap.process(critter);
}
}
}
}
import java.util.LinkedList;
import java.util.List;
public class MainClass {
public static void main(String[] args) {
ZooKeeper keeper = new ZooKeeper();
keeper.registerProcessor(new MammalProcessor());
keeper.registerProcessor(new BirdProcessor());
List<Animal> animals = new LinkedList<Animal>();
animals.add(new Mammal() { // badger
#Override
public int getToothCount() {
return 40;
} }
);
animals.add(new Bird() { // condor
#Override
public float getWingspan() {
return 2.9f;
} }
);
keeper.processNewAnimals(animals);
}
}
Generally this is easy to understand and works nicely! I can add plug-in new processors and animal types at my leisure without changing the ZooKeeper class or any of the interfaces. You can imagine a more advanced main class, loading the Animals from a database, and processing them all in turn.
However, I worry about the downcasts inside the AnimalProcessor subclasses! This strikes me as something which should not be there, and may be a violation of OO principles. After all, at the moment I can pass a Bird to a MammalProcessor's process() method, and there will be a ClassCastException.
Can anyone suggest a design pattern to solve this? I looked at the Visitor pattern, but couldn't quite figure out how to apply it in this case! The key is to make the core code (ZooKeeper) treat all animals the same, and make it so that support for new Animals can be added trivially. Thanks!
I would suggest the following:
public interface Animal {
public AnimalProcessor<? extends Animal> getProcessor();
}
so each animal will return it's matching processor.
public interface AnimalProcessor<T extends Animal> {
public void process(T a);
}
so the processors will be typed with their matching type it's should process.
so the implantation will be like this:
public abstract class Bird implements Animal {
private BirdProcessor processor = new BirdProcessor();
public abstract float getWingspan();
#Override
public AnimalProcessor<Bird> getProcessor() {
return processor;
}
}
public class BirdProcessor implements AnimalProcessor<Bird> {
#Override
public void process(Bird b) {
System.out.print("Wingspan is " + b.getWingspan());
}
}
This is where generics work great.
First, you need to make AnimalProcessor generic:
public interface AnimalProcessor <T extends Animal> {
public String getSupportedTaxonomyClass();
public void process(T a);
}
Next, in your specific processors, you specify the generic type - eg for mammals:
public class MammalProcessor implements AnimalProcessor<Mammal> {
public String getSupportedTaxonomyClass() {
return "mammalia";
}
public void process(Mammal a) {
System.out.println("Tooth count is " + a.getToothCount());
}
}
Now, the process method only accepts Mammal objects, no birds here.
I suggest the following :
public interface Animal {
public String getTaxonomyClass();
public void process();
}
Now each animal class implementing Animal should implement its own processing logic.
For example :
public class Bird implements Animal {
public Bird(float wingSpan) {
this.wingSpan = wingSpan;
}
#Override
public String getTaxonomyClass() {
return "aves";
}
#Override
public void process() {
System.out.print("Wingspan is " + wingSpan);
}
// Specific to birds
private float wingspan;
}
Now you can have only one AnimalProcessor which processes as follows :
public void process(Animal a) {
a.process();
}
Make you AnimalProcessor generic;
public interface AnimalProcessor<T extends Animal> {
public String getSupportedTaxonomyClass();
public void process(T a);
}
public class MammalProcessor implements AnimalProcessor<Mammal> {
#Override
public String getSupportedTaxonomyClass() {
return "mammalia";
}
#Override
public void process(Mammal a) {
System.out.println("Tooth count is " + a.getToothCount());
}
}
So you've got a class like this...
public abstract class Bird implements Animal {
#Override
public String getTaxonomyClass() {
return "aves";
}
// Specific to birds
public abstract float getWingspan();
}
All Birds will have a wingspan, even if the wingspan is 0. So, why don't you change the class to something like this...
public class Bird implements Animal {
float wingspan = 0.0f;
public Bird(float wingspan){
this.wingspan = wingspan;
}
#Override
public String getTaxonomyClass() {
return "aves";
}
// Specific to birds
public float getWingspan(){
return wingspan;
}
}
So, to create a new Bird, instead of doing this...
animals.add(new Bird() { // condor
#Override
public float getWingspan() {
return 2.9f;
} }
);
You would just do this...
animals.add(new Bird(2.9f)); // condor
This would seem to make the whole thing a lot simpler and nicer for your purposes. You would do a similar change for your Mammal class too.
Now, for the processing of animals... if all Animals are going to be processed, you could just implement process() in Bird rather than needing a separate BirdProcessor class. To do this, in Animal, declare a method public void process();. Your Bird would implement it like this...
public void process() {
System.out.print("Wingspan is " + getWingspan());
}
and you would change your AnimalProcessor to simply do this (note: no longer an interface)...
public class AnimalProcessor {
public void process(Animal a) {
a.process();
}
}
Your AnimalProcessor class would then be able to handle all Animals.
Alternatively, if you want to leave AnimalProcessor as it is, it would probably be good to change the following though, to avoid the ClassCastException (this code here is for the BirdProcessor)...
public void process(Animal a) {
if (a instanceof Bird){
System.out.print("Wingspan is " + ((Bird)a).getWingspan());
}
}
Is this kinda what you were looking for?
Your problem are methods such as
public abstract int getToothCount();
...aren't defined in Animal. Instead, they are defined in specific subclasses of Animal. This means you can't treat Animals generically since they are fundamentally different.
To overcome this, the one approach would be to create abstract methods for all these in the Animal class.
Bird might respond to getToothCount() with "0".
Since all animals could respond to getWingspan(), getTootCount(), etc, you would not have to perform any type-specific checking. If this isn't good enough, create abstract implementations of "boolean hasWings()", "boolean hasTeeth()" etc etc in Animal.
Now you could say, for some animal a:
if (a.hasWings()) System.out.println("My wingspan is "+a.getWingSpan());
which would work for any animal. Of course, each subclass of Animal would have to implement all the various methods.
Another option is to add non-abstract methods to Animal. These methods would supply default answers. For example, getWingSpan() would return 0, getToothCount() would return 0, etc. Shark would override getToothCount(). Eagle would override getWingSpan()...
Then your subclasses would only have to override (or even know about) methods related directly to them.