Interfaces separated from the class implementation in separate projects? [closed]
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this questionWe work on a middle-size project (3 developers over more than 6 months) and need to make following decision: We'd like to have interfaces separated from concrete implementation. The first is to store the interface in a separate file.
We'd like to go further and separate the data even more: We'd like to have one project (CSPROJ) with interface in one .CS file plus another .CS file with help classes (like some public classes used within this interface, some enums etc.). Then, we'd like to have another project (CSPROJ) with a factory pattern, concrete interface implementation and other "worker" classes.
Any class which wants to create an ob开发者_如何学Pythonject implementing this interface must include the first project which contains the interfaces and public classes, not the implementation itself.
This solution has one big disadvantage: it multiplies the number of assemblies by 2, because you would have for every "normal" project one project with interace and one with implementation.
What would you recommend? Do you think it's a good idea to place all interfaces in one separate project rather than one interface in its own project?
I would distinguish between interfaces like this:
Standalone interfaces whose purpose you can describe without talking about the rest of your project. Put these in a single dedicated "interface assembly", which is probably referenced by all other assemblies in your project. Typical examples:
ILogger
,IFileSystem
,IServiceLocator
.Class coupled interfaces which really only make sense in the context of your project's classes. Put these in the same assembly as the classes they are coupled to.
An example: suppose your domain model has a
Banana
class. If you retrieve bananas through aIBananaRepository
interface, then that interface is tightly coupled to bananas. It is impossible to implement or use the interface without knowing something about bananas. Therefore it is only logical that the interface resides in the same assembly asBanana
.The previous example has a technical coupling, but the coupling might just be a logical one. For example, a
IFecesThrowingTarget
interface may only make sense as a collaborator of theMonkey
class even if the interface declaration has no technical link toMonkey
.
My answer does depend on the notion that it's okay to have some coupling to classes. Hiding everything behind an interface would be a mistake. Sometimes it's okay to just "new up" a class, instead of injecting it or creating it via a factory.
Yes, I think this is a good idea. Actually, we do it here all the time, and we eventually have to do it because of a simple reason:
We use Remoting to access server functionality. So the Remote Objects on the server need to implement the interfaces and the client code has to have access to the interfaces to use the remote objects.
In general, I think you are more loosely coupled when you put the interfaces in a separate project, so just go along and do it. It isn't really a problem to have 2 assemblies, is it?
ADDITION:
Just crossed my mind: By putting the interfaces in a separate assembly, you additionally get the benefit of being able to reuse the interfaces if a few of them are general enough.
I think it you should consider first whether ALL interfaces belong to the 'public interface' of your project.
If they are to be shared by multiple projects, executables and/or services, i think it's fair to put them into a separate assembly.
However, if they are for internal use only and there for your convenience, you could choose to keep them in the same assembly as the implementation, thus keeping the overall amount of assemblies relatively low.
I wouldn't do it unless it offers a proven benefit for your application's architecture.
It's good to keep an eye on the number of assemblies you're creating. Even if an interface and its implementation are in the same assembly, you can still achieve the decoupling you rightly seek with a little discipline.
If an implementation of an interface ends up having a lot of dependencies (on other assemblies, etc), then having the interface in an isolated assembly can simply life for higher level consumers.
They can reference the interface without inadvertently becoming dependent on the specific implementation's dependencies.
We used to have quite a number of separate assemblies in our shared code. Over time, we found that we almost invariably referenced these in groups. This made more work for the developers, and we had to hunt to find what assembly a class or interface was in. We ended up combining some of these assemblies based on usage patterns. Life got easier.
There are a lot of considerations here - are you writing a library for developers, are you deploying the DLLs to offsite customers, are you using remoting (thanks, Maximilian Mayerl) or writing WCF services, etc. There is no one right answer - it depends.
In general I agree with Jeff Sternal - don't break up the assemblies unless it offers a proven benefit.
There are pros and cons to the approach, and you will also need to temper the decision with how it best fits into your architectural approach.
On the "pro" side, you can achieve a level of separation to help enforce correct implementations of the interfaces. Consider that if you have junior- or mid-level developer working on implementations, the interfaces themselves can be defined in a project that they only have read access on. Perhaps a senior-level, team lead, or architect is responsible for the design and maintenance of the interfaces. If these interfaces are used on multiple projects, this can help mitigate the risk of unintentional breaking changes on other projects when only working in one. Also, if you work with third party vendors who you distribute an API to, packaging the interfaces is a very good thing to do.
Obviously, there are some down sides. The assembly does not contain executable code. In some shops that I have worked at, they have frowned upon not having functionality in an assembly, regardless of the reason. There definitely is additional overhead. Depending on how you set up your physical file and namespace structure, you might have multiple assemblies doing the same thing (although not required).
On a semi-random note, make sure to document your interfaces well. Documentation inheritance from interfaces using GhostDoc is a beautiful thing.
This is a good idea and I appreciate some of the distinctions in the accepted answer. Since both enumerations and especially interfaces are by their very nature dependency-less this gives them special properties and makes them immune from circular dependencies and even just complex dependency graphs that make a system "brittle". A co-worker of mine once called a similar technique the "memento pattern" and never failed to point out a useful application of it.
Put an interface into a project that already has many dependencies and that interface, at least with respect to the project, comes with all the dependencies of the product. Do this often and you're more likely to face situations with circular dependencies. The temptation is then to compensate with patches that wouldn't otherwise be needed.
It's as if coupling interfaces with projects having many dependencies contaminates them. The design intent of interfaces is to de-couple so in most cases it makes little sense to couple them to classes.
精彩评论