0 Comments

What's the best practice on interface placement?

I was playing around with this idea after seeing an episode of DNRTV and thought it might be nice to just put this up in the blog and ask others' opinions on this subject.

For now, I can only think of 3 possible options for this issue.  And they are as follow:

Option 1.
Where: Place them in the assembly where the interface is implemented
When:

  • You should use this if you have all of your code inside a single assembly
  • You should use this when the majority of the time the interface is being used by derived classes in the same assembly

Some possible motives: 

  •   simple deployment (hey it's only a single dll)
  •   trying to implement some sort of design pattern / abstraction   (for example: plugin framework)

Example: I have an ILogWriter that is being used internally and my application configuration has a section in it where I can choose which kind of logging mechanism to use.

//Assembly : Foo.exe
//File     : ILogWriter.cs
namespace Foo
{
    public interface ILogWriter
    {
        void Write(string text);
    }
} 
//Assembly : Foo.exe
//File     : ConsoleLogWriter
namespace Foo
{
    public ConsoleLogWriter : ILogWriter
    {
        public void Write(string text)
        {
            Console.WriteLine(text);
        }
    }
}
//Assembly : Foo.exe
//File     : FileLogWriter
using System.IO; 

namespace Foo
{
    public FileLogWriter : ILogWriter
    {
        public void Write(string text)
        {
            //append text to file here
        }
    }
} 

//Assembly : Foo.exe
//File     : Class1.cs
using System;
using System.Configuration; 

namespace Foo
{
    public Class1
    {
        public void Main(string[] args)
        {
             // Do something
             ILogWriter logWriter = (ILogWriter)
             Activator.CreateInstance(Type.GetType(ConfigurationSettings.AppSettings["LogWriter"]));
        }
    }
} 

<!-- File : app.config -->
<appSettings>
    <add value="Foo.ConsoleLogWriter, Foo" key="LogWriter" />
</appSettings> 

Option 2.
Where: Place them in the assembly where the interface is being used
When:

  • You should use this one when the interface is only being used by that one assembly and  any other assembly using that interface will actually use the hosting assembly.

Some possible motives :

  • to only reference assembly that you are actually using.

Example: I have an IViewUserView in my presentation layer assembly and my actual view implementation will reference the presentation layer assembly since its going to use it anyway.

//Assembly : Blah.Presenter.dll
//File     : IViewUserView.cs
namespace Blah.Presenter
{
    public interface IViewUserView
    {
        int UserID { get; }
        string Login { set; }
        string Password { set; }
        // More here ...
    }
}
//Assembly : Blah.Presenter.dll
//File     : ViewUserPresenter.cs
using Blah.DTO;
using Blah.Task; 

namespace Blah.Presenter
{
    public class ViewUserPresenter
    {
        private IViewUserView view;
        private IUserTask task;
        public ViewUserPresenter(IViewUserView view) : this(view, new UserTask());
        public ViewUserPresenter(IViewUserView view, IUserTask task)
        {
            this.view = view;
            this.task = task;
        }
        public DisplayUser()
        {
            IUserDTO userDTO = task.GetUserById(view.UserId);
            view.Login = userDTO.Login;
            view.Password = userDTO.Password;
            // More here ...
        }
    }
} 

Option 3.
Where: Place them in their own assembly (assembly of just interfaces)
When: You should use this one when the interface is used by more than one assembly
Some posible motives :

  • reduce unnecessary references (you now only have to reference a single dll instead of multiples for different interfaces)
  • you want to separate implementations in different projects
  • trying to implement some sort of design pattern / abstraction (for example: extensible plugin framework)

Example: I have an IUserDTO (Data Transfer Object) that is being used by 2 other assemblies (Blah.Task and Blah.Presenter). Then it makes sense to put it into another interface specific assembly (Blah.Core).

//Assembly : Blah.Core.dll
//File     : IUserDTO.cs
namespace Blah.DTO
{
    public interface IUserDTO
    {
        string Login { get; set; }
        string Password { get; set; }
        // More here ...
    }
}
//Assembly : Blah.Core.dll
//File     : IUserTask.cs
namespace Blah.Task
{
    public interface IUserTask
    {
        bool Login(IUserDTO userDTO);
        // More here ...
    }
} 
//Assembly : Blah.Core.dll
//File     : IViewUserView.cs
namespace Blah.View
{
    public interface IViewUserView
    {
        int UserID { get; }
        string Login { set; }
        string Password { set; }
        // More here ...
    }
} 
//Assembly : Blah.Task.dll
//File     : SecurityTask.cs
//Reference: Blah.Core.dll for the IUserDTO interface
//    ...
using Blah.DTO;
using Blah.Task;
using Blah.DataAccess; 

namespace Blah.Task
{
    public class SecurityTask : ISecurityTask
    {
        public bool Login(IUserDTO userDTO)
        {
            return SecurityDataAccess.Instance.Login(userDTO.Login, userDTO.Password);
        }
    }
} 
//Assembly : Blah.Presenter.dll
//File     : ViewUserPresenter.cs
//Reference: Blah.Core.dll for the IUserDTO, IViewUserView and IUserTask interfaces
//    ...
using Blah.DTO;
using Blah.Task;
using Blah.View; 

namespace Blah.Presenter
{
    public class ViewUserPresenter
    {
        private IViewUserView view;
        private IUserTask task;
        public ViewUserPresenter(IViewUserView view) : this(view, new UserTask());
        public ViewUserPresenter(IViewUserView view, IUserTask task)
        {
            this.view = view;
            this.task = task;
        }
        public DisplayUser()
        {
            IUserDTO userDTO = task.GetUserById(view.UserId);
            view.Login = userDTO.Login;
            view.Password = userDTO.Password;
            // More here ...
        }
    }
}

These examples and options are just what were on my mind when I wrote this post and not to be taken as a real guideline for interface placing.  If anyone should know a better way to do this, by all mean, please share them.