When writing code, I try to structure my project in such a way that it’s impossible to misuse. I often run into cases where I need some class to be responsible for instantiating and managing its own resources, such as a message hub (which needs to maintain a list of subscribers) or a tweening engine (which would need to update and dispose of tween control objects according to a timer). In these cases, I like to ensure that these objects can never be created outside the class which manages them.
A simple solution, of course, is to use internal
constructors, but I personally prefer to avoid using the internal
access modifier whenever possible, as I find it does a poor job of signalling the intent of the code, and in many cases is just a band-aid fix for a poorly planned access scheme. Furthermore, in cases where the class in question will only be consumed within the same project, internal
is essentially no different than public
.
My solution to this problem is as follows:
class Manager
{
// seal class to prevent derivation
public sealed class Resource
{
// private constructor;
// prevents creation outside Resource
private Resource()
{
}
public class Factory : IResourceFactory
{
// method is explicitly-implemented;
// prevents invocation except through interface
Resource IResourceFactory.Create() => new Resource();
}
}
// factory interface is private;
// prevents Create() from being called outside Manager
private interface IResourceFactory
{
Resource Create();
}
// can be instantiated from anywhere, but has no public interface
private IResourceFactory factory = new Resource.Factory();
}
Whenever a new Resource
needs to be created, the Manager
class (and nothing else!) can instantiate it with a call to factory.Create()
. This method signature can of course be modified to pass any other parameters to the private constructor as needed.
It is of course possible to not expose the concrete Resource
class at all and instead use an interface/implementation pattern, but I don’t really like this approach — the objects I want to serve back are concrete, and the user shouldn’t be led to believe that they can implement the same interface in their own classes.
One apparent flaw in this approach is that the Factory
class is still publicly available (this could of course be made internal
, though all this would achieve is a slight decluttering of the shipped interface). However, since the factory interface itself is private (and all methods are implemented explicitly), an externally-created instance of the factory would be entirely useless. I prefer to implement these classes as simply as possible — nothing more than a single method implementation — so realistically there’s no problem with spurious instances being created. In case the factory needs to be a little more complex, it’s possible to further lock the pattern down, as below:
class Manager
{
public Manager()
{
// instantiate factory in ctor
// so we can access 'this'
factory = new Resource.Factory(this);
}
// seal class to prevent derivation
public sealed class Resource
{
// private constructor;
// prevents creation outside Resource
private Resource()
{
}
// can be instantiated from anywhere, but has no public interface
public class Factory : IResourceFactory
{
public Factory(Manager owner)
{
// require a non-null Manager parameter
if (owner == null)
throw new ArgumentNullException("owner");
// ensure it doesn't already have a factory
if (owner.factory != null)
throw new Exception("Cannot instantiate Resource.Factory");
}
// method is explicitly-implemented;
// prevents invocation except through interface
Resource IResourceFactory.Create() => new Resource();
}
}
// factory interface is private;
// prevents Create() from being called outside Manager
private interface IResourceFactory
{
Resource Create();
}
private readonly IResourceFactory factory;
}
I sometimes use this “owner” approach if I need the constructed resources to have a link back to their parent object; for example, to allow them to unsubscribe or send messages up the chain. Another approach is to not pass in any parameter at all, but rather check against a static field, which turns the factory into a proper singleton:
public Factory()
{
if (factory != null)
throw new Exception("Cannot instantiate Resource.Factory");
}
private static readonly IResourceFactory factory = new Resource.Factory();
I’ve used all of these variations at one time or another, depending on the requirements of each particular case. I hope you find it useful as well.