Matching pattern as behavior

I am looking for a sample template for my simple problem. Here is a simplified version.

class Animal{...} class Dog extends Animal{...} class Cat extends Animal{...} ... // so on, 3 other classes as of now 

I have a static method (actually exposed through a web service, but its synonym) that takes id and returns an animal.

If cat returns, then another command using the cat object generates a CatReport. If the dog, then the dog is a message (they can use it for anything). Obviously, cats and dogs have different attributes. cat and Dog have nothing in common except that they are animals. Therefore, making a call as shown below is not enough, because I need the exact type:

 public static Animal getAnimal(int id){} 

Not enough, because animal does not contain all the information that the exact type can give.

What is the best way to solve this problem?


PS: In Scala I would simply perform pattern matching on an object. This solves the problem elegantly.

One of my solutions: make a call that returns enum , which matches the id value. And then for each of them there will be a separate call:

 public static AnimalType getAnimalType(int id){} public static Cat getCat(int id){} public static Dog getDog(int id){} .... 

But this is cumbersome.

+4
source share
3 answers

In a language like Java, you can model the behavior of pattern matching using the Visitor pattern.

You can do this in a few steps:

  • Define the Animal interface representing Animal using the accept method
  • Add some subclasses to Animal and give the same implementation as in my small example below.
  • Define the Visitor interface and give it an implementation. This class allows you to model pattern matching on your classes.

Here is a small example:

 public interface Animal { public void accept(AnimalVisitor v); } public class Dog extends Animal { public void accept(AnimalVisitor v) { v.visit(this); } } public class Cat extends Animal { public void accdept(AnimalVistior v) { v.visit(this); } } public interface AnimalVisitor { public void visit(Dog d); public void visit(Cat c); } public class PrintAnimal implements AnimalVisitor { public void visit(Dog d) { System.out.println("Dog"); } public void visit(Cat c) { System.out.println("Cat"); } } 

Visitor pattern is an elegant way to solve your problem and also avoid accumulating if (x instance of bar) in one function. With this template, your code will be more readable and easier to extend.

Relevant Scala code to get an idea of ​​my answer:

 abstract class Animal {} case class Dog() extends Animal case class Cat() extends Animal object Animal { def printAnimal(a : Animal) = a match { case x : Dog => "Dog" case x : Cat => "Cat" case _ => "Unknown" } def main(args : Array[String]) = { println(printAnimal(Dog())) } } 
+2
source

Well, I don’t see a really elegant solution, but you can create a kind of factory report with this code

 public Report getCorrespondingReport(Animal animal){ if(animal instanceof Dog) return new DogReport(); if(animal instanceof Cat) return new CatReport(); ... 

... or you can make a general report and use reflection to check your instance of Animal and generate your report according to general rules, but this can be impossible.

0
source

If I understand the question correctly, you want to call the correct implementation of the methods regarding the type of object you have. Therefore, if the animal is a cat, the report method should be called from the Cat class if you have code, as shown below

 public static Animal getAnimal(int id){ //your code to return object of either Cat or Dog } animal.generateReport(); 

First of all, as you said,

Obviously, cats and dogs have different attributes. A cat and a dog do not have anything other than the fact that they are animals.

Since subclasses do not have common functions, define Animal as an interface instead of a class, as follows

 interface Animal{ public void generateReport(); } 

And create a Cat & Dog like this

 class Cat implements Animal{ //define cat specific attributes here public void generateReport(){ //your logic to generate cat report } } class Dog implements Animal{ //define dog specific attributes here public void generateReport(){ //your logic to generate dog report } } 

Since the generateReport () method is defined in the interface, all classes implementing the interface must have generateReport ().

So, when you make such a call,

 public static Animal getAnimal(int id){ //your code to return object of either Cat or Dog } animal.generateReport(); 

The base object method will be called.

If you just want to know what the animal object refers to (returned from the getAnimal method, that is, Cat or Dog), you can check it as shown below

 class Animal{ } class Dog extends Animal{ public String dogName = "Dog1"; } class Cat extends Animal{ public String catName = "Cat1"; } public class HelloWorld{ public static void main(String []args){ //call getAnimal and get the object instead of following line Animal animal = new Cat(); if ( animal instanceof Cat ){ //cast Animal to Cat Cat cat = (Cat) animal; System.out.println(cat.catName); }else if ( animal instanceof Dog ){ //cast Animal to Dog Dog dog = (Dog) animal; System.out.println(dog.dogName); } } } 
-one
source

All Articles