views:

69

answers:

2

Assuming you had some kind of factory-created resource that would still belong to the factory after construction like this:

public class Resource : IDisposable {
  public void Dispose() { /* ... */ }
}

public class ResourceManager : IDisposable {
  public Resource Load() { /* create resource and add to list */ }
  public void Dispose() { /* dispose all loaded resources in list */ }
}

Now you have a base class that requires a Resource instance in its constructor to initialize:

public class Fooificator {
  public Fooificator(Resource resource) {
    this.resource = resource;
  }
  private Resource resource;
}

How can a class that derives from such a base class construct a Resource without loosing the reference to the factory?

The naive approach, using a static method, has no way of remembering the factory (except for ugly hacks like storing it in a static field for the constructor to pick up after base class construction finishes, or using a two-stage initialization where the Resource is given to the base class through a constructor-called Initialize() method)

public class DefaultFooificator : Fooificator {
  public DefaultFooificator() : base(loadDefaultResource()) {}

  private static Resource loadDefaultResource() {
    ResourceManager resourceManager = new ResourceManager();
    return resourceManager.Load();
    // Bad: losing reference to ResourceManager here!
  }
}

One possible workaround would be the named constructor idiom:

public class DefaultFooificator : Fooificator, IDisposable {

  public static DefaultFooificator Create() {
    ResourceManager resourceManager = new ResourceManager();
    DefaultFooificator fooificator = new DefaultFooificator(
      resourceManager.Load()
    );
    fooificator.resourceManager = resourceManager;

    return fooificator;
  }

  private DefaultFooificator(Resource resource) : base(resource) {}
  private ResourceManager resourceManager;
}

However, while solving the problem of storing the ResourceManager reference, this will not allow for further classes to derive from the DefaultFooificator.

Are there any elegant solutions that would enable me to further derive from DefaultFooificator or to store the ResourceManager reference in some other way?

+1  A: 

Why should a class deriving from Fooificator be responsible for constructing a Resource? Why can't you just inject this dependency into its constructor?

public class DefaultFooificator : Fooificator
{
    public DefaultFooificator(Resource resource) : base(resource) {}
}

You could defer the creation of the resource to a FooificatorFactory.

Matt Howells
That's about what my final example does. Probably you're referring to an abstract factory whereas my named constructor is in fact a factory method, but the result is the same.Both cases wouldn't allow for further classes to derive from `DefaultFooificator`, mine because the runtime can't derived based in the static method and yours because it would pull the deriving class into the responsibility of providing the `DefaultFooificator`'s `Resource`, of which the data required to construct it is a private implementation detail of the `DefaultFooificator`.
Cygon
+1  A: 

I think you should consider whether your classes aren't too tightly coupled. If Resource has a public constructor, everyone can create an instance and pass it as a reference to the Fooificator constructor. That is actually a good thing.

What seems less attractive is that you seem to have a requirement that only Resources created by ResourceManager.Load (BTW, this is a violation of Command/Query Separation) can be legally used. However, the API doesn't enforce this constraint.

Either you should enforce this constraint by making the Resource constructor internal, or by lifting the ResourceManager constraint.

Internal constructors tends to pull in the direction of tight class coupling, so I'd seriously consider the second option.

Alternatively, change the constructor of Fooificator to take a reference to the ResourceManager, or perhaps a Func<ResourceManager, Resource>.

Without more context it is difficult to give a better answer, I think.

Mark Seemann
Agreed, but it's hard to state the problem without putting people of with several pages of explanations. In the actual codebase, which is an XNA game, `Resource` and `ResourceManager` (their real names being `Effect` and `ContentManager`) are provided by Microsoft's framework and, sadly, outside of my control. The `Resource` (aka `Effect`) class already uses an `internal` constructor, I just omitted it for trim down the code.
Cygon
Adding a `ResourceManager` reference to the `Fooificator` would break up encapsulation, since `Resource` instances, in the real code, can be created either through the `ResourceManager` or by a static method `Resource.CompileFromFile()` (also outside of my control), so in at least one case, the `ResourceManager` would be `null`.
Cygon
@Cygon: Thanks for the explanation. Every time I find an existing API inadequate, but I can't change it, I usually end up creating an interface that shields me from it - an Anti-Corruption Layer, if you will. That could be an interface that creates Resource instances, such as the Func I was alluding to - functions/delegates are basically just anonymous one-method interfaces...
Mark Seemann