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?