Note that there are some explanatory texts on larger screens.

plurals
  1. POOOP, Interface Design and Encapsulation
    primarykey
    data
    text
    <p>C# project, but it could be applied to any OO languages. 3 interfaces interacting:</p> <pre><code>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(); } } </code></pre> <p>Clearly <code>Engine</code> is parametric in the actual worker that does the job. A further source of parametrization is how we produce the 'internal data' via <code>IInternalDataProducer</code>. This implementation requires <code>IInternalDataProducer</code> to be public because it's part of the declaration of the public interface <code>IPublicWorker</code>. However, I'd like it to be internal since it's only used by the engine.</p> <p>A solution is make the <code>IPublicWorker</code> 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 <code>IInternalDataProducer</code> is used in more places inside the engine, so it's good for the engine to pass around the actual object.</p> <p>Also not an option is (obviously) to pass an instance of <code>IInternalDataProducer</code> directly to the <code>Engine</code> constructor. We don't want users of the library (<code>Engine</code>) to know about data producers. It's the worker's responsibility to declare (produce) what data producer should be used.</p> <p>I'm looking for elegant, type-safe, ideas/patterns. Cheers :-)</p> <p><strong>EDIT: Based on Jeff's answer, here's a solution:</strong></p> <p>It's not perfect, because the interface is still 'dirty' with <code>GetData()</code> which the user shouldn't need to see (but my original interface was dirty too), and because it leads to 'interface duplication' (<code>GetData()</code> is declared in 2 interfaces) - you can't have everything.</p> <p>The next problem is how to clean <code>GetData()</code> out of the <code>IPublicWorker</code> interface then :)</p> <pre><code>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(); } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload