views:

98

answers:

5

We have a class, say LegacyUserSettingsService. LegacyUserSettingsService implements an interface, IUserSettingsService.

You can get an instance of the IUserSettingsService by calling our ApplicationServicesFactory. The factory uses Spring.NET to construct the concrete LegacyUserSettingsService.

The trouble is that new developers sometimes do their own thing and construct new instances of the LegacyUserSettingsService directly (instead of going via the factory).

Is there a way to protect the constructor of the concrete class so it can only be called from the factory? A well-known pattern perhaps?

Note that the concrete class resides in a different assembly (separate from the Factory's assembly, so the internal keyword is not a solution). The factory assembly references the other assembly that contains the concrete class.

Any ideas?

+1  A: 

Mark LegacyUserSettingsService constructors with ObsoleteAttribute with IsError set to true. This is purely a "compile-time attribute", which will result in compilation error everywhere a constructor is used, but an instance can be created via reflection by Spring.NET just fine.

Anton Gogolev
+2  A: 

On the contrary, internal would be a great solution, the key though is to make the factory assembly a "Friend Assembly" of the other. See Friend Assemblies for more info.

Make LegacyUserSettingsService an internal class then in the assemblyinfo.cs of the project that contains that class add the line [assembly:InternalsVisibleTo("Spring.Core")]. This should give Spring.NET access to the type but no other assembly.

joshperry
Friend assemblies and dependency injection frameworks will not give good results. The nice thing in dependency injection is that you define the concrete class in another place, even in a config file, so if you create new concrete class and try to inject it, you'll have to add it to the friends assemblies list (e.g. recompile your code)
sagie
Sorry, but you are mistaken. You put the `InternalsVisibleTo` attribute on the target assembly specifying the DI framework assembly, where the `Activator.CreateInstance` -- or similar -- is executed, as the "Friend".
joshperry
Dind't think about it, nice.
sagie
A: 

The only way I know is to make the constructor private and have static factory methods on the class itself which can be called from the outside.

Of course that would not the meet the requirements of your questions. So what about taking a less technical approach involving, communication and code-review?

klausbyskov
...which will still be accessible for calling from the outside world.
Anton Gogolev
Always a good option, but not as full-proof as a compiler :)
willem
A: 

We found a workable solution. Since our factory is using Spring.NET we can simply make LegacyUserSettingsService's constructor private. Spring will still be able to call the private constructor.

And nobody else will be able to (unless they try really really hard and go the reflection route).

willem
You should then accept this (or Enigmativity's answer that suggested this earlier) as your answer so that the question isn't perpetually "Unanswered" ans so that searchers know which answer solved the problem.
joshperry
True. Unfortunately I have to wait 48 hours before I can answer my own question :)
willem
A: 

You could always have a private constructor that you invoke using System.Reflection.ConstructorInfo or a System.Linq.Expressions.Expression>. This would stop the casual developer from creating instances directly. Your factory object would then be able to create the instance, but those pesky developers would be thwarted!

Enigmativity