11

OK .. after all the discussion I'm changing my question slightly to better reflect a concrete example that I am dealing with.


I have two classes ModelOne and ModelTwo, These classes perform similar type of functionality but are unrelated to each other. However I have a third class CommonFunc that contains some public functionality that is implemented in both ModelOne and ModelTwo and has been factored out as per DRY. The two models are instantiated within the ModelMain class (which itself is instantiated at a higher level etc - but I am stopping at this level).

The IoC container that I am using is Microsoft Unity. I don't pretend to be an expert in it, but my understanding of it is that you register a tuple of interface and class with the container and when you want a concrete class you ask the IoC container for whatever object matches a specific interface. This implies that for every object I want to instantiate from Unity, there has to be a matching interface. Because each of my classes performs different (and-non-overlapping) functionality this means that there is a 1:1 ratio between interface and class1. However it does not mean that I am slavishly writing an interface for each and every class I write.

Thus code wise I end up with2:

public interface ICommonFunc 
{ 
}

public interface IModelOne 
{ 
   ICommonFunc Common { get; } 
   .. 
}

public interface IModelTwo
{ 
   ICommonFunc Common { get; } 
   .. 
}

public interface IModelMain 
{ 
  IModelOne One { get; } 
  IModelTwo Two { get; } 
  ..
}

public class CommonFunc : ICommonFunc { .. }

public class ModelOne : IModelOne { .. }

public class ModelTwo : IModelTwo { .. }

public class ModelMain : IModelMain { .. }

The question is about how to organize my solution. Should I keep the class and interface together? Or should I keep classes and interfaces together? EG:

Option 1 - Organized by class name

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Common
          |   |
          |   |-CommonFunc.cs
          |   |-ICommonFunc.cs
          |
          |-Main
          |   |
          |   |-IModelMain.cs
          |   |-ModelMain.cs
          |
          |-One
          |   |
          |   |-IModelOne.cs
          |   |-ModelOne.cs
          |
          |-Two
              |
              |-IModelTwo.cs
              |-ModelTwo.cs
              |

Option 2 - Organized by functionality (mostly)

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Common
          |   |
          |   |-CommonFunc.cs
          |   |-ICommonFunc.cs
          |
          |-IModelMain.cs
          |-IModelOne.cs
          |-IModelTwo.cs
          |-ModelMain.cs
          |-ModelOne.cs
          |-ModelTwo.cs
          |

Option 3 - Seperating Interface and Implementation

MySolution
  |
  |-MyProject
      |
      |-Interfaces
      |   |
      |   |-Models
      |   |   |
      |       |-Common
      |       |   |-ICommonFunc.cs
      |       |
      |       |-IModelMain.cs
      |       |-IModelOne.cs
      |       |-IModelTwo.cs
      |
      |-Classes
          | 
          |-Models
          |   |
              |-Common
              |   |-CommonFunc.cs
              |
              |-ModelMain.cs
              |-ModelOne.cs
              |-ModelTwo.cs
              |

Option 4 - Taking the functionality example further

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Components
          |   |
          |   |-Common
          |   |   |
          |   |   |-CommonFunc.cs
          |   |   |-ICommonFunc.cs
          |   |   
          |   |-IModelOne.cs
          |   |-IModelTwo.cs
          |   |-ModelOne.cs
          |   |-ModelTwo.cs
          |
          |-IModelMain.cs
          |-ModelMain.cs
          |

I sort of dislike option 1 because of the class name in the path. But as I am tending to 1:1 ratio because of my IoC choice/usage (and that may be debatable) this has advantages in seeing the relationship between the files.

Option 2 is appealing to me, but now I have muddied the waters between the ModelMain and the sub-models.

Option 3 works to separate the interface definition from the implementation, but now I have these artificial breaks in the path names.

Option 4. I took Option 2 and tweaked it in order to separate the components from the parent model.

Is there good reason for preferring one over the other? Or any other potential layouts that I have missed?


1. Frank made a comment that having 1:1 ratio harkens back to C++ days of .h and .cpp files. I know where he is coming from. My understanding of Unity seems to put me into this corner, but I am also not sure even how to get out of it if you are also following the adage of Program to an interface But thats a discussion for another day.

2. I have left out the details of each objects constructor. This is where the IoC container injects objects as required.

  • 1
    Ugh. It smells that you have a 1:1 interface/class ratio. Which IoC container are you using? Ninject provides mechanisms that don't require a 1:1 ratio. – RubberDuck Jul 26 '17 at 21:03
  • @RubberDuck FWIW I'm using Unity. I don't claim to be an expert in it, but if my classes are well designed with single responsibilities, how do i not end up with almost a 1:1 ratio? – Peter M Jul 26 '17 at 22:10
  • Do you need IBase or could base be abstract? Why have IDerivedOne when it already implements IBase? You should be depending on IBase, not derived. I don't know about Unity, but other IoC containers allow you to do "context sensitive" injection. Basically when Client1 needs an IBase, it provides a Derived1. When Client2 needs an IBase, the IoC provides a Derived2. – RubberDuck Jul 26 '17 at 22:16
  • @RubberDuck Nominally Base is abstract but I don't see that that changes my question in general. And Unity is very lightweight/simplistic in its approach. See msdn.microsoft.com/en-us/library/dn507499(v=pandp.30).aspx for example – Peter M Jul 26 '17 at 22:24
  • 1
    Well, if base is abstract, then there's no reason to have an interface for it. An interface is really just an abstract class with all virtual members. – RubberDuck Jul 26 '17 at 22:26
4

Since an interface is abstractly similar to a base class, use the same logic you would use for a base class. Classes implementing an interface are closely related to the interface.

I doubt you would prefer a directory called "Base Classes"; most developers would not want that, nor a directory called "Interfaces". In c#, directories are also namespaces by default, making it doubly confusing.

The best approach is to think about how you would break up the classes/interfaces if you had to put some into a separate library, and organize the namespaces/directories in a similar manner. The .net framework design guidelines has namespace suggestions that may be helpful.

  • I am sort of familiar with the link you provided, but I am not sure how it relates to file organization. While VS defaults to path names for the initial namespace I know that this is an arbitrary choice. Also I have update my code 'sample' and layout possibilities. – Peter M Jul 27 '17 at 17:00
  • @PeterM I am not sure which IDE you are using, but Visual Studio does use the directory names to automatically generate the namespace declarations when adding a class, for instance. This means that while you can have differences between directory names and namespace names, it is more painful to work that way. So most people don't do that. – Frank Hileman Jul 27 '17 at 17:30
  • I am using VS. And yes I know pain. It brings back memories of Visual Source Safe file layout. – Peter M Jul 27 '17 at 17:36
  • 1
    @PeterM With regard to 1:1 interface to class ratio. Some tools require this. I view this as a defect of the tool -- .net has enough reflection capabilities to not need such restrictions in any such tool. – Frank Hileman Jul 27 '17 at 17:56
1

I do take the second approach, with some more folders/namespaces in complex projects of course. It means that I de-couple the interfaces from the concrete implementations.

In a different project, you may have to know the interface definition, but it is not at all necessary to know any concrete class implementing it - especially when you use an IoC container. So those other projects only need to reference the interface projects, not the implementation projects. This can keep references low, and can prevent circular reference issues.

  • I have revised my code example and added some more options – Peter M Jul 27 '17 at 16:56
1

As always with any design question, the first thing to do is determine who are your users. How are they going to use your organization?

Are they coders who will be using your classes? Then separating the interfaces from the code may be best.

Are they maintainers of your code? Then keep the interfaces and classes together may be best.

Sit down and create some use-cases. Then the best organization may appear before you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.