views:

145

answers:

5

Hi, Im making a factory method that returns new instances of my objects. I would like to prevent anyone using my code from using a public constructor on my objects. Is there any way of doing this?

How is this typically accomplished:

public abstract class CarFactory
{
  public abstract ICar CreateSUV();
}

public class MercedesFactory : CarFactory
{
  public override ICar CreateSUV()
  {
    return new Mercedes4WD();
  }
}

I then would like to limit/prevent the other developers (including me in a few months) from making an instance of Mercedes4WD. But make them call my factory method. How to?

A: 

You can define the default contructor of your class Mercedes4WD as internal. So the default constructor can only be called within the assembly where the type Mercedes4WD is defined.

Jehof
A: 

for a factory method, make the constructor private but it looks like you are actually implementing the factory pattern

jk
A: 

Keep the constructors internal or implement the interfaces explicitly, so that the classes can not be instantiated directly.

Kangkan
+2  A: 

you can make the class Mercedes4WD a internal class with an internal constructor which will stop it being instantiated from outside of the library which contains the factory.

You might be better having a factory method on your class which produces the instance, which you could call from your factory class, but that is not that different from just 'newing' it up.

You could also have the Mercedes4WD on one assembly, the factory in another and your code in a third, then the factory assembly could be a friend of the Mercedes4WD assembly so it could create the instances, but the main code would not be so it couldn't. That would reduce the scope under which the instances could be created, but everything would a bit more complex.

Really you need to provide some more information. when you say you would like to stop people constructing later, are you talking about other devs working in the same codebase? If so there is not really that much you can do. You might be able to do some checking in the constructor to see if the next method up in the stack is the factory's method and throw an exception if not, but I'm not sure that is a great idea...

The real question is why do you want to do this?

Sam Holder
Just checked around. It is NOT an option to make seperate assemblies (corporate politics). My initial idea was to limit other devs in the team from newing the objects up all around the app. I guess im trying to be the code-police here. My solution may very well be more of a "educational" approach, teach the other devs to only use the factories...
H4mm3rHead
then I think that your best option might be to enforce this through code review. This is fairly easy to check by finding usages of the constructor, and if its used in more than 1 place (the factory) then you need to change it. This is probably better as it gives you the opportunity to explain _why_ they should use the factory.
Sam Holder
Use code review, as Sam said. Or, try enforcing these rules using a tool such as NDepend. It is especially design to do these kinds of validation.
Steven
@Steven: +1 for NDepend. Might also be able to do this with a custom FXCop rule.
Sam Holder
@Sam: +1 for FXCop rule. Note that writing a FxCop rule is pretty hard. On the other hand, FxCop is free. NDepend is certainly not.
Steven
+1  A: 

You really want the C++ equivalent of friend classes, but C# does not have them. You can always make the constructor private and add a static factory method to Mercedes4WD. This would prevent "accidental" direct construction:

public class Mercedes4WD : ICar
{
    private Mercedes4WD()
    {
        // ...
    }

    public static Mercedes4WD Create()
    {
        return new Mercedes4WD();
    }
}

public abstract class CarFactory
{
    public abstract ICar CreateSUV();
}

public class MercedesFactory : CarFactory
{
    public override ICar CreateSUV()
    {
        return Mercedes4WD.Create();
    }
}
Chris Schmich
This is the same as direct construction, only 1 call further. So this will not prevent anyone for creation a Mercedes4WD but will keep out people that try it out without looking up the interface.
PoweRoy
@PoweRoy, right, the idea is to just discourage direct creation since prohibiting it within the assembly is not possible given the problem's constraints.
Chris Schmich