views:

171

answers:

5

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();
    }
}
A: 

Why not define GetInternalProducer() as

  object GetInternalProducer();

and use something like

   IInternalDataProducer producer = GetInternalProducer() as IInternalDataProducer.

you would have to check for null pointer, but you don't need to expose IInternalDataProducer anymore.

This way you can provide a factory to generate IInternalProducer objects inside Engine and associate them with IPublicWorker objects or any other code througout the application. Otherwise the client code has to know about IInternalDataProducer thus violating the encapsulation of the Engine.

Oleg Zhylin
It doesn't solves described problem but adds type non-safety and non-needed assumptions.
Restuta
Well, I'm looking for a type safe design.
Mau
If an Internal Producer is associated with a Public Worker, but workers do not care about it this is the way to keep IInternalDataProducer private. Engine could have a factory to produce objects which can be later passed to back Engine and cast to an appropriate interace. In the solution you've posted the client code must still know about IInternalDataProducer which entails that the only possible client code is Engine code itself.Type safety indeed has to enforced in run-time, but this fits quite well in C# paradigm.
Oleg Zhylin
A: 

Edited to reflect requirements from comments:

public interface IPublicData { }

public interface IDataProducer { string GetData(); }

internal interface IInternalDataProducer : IDataProducer { string GetData(); }

internal class InternalDataProducer : IInternalDataProducer
{
    public string GetData()
    {
        throw new NotImplementedException();
    }
}

public interface IPublicWorker
{
    IPublicData DoWork();
    IDataProducer GetInternalProducer();
}

class PublicWorker : IPublicWorker
{
    public IPublicData DoWork()
    {
        throw new NotImplementedException();
    }

    public IDataProducer GetInternalProducer()
    {
        return new InternalDataProducer(); //here you binds PublicWorker to paricular data provider
    }
}


public class Engine
{
    private IPublicWorker worker;

    public Engine(IPublicWorker worker)
    {
        this.worker = worker;
    }

    IPublicData Run()
    {
        DoSomethingWith(this.worker.GetInternalProducer());
        return worker.DoWork();
    }

    private void DoSomethingWith(IDataProducer getData)
    {
        throw new NotImplementedException();
    }
}
Restuta
Thanks Restuta. That doesn't really solve the problem because1) the user of Engine would need to know about internal data producers, which I'm trying to eliminate.2) It's the IPublicWorker's responsibility to declare what IInternalDataProducer should be used to gather data (each worker implementation can pick one of few concrete producers).
Mau
I will update my answer according your remarks.
Restuta
It seems you are trying to achieve static binding. I mean statically bind IPublicWorker to a paticular DataProvider, how about to do this dynamically? You will create generic interface IDataProducer and your every paticular IPublicWorker realization will know what inheritor (IInternalDataProducer ) in this case it should use?
Restuta
See updated code.
Restuta
Uhm, there is no 'public' IDataProducer. So we have just shifted the problem of hiding InternalDataProducer to that of hiding IDataProducer.
Mau
So we are exposing dependency and hiding implementation details of different Data Producers. Totally hide this dependency you can only with dependency injection, but calling code will need to configure ioc container or properly inject it. May be you should put your domain logic here to let me think, taks seems very artificial without details.
Restuta
A: 

UPDATE
My solution doesn't seem to be correct, at least it doesn't work as i intended. Maybe someone else can get it to work as it would be the (imho) most logical solution.


let the internal interface inherit from a public interface. This allows the external parts to only access what you really want accessible while still hiding the internal parts.

public interface IPublicData {}

public interface IPublicDataProducer {}
internal interface IInternalDataProducer : IPublicDataProducer { string GetData(); }

public interface IPublicWorker {
    IPublicData DoWork();
    IPublicDataProducer GetProducer();
}

public class Engine {
    Engine(IPublicWorker worker) {}
    IPublicData Run() {
        DoSomethingWith(worker.GetProducer());
        return worker.DoWork();
    }
}

This allows you to seperate the data for the external and internal users of your interfaces.

dbemerlin
Yes! This is close to what I'd like. But still you need a cast inside DoSomethingWith (from IPublicDataProducer to IInternalDataProducer).
Mau
Hmm, seems this is an additional requirement =)
Restuta
If you need to cast to CONCRETE IInternalDataProducer withing an Engine type than I am missing somethig, cos you were saying "We don't want users of the library (Engine) to know about data producers. "
Restuta
A: 

Actually, Oleg's solution is not that bad, but can be improved.

Given that you want to put restrictions on the IPublicWorker interface, I'm assuming that you want to control the implementations of IPublicWorker, and provide your users with a specific API to obtain them. If that is the case, you could derive an IInternalPublicWorker from IPublicWorker, and in your Engine's constructor, verify that the IPublicWorker is indeed of the expected type:

public interface IPublicData {}


public internal interface IInternalDataProducer { string GetData(); }

public interface IPublicWorker {
    IPublicData DoWork();
}

public internal interface IInternalPublicWorker : IPublicWorker {
    IInternalDataProducer GetInternalProducer();
}

public class Engine 
{
    IInternalPublicWorker _worker;

    Engine(IPublicWorker worker) 
    { 
        if (!(worker is IInternalPublicWorker)) 
        {
           throw new InvalidOperationException("We don't support workers that were not obtained from our library."); // add some helpful message about which method to use to obtain a worker
        }
        _worker = (IInternalPublicWorker)worker;
    }
    IPublicData Run() 
    {
        DoSomethingWith(_worker.GetInternalProducer().GetData());
        return _worker.DoWork();
    }
}
jeroenh
A: 

You can solve this by encapsulating the data producer within the worker:

public interface IPublicWorker {
    IPublicData DoWork();
    // Callers don't care how a worker gets this data
    string GetData();
}

The call in engine then looks like this:

IPublicData Run() {
    DoSomethingWith(worker.GetData());
    return worker.DoWork();
}
Jeff Sternal
You're missing your IPublicWorker member inside engine, but nit-picks aside, that's exactly what I would have recommended too.
Andy_Vulhop
Thanks Jeff. Unfortunately that's just what I'm trying to avoid. 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, and this should be transparent to the user.
Mau
@Mau - I understand you're trying to avoid this, but I'm trying to convince you that doing so would be a bad idea! We could rearrange things a bit to inject the data producer at the worker instead of the engine (though that sounds like the wrong owner, given what you've described) - but that just shifts the problem to the worker.
Jeff Sternal
This is the copy of my solution
Restuta
@Mau, I'll try this from another angle - I may have been assuming too much (e..g, that you're connecting to a database, etc.).
Jeff Sternal
@Jeff: no databases involved. Your latest solution works and is type safe. The actual ways GetData() can be implemented are just a few, while there are many implementations of IPublicWorker. So it makes sense to have GetData() be part of an interface that has few implementations. IPublicWorker can then just build the right implementation internally and use it. Kinda trivial actually :-)
Mau
Yes this is trivial, but your Public Worker also gets data, not only does work, seems single responsibility principle is broken here. So all my solutions was based on assumption that you want separate concepts of getting data and doing work. :(
Restuta
@Restuta - the logic to get data is still in another class, which is then just an implementation detail as far as external callers are concerned. The public worker still only has one reason to change, which is the heart and soul of the single responsibility principle.
Jeff Sternal
Object that will implement IPublicWorker will still do two things no matter how many classes helps to do this, and I don't understand your poing about "one reason to change".
Restuta
For "one reason to change" I was just referring to Bob Martin's [classic definition of the single responsibility principle](http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod). Having said that, I agree with you: I would probably use constructor injection just as you suggest in your answer.
Jeff Sternal