views:

191

answers:

3

In cases where you have a function that works for many different custom types with the same implementation, is it ok to use a design pattern like this?:

type1 implicitly casts to type0
type2 implicitly casts to type0
type3 implicitly casts to type0

Operate ( type0 )

and call:

type1 a
type2 b
type3 c

Operate ( a )
Operate ( b )
Operate ( c )

Are there any problems with this technique? Performance wise, clarity, etc?

EDIT: Also by implicit cast, I meant custom implemented casts for the type with no data loss, just convenience. Say you have a Pixel class, then sending an instance of this to a method that takes a Point2 automatically casts the pixel to a Point2.

+2  A: 

Casting usually creates a new object, which is unnecessary in this case

The more OOP way to do this would be through a base class or an interface.

class Type1 : IOperatableType {}
class Type2 : IOperatableType {}
class Type3 : IOperatableType {}

void Operate ( IOperatableType a )

or

class Type1 : Type0 {}
class Type2 : Type0 {}
class Type3 : Type0 {}

void Operate ( Type0 a )

The calling method (Operate in this case) depends on using its parameters' methods or properties. If these properties/methods are defined across all types (type1, type2, type3), consider using an interface that defines common fuctionality. If the implementation of the properties and methods are the same, save yourself from repeated code and consider inheriting from a base class.

Also, when trying to understand your code, developers are more likely to first look at a class diagram, which allows them to see the relationship between classes, or at least the class definition (which will show the base types and implemented interfaces) rather than looking into (implicit/explicit) operators to see which class is castable to which other class.

foson
Depends on the relation between the objects. If type2 is a particular case of type1, then polymorphism is the way to go.
dub
Thanks, what's the advantage of this vs the above technique? The reason I wonder is the DoSomething method is exactly the same for all types, except the conversion that needs to be done first.
Joan Venge
Thanks, yeah casting creates new objects but in the case of say Pixel class, if you had X and Y properties, when passing it to a method that requires Point2, you will still need to create these values inside the method, if not implicitly cast.
Joan Venge
+2  A: 

This is a perfectly normal practice.

Performance wise, I don't believe there are any issues.

Clarity wise, because of Intellisense, you're unlikely to have issues with this either.

nbolton
Intellisense won't help you out here. As you type the method Operate(), Intellisense will only tell you that it takes a type0. You won't be able to tell that you could put in a type1 unless you open up the definition of type1.
foson
Actually it will, simply press the up and down keys to scroll through the overloads once Intellisense shows you the method.
nbolton
Clearly this only works for Visual Studio 2005 and above, for any other IDE and your point may be correct.
nbolton
+3  A: 

That's the basis of how interfaces work.

public interface IBlah {
    void DoSomething();
}
public class Type1 : IBlah {
    void DoSomething() { /* implementation */ }
}
public class Type2 : IBlah {
    void DoSomething() { /* implementation */ }
}
public class Type3 : IBlah {
    void DoSomething() { /* implementation */ }
}

public class Foo {
    void Operate(IBlah blah) {
        blah.DoSomething();
    }
}
Chris Doggett
Thanks, what's the advantage of this vs the above technique? The reason I wonder is the DoSomething method is exactly the same for all types, except the conversion that needs to be done first.
Joan Venge
If you need to add a new type, even one that's not related, it'll still work. For example, if types 1-3 are types of cars, type 4 is a person, and DoSomething() is really MoveForward(), as long as the object fulfills the contract of the interface, it'll work.
Chris Doggett
Good point, thanks Chris.
Joan Venge