C# project, but it could be applied to any OO languages. 3 interfaces interacting:
public interface IPublicData {}
public /* internal */ interface IInternalDataProducer { string GetData(); }
public interface IPublicWorker {
IPublicData DoWork();
IInternalDataProducer GetInternalProducer();
}
public class Engine {
Engine(IPublicWorker worker) {}
IPublicData Run() {
DoSomethingWith(worker.GetInternalProducer().GetData());
return worker.DoWork();
}
}
Clearly Engine
is parametric in the actual worker that does the job. A further source of parametrization is how we produce the 'internal data' via IInternalDataProducer
. This implementation requires IInternalDataProducer
to be public because it's part of the declaration of the public interface IPublicWorker
. However, I'd like it to be internal since it's only used by the engine.
A solution is make the IPublicWorker
produce the internal data itself, but that's not very elegant since there's only a couple of ways of producing it (while there are many more worker implementations), therefore it's nice to delegate to a couple of separate concrete classes. Moreover, the IInternalDataProducer
is used in more places inside the engine, so it's good for the engine to pass around the actual object.
Also not an option is (obviously) to pass an instance of IInternalDataProducer
directly to the Engine
constructor. We don't want users of the library (Engine
) to know about data producers. It's the worker's responsibility to declare (produce) what data producer should be used.
I'm looking for elegant, type-safe, ideas/patterns. Cheers :-)
EDIT: Based on Jeff's answer, here's a solution:
It's not perfect, because the interface is still 'dirty' with GetData()
which the user shouldn't need to see (but my original interface was dirty too), and because it leads to 'interface duplication' (GetData()
is declared in 2 interfaces) - you can't have everything.
The next problem is how to clean GetData()
out of the IPublicWorker
interface then :)
public interface IPublicData {}
internal /* ! */ interface IInternalDataProducer { string GetData(); }
public interface IPublicWorker {
IPublicData DoWork();
string GetData();
}
public class APublicWorker : IPublicWorker {
private IInternalDataProducer dataProducer;
public APublicWorker() {
dataProducer = new SomeDataProducer();
}
IPublicData DoWork() { ... }
string GetData() {
/* Delegation! */
return dataProducer.GetData();
/* ********* */
}
}
public class Engine {
Engine(IPublicWorker worker) {}
IPublicData Run() {
DoSomethingWith(worker.GetData());
return worker.DoWork();
}
}