1

I have a collection of animals (interface) which contains birds and cats (both also interfaces). I want to print out all the cats to the console and I am forbidden to use instanceof (I did not make up this arbitrary restriction).

What is the correct way in OOP to solve this? getClass() is not possible because I just have access to the API and it would not comply with OOP concepts. Another alternative would be the visitor pattern (I can change the API), but this would be just be a very convoluted way to effectively perform a type-check.

  • 1
    Add a method isCat(). Only instances of Cat will return true – BobDalgleish Feb 14 at 19:33
  • I also thought of this possibility. I avoid the instanceof by doing this, but the code does effectively the same thing and instanceof is banned for a design reason. – dav20011 Feb 14 at 19:41
  • Well, instanceof is general-purpose and overkill for most situations. If you only need to determine whether it is a Cat then isCat() is adequate. Better is a method getAnimalType() which returns a string name; it doesn't wander into instanceof territory, but ... meh. – BobDalgleish Feb 14 at 19:50
  • 1
    This doesn't happen to be a homework assignment, does it? Because being forbidden from using the instanceof operator when that language supports it is not a real thing. – Greg Burghardt Feb 14 at 20:28
  • @BobDalgleish Using instanceof or getAnimalType is effectively the same thing. When forbidding one (for homework matters) the other is automatically forbidden as well. The point is usually to force the student into using polymorphism. Usually ;) – Andres F. Feb 14 at 21:25
3

This doesn't occur to a lot of people for some reason, but my preferred way in real code is to never mix them up in the first place. Have a List<Bird> and a separate List<Cat>, and either create your List<Animal> from the first two on demand when needed, or add to it at the same time you construct the bird and cat lists.

The other ways I'm aware of either violate the Interface Segregation Principle by requiring classes to use or implement methods that are of no interest to them (like an empty print or an isCat or a visitor pattern), or are functionally equivalent to instanceof. In my opinion, if you can't avoid the intermingling, you may as well use instanceof to split it out into a separate list. Whoever gave you that restriction should advise you on what principle is worse to violate in your particular case.

|improve this answer|||||
  • 1
    "Print nothing(empty method)" is legit implementation of the Print method. For example OP could say "Print cats to the console, but Birds to the text file". – Fabio Feb 14 at 20:25
  • 1
    I think this question has nothing to do with segregation principle, because all animals should be printed out, but for birds we have different behaviour - in this case this behaviour is to print nothing. – Fabio Feb 14 at 20:29
  • 1
    Where does it say birds would be printed differently? Being forced to implement a print method if you're not interested in printing anything is a textbook ISP violation. It might be better than other solutions given certain trade offs. In fact, I would probably use a similar solution if forced to use a commingled list, but it's still an ISP violation. – Karl Bielefeldt Feb 14 at 20:31
  • Would you say that "Null object pattern" where you implement "empty" object, so instead of null you can return valid object but with "empty" behavior, would be a violation of segregation principle? – Fabio Feb 14 at 20:36
  • Being forced to implement a print method if you're not interested in printing anything is a textbook ISP violation - that would be an SRP violation, not an ISP violation. ISP is about providing each client with the minimal interface that they require, and in this case, the client is very much interested in printing, however Animal might not be the right place to implement that. – casablanca Feb 15 at 6:30
5

Add method Print to the interface
Cats will implement this method by printing out some value
Birds will implement it as an "empty" method, which do nothing or print empty string.

public interface IAnimal
{
    public string Name { get; }
    public void Print();
}    

public class Cat : IAnimal
{
    public Cat(string name) => Name = name;

    public Print() => Console.WriteLine(Name);
}

public class Bird : IAnimal
{
    public Cat(string name) => Name = name;

    public Print()
    {
        // Do nothing or print empty string
    }
}

Usage would look like below

var animals = new IAnimal[] { new Cat("First"), new Bird("Second"), new Cat("Third") };

foreach (var animal in animals)
{
    animal.Print();
}

 // Output:
 // First
 // Third

With such approach consumers of Animals(Cats and Birds) don't need to know of their actual type.

"Knowing" specific type somewhere where interface is used - will bound consumer code to the implementation details, which we trying to avoid when following Dependency Inversion Principle.

|improve this answer|||||
  • +1 This lesson is likely the point of the homework assignment given to the OP. – Andres F. Feb 14 at 21:26
  • @Andres F. Actually no, the task is much larger (currently at ~3400 lines of code). It is just a problem I stumbled on while coding and because I am not used to such strict OOP enforcement (only programmed Java for myself the last 8 years), I thought it would be better to get some advice before doing something wrong. – dav20011 Feb 14 at 23:29
  • @dav20011: "OOP enforcement" - by whom? OOP is not about enforcing things; sometimes, a type cast or a type check is fine. It's just that the need to do an instanceof suggests that some key aspects of your design aren't accommodating the needs of the code, in the sense that they are making it harder to change in the ways it needs to. In OOP, the need to use instanceof is an indicator of this, but this is not an OOP issue in itself. – Filip Milovanović Feb 15 at 15:40
  • @dav20011: Sometimes, this rigidity is not that important, but sometimes it is. When it is, the thing to do is to try and reconceptualize - think out of the box, approach the problem in a different way, try different things. It's not about making a little tweak just to make it work. If this results in a better understanding of the problem, and a better code - great, if not, at least it was a fun exercise, and a chance to think the problem through. – Filip Milovanović Feb 15 at 15:41
  • @FilipMilovanović I am fully aware of this and after thinking long enough about the problem, I came to the conclusion that instanceof is the best solution. I just called it wrong, it is not OOP enforcement, it is a task restriction (every instanceof counts as a mistake). – dav20011 Feb 15 at 16:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.