Programming to an Interface

Code smell is a way of saying that some code is problematic and will have difficulty in future maintainability. It is an indication of technical debt and need to be repaired as early as possible before it getting difficult to fix.

In this section we will look at a code smell related to “inappropriate intimacy”. Below is an example of code smell due to the direct object instantiation (use of “new” keyword).

    public class LoginController
    {
        private readonly LoginService _LoginService;
        public LoginController()
        {
            _LoginService = new LoginService();
        }

        public void ChangeLoginName(int userId, string loginName)
        {
            var userRepository = new UserRepository();
            var user = userRepository.GetUserBy(userId);
            _LoginService.ChangeLoginName(user, loginName);
        }
    }

There are several problems associated with the above code:

  • Dependencies

    • LoginController is dependent on the LoginService and UserRepository implementations. If we want to enhance the LoginService implementation LoginController need to modify to refer to the new implementation which is not a good option. Also If LoginService or UserRepository have some other dependencies LoginController also implicitly depends on those.
  • Testability

    • LoginController is very difficult to unit test due to the tight coupling with LoginService and UserRepository and it is difficult to mock.
  • ChangeLoginName method need to load User instance which is actually needed for the LoginService class.

Refactor: Coding to interface

First thing we need to do is hide the implementation of LoginService behind an interface. This allows LoginController to depend only on the interface and not on the implementation.

    public interface ILoginService
    {
        void ChangeLoginName(int userId, string loginName);
    }

    public class LoginService : ILoginService
    {
        public void ChangeLoginName(int userId, string loginName)
        {
            var userRepository = new UserRepository();
            var user = userRepository.GetUserBy(userId);
            user.ChangeLoginName(newPassword);
        }
    }

Update the LoginController so that it is no longer depends on LoginService class but rather depend on the ILoginService interface.

    public class LoginController
    {
        private readonly ILoginService _LoginService;

        public LoginController()
        {
            _LoginService = new LoginService();
        }

        public void ChangeLoginName(int userId, string loginName)
        {
            _LoginService.ChangeLoginName(userId, loginName);
        }
    }

Still there is a dependency on LoginService implementation because of the constructor. To separate the two classes completely, further refactor with Dependency Injection (DI) using constructor

    public class LoginController
    {
        private readonly ILoginService _LoginService;

        public LoginController(ILoginService loginService)
        {
            _LoginService = loginService;
        }

        public void ChangeLoginName(int userId, string loginName)
        {
            _LoginService.ChangeLoginName(userId, loginName);
        }
    }
    public class LoginService : ILoginService
    {
        private readonly IUserRepository _UserRepository;

        public LoginService(IUserRepository userRepository)
        {
            _UserRepository = userRepository;
        }
        public void ChangeLoginName(int userId, string loginName)
        {
            var user = _UserRepository.GetUserBy(userId);
            user.ChangeLoginName(loginName);
        }
    }

    public interface IUserRepository
    {
        User GetUserBy(int userId);
    }

    public interface ILoginService
    {
        void ChangeLoginName(int userId, string loginName);
    }