0

I have a overlapping data objects that need to be given to at least one, possibly more, of several calculation methods to produce a common data object.

I think this is best solved by implementing both covarriance contravariance but I haven't been able to wrap my head around how what that would look like so I am open to other suggestions. In reality Animal/IAnimal and Noise have a lot of data properties so I'd like to avoid mapping them in constructors as much as possible.

Public Class Noise
    Public Property Sound() As String
    Public Sub Listen()
        Console.WriteLine(sound)
    End Sub 
End Class

Public Interface IAnimal
    Function Speak() As Noise()
    Property Name() As String
End Interface

Public Class Dog
    Implements IAnimal
    Public Function Speak() As Noise() Implements IAnimal.Speak
        return { new Noise() with { .Sound = "Bark" } }
    End Function
    Public Property Name As String Implements IAnimal.Name
End Class

Public Class Cat
    Implements IAnimal
    Public Function Speak() As Noise() Implements IAnimal.Speak
        return { new Noise() with { .Sound = "Meow" } }
    End Function
    Public Property Name As String Implements IAnimal.Name
End Class

The below test produces the right output, and also works if IAnimal were an abstract base class with abstract function speak. If I understand correctly this means the covariant behavior is achieved, since it is kind of what you expect from inheritance.

 Public Sub ListenToAnimalsTest()
    dim spot = new Dog() With {.Name = "Spot" }
    dim kitty = new Cat() With {.Name = "Kitty" }

    Dim animalList As List(of IAnimal)
    animalList.Add(spot)
    animalList.Add(kitty)
    For Each animal in animalList 
        Console.WriteLine(animal.Name)
        animal.Speak().Listen()
    Next
End Sub

The problem is I also need a CatDog that is an IAnimal

Public Class CatDog
    Implements IAnimal

    Public Overrides Function Speak() As Noise() Implements IAnimal.Speak
        dim myself As Cat = me
        dim andI As Dog = me
        Return { myself.Speak(), andI.Speak()}
    End Function

   Public Property Name As String Implements IAnimal.Name
End Class

I'm trying to work out if I can use contravariance to allow casting Animal or IAnimal to Cat or Dog, obviously only populating the common properties.

Any thoughts or suggestions?

With regard to Decorator / Visitor Pattern I want to be able to continue whatever pattern ends up implemented by making another implementation of the parent or interface so that making the animals speak doesn't change. That means the catDog implementation of Decorator still needs to treat the IAnimal as a cat and then a dog or else I am replicating how to bark and meow in two places am I not?

Kludge
  • 19
  • 1
  • 8
  • You already have it so that IAnimal could be a cat or a dog. Else you would not be able to add them to your list of IAnimal. Your real question should be: "Am I trying to accomplish something that has nothing to do with Polymorphism?". You use an interface to set rules on a concrete class like: "You must do these things". If you are then trying to implement a catdog. That has nothing to do with two classes that could be construed to be either or. As you are using an interface to achieve similar means to different objects, not the same thing twice. – djangojazz Mar 20 '17 at 17:54
  • You're right, turning a cat into a dog is not what i really want. The whole point of catDog is to call cat.speak then dog.speak on the same set of data by simple calling catDog.Speak while iterating through the list. – Kludge Mar 20 '17 at 18:01
  • You either want the [Decorator](http://www.dofactory.com/net/decorator-design-pattern) or [Visitor](http://www.dofactory.com/net/visitor-design-pattern) pattern. First is to add responsibilities to an object without inheritance and the letter is to implement future functions without changing the object itself. – Alex B. Mar 20 '17 at 18:05
  • 1
    Your last edit: This calls for a Decorator. Instead of having various CatDog, BirdDog or CatDogBat classes you "decorate" an "component" (IAnimal) with various "decorators" (Noises). – Alex B. Mar 20 '17 at 18:14

1 Answers1

2

Covariance and contravariance are properties of generic types (e.g. List(Of IAnimal)), which isn't really relevant to your question.

VB.NET doesn't allow true multiple inheritance, but you can create interfaces ICat and IDog that each implement IAnimal. Then you use explicit interface implementations to have your IAnimal bark or meow depending on which interface your object has been cast as.

Jacob Krall
  • 28,341
  • 6
  • 66
  • 76
  • VB strikes again... sigh... in c# then I could inherit both so that casting to either would work. Would Name always point to the same thing regardless of how I cast an AnimalCatDog? – Kludge Mar 20 '17 at 18:22
  • C# also does not have multiple inheritance – Jacob Krall Mar 20 '17 at 18:23
  • And yes, you could have `Name` implemented on `IAnimal` so it always returns the same thing regardless of how you cast it. – Jacob Krall Mar 20 '17 at 18:23