views:

216

answers:

2

I'm designing a framework which users can extend. Basically I will provide them a set of interfaces which they can implement, and my "manager" class will call their implementations like this:

abstract1 = factory.GetConcreteExtension1()
abstract2 = factory.GetConcreteExtension2()
abstract1.DoSomething(abstract2)

Where my client implements the concrete version of the abstractions. However they might decide to add data to concrete2 which is not in the abstract2 interface. This will force them to downcast abstract2 to concrete2 in concrete.DoSomething implementation.

This looks like a code smell because I force them to implement a method (DoSomething) which in its signature expects abstract1 but actually can only get concrete1 (plus I force them to write the casting).

I can't find a solution that both keeps contract well defined and allows my framework to manage the process by itself. Any ideas?

+1  A: 

It would depend on the language you are using, but in C# for example you could use templates with a constraint on them to give you type safety while still enforcing that the class must be derived from your abstract base class.

Eric Petroelje
But in this case the responsibility goes from the called function to the caller - it has to put the correct type in the generic parameter. So it's still a strange api - it looks like it is generic but actually allows only one type...
Yaron Naveh
@Yaron - it is, but on the other hand, this is a bit of a strange API to begin with. The DoSomething() method is declared to take an "abstract2" as a parameter, yet the logic inside of it may require it to do things that an "abstract2" can't do (hence the need for a downcast). I'd agree with Hans here that the best thing would be to make sure that the "abstract2" class has all the required behavior, making a downcast unneccessary.
Eric Petroelje
This would work if I would implement all the code. However this is a framework users will extend. Let me give you a concrete example:- Developers will implement a UI form that returns some object based on user input- Additional objects implemented by the same developer will require as input the one that the UI returns. My framework is responsible to invoke them and give them the data.This object will contain data that I cannot know of when I design the interface.
Yaron Naveh
+2  A: 

So abstract1's DoSomething is called with the parameter abstract2 and you want to get to abstract2's data from the DoSomething method?

In this case it seems to me that it would be better to give abstract2 behaviour so that abstract1 interacts with it through abstract2's behaviour and not through its data.

Hans Malherbe
+1. Exactly right. abstract1.DoSomething(abstract2) should call abstract2.SomeInterfaceMethodOfAbstract2() (which is implemented in concrete2) to get things done, rather than downcasting abstract2 to concrete2 and accessing the data directly.
j_random_hacker
I mean that abstract1.DoSomething(abstract2) can call any number of methods in the abstract2 interface, provided they are in the interface. However, if the desired behaviour depends on the concrete types of both abstract1 and abstract2, then the cleanest solution I know of is to request "named" interfaces as necessary like QueryInterface() in COM. Here is a C++ approach: http://stackoverflow.com/questions/851323/reflection-in-c/853130#853130
j_random_hacker
Actually it's not abstract2 behavior we are interested in but its data. So abstract1 needs access to concrete2.SomeData - but I don't know SomeData in my framework so I can't add it to the abstract api.As for named interfaces - that still looks like downcast in disguise...
Yaron Naveh
@Yaron: Yes, "named interfaces" is a "downcast in disguise" -- it's just a way of managing this in an extensible way. Re needing data not behaviour: are you sure you need the data? Suppose you need the data in order to do X with it; then a better way is to give abstract2 an interface method that returns a "command" object of type abstract3 containing interface methods for doing X. An implementation (e.g. concrete3, which knows the details of both concrete1 and concrete2) must implement these methods.
j_random_hacker
@Yaron: If the additional abstract2 method I propose that returns an abstract3 object *does* depend on details of concrete1, then for it to be maintainable it must take the "name" of concrete1 and look this up in a table of supported types -- IOW, named concrete types. (Look up "multiple dispatch" and "multimethod".) Sometimes you have pairs/triples/etc. of interface types that interact strongly and this design choice is forced, but if you can design things so that X depends only on details of concrete2, then this is unnecessary.
j_random_hacker
j_random_hackerThanks for the suggestions. But any way you look at it it seems there is a "downcast" or a "named interface" or something which is not "typed"... b/c if abstract3 is in my sdk it cannot contain anything specific to a concrete extension so concrete1 would have to cast it to concrete3...
Yaron Naveh
@Yaron: Nearly -- abstract3 cannot refer to concrete1 (or concrete-anything), but concrete3 is allowed to refer to concrete2. Observe that this is totally safe and downcast-free if the *only* way that an instance of concrete3 can be created is by the factory method in concrete2 (this requires language support, e.g. in C++ you would make all of concrete3's constructors private, and make concrete2 a friend of concrete3.). How? concrete3's ctor takes a ref-to-concrete2 as a param, and concrete2's implementation of the factory method calls the ctor, passing a ref to itself (*this).
j_random_hacker
The idea is that each concrete class implementing abstract2 chooses it's own particular concrete implementation of abstract3 to return from the factory method (let's call it CreateCmd()). E.g. suppose conc2a and conc2b are separate implementations of abstract2; then conc2a.CreateCmd() will return an instance of conc3a (that conforms to abstract3 but knows that it is dealing with a conc2a object), while conc2b.CreateCmd() will return an instance of conc3b (that also conforms to abstract3 but knows that it is dealing with a conc2b object). So you can do: "abstract2.CreateCmd().DoX();"
j_random_hacker
The difficulty is if doing X requires knowledge of the concrete types of *both* the abstract1 and the abstract2 objects, rather than just the latter. Then you would need to pass the "name" of the concrete type of the abstract1 object as a param to abstract2.CreateCmd() so that this factory function could return an implementation of abstract3 that is tailored to both -- known -- concrete object types. This requires either an object registry (relatively clean) or a big switch stmt (messy). And yes, constructing that command object *will* require a downcast of the abstract1 object type.
j_random_hacker
I don't think abstract3 improves the situation sue to its access to concrete2 as abstract2 has also trivially access to concrete2. Since I do not know what to put in the abstract2 in order to cover all possible concrete2 I also do not know what to put in abstract3 which brings us to the same position.I don't think there is a way to run from downcast under my requirements. I'm now looking for justifications in the .net base class library...
Yaron Naveh