views:

371

answers:

2

I just finished James Kovac's article on Inversion of Control and Dependency Injection and then used what I learned to make my own IoC/DI example below.

I'm quite satisified with this example since it:

  • satisfies the testability aspect of IoC in that it instantiates Customers by passing both a Repository and a MockRepository
  • also the authorization service is decoupled which would allow you to e.g. write another authorization service with different rules, and then you could easily swap them out based on other conditions

However, looking toward progressing from here, some things seem odd:

  • I don't seem to have a "container". Is my Customer class "acting as a container" in this sense?
  • if I were to port this to WPF, where would Modules (in terms of Prism) fit into this example (e.g. would AuthorizationService and Repository be modules?)
  • if I were to port this to WPF, where would MVVM fit in? Do have have parts of MVVM through having the dependency injection or is MVVM something separate altogether.

Thanks for any direction you can provide on this.

NEW CODE BASED ON COMMENTS:

using System;
using System.Linq;
using System.Collections.Generic;

namespace TestSimpleDependencyInjection1
{
    class Program
    {
        static void Main(string[] args)
        {
            AuthorizationService authorizationService = new AuthorizationService();

            //real example
            Repository repository = new Repository(authorizationService);
            for (int id = 1; id <= 3; id++)
            {
                Customer customer = repository.GetCustomer(id);
                customer.Display();
            }
            Console.WriteLine();

            //mock test example
            MockRepository mockRepository = new MockRepository(authorizationService);
            Customer mockCustomerAdministrator = repository.GetCustomer(1);
            Customer mockCustomerSalesperson = repository.GetCustomer(2);
            UnitTester.Assert("Administrators have access", mockCustomerAdministrator.GetAuthorizationMessage(), "Access Granted");
            UnitTester.Assert("Salespeople do not have access", mockCustomerAdministrator.GetAuthorizationMessage(), "Access Granted");

            Console.ReadLine();
        }
    }

    public static class UnitTester
    {
        public static void Assert(string title, string value, string expectedResult)
        {
            Console.WriteLine(value == expectedResult ? String.Format("{0}: test succeeded", title) : String.Format("{0}: TEST FAILED!", title));
        }
    }

    public class Customer
    {

        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public AccessGroup AccessGroup { get; set; }
        public AuthorizationService AuthorizationService { get; set; }

        public string GetAuthorizationMessage()
        {
            return this.AuthorizationService.GetAccessMessage(this);
        }

        public Customer()
        {

        }

        public void Display()
        {
            Console.WriteLine("Customer: {1}, {0} ({2}): {3}", this.FirstName, this.LastName, this.AccessGroup, this.GetAuthorizationMessage());
        }
    }

    public class AuthorizationService
    {
        public string GetAccessMessage(Customer customer)
        {
            return customer.AccessGroup == AccessGroup.Administrator ? "Access Granted" : "Access Denied";
        }
    }


    public class Repository : IRepository
    {
        private List<Customer> _customerSet = new List<Customer>();
        private AuthorizationService _authorizationService;

        public Repository(AuthorizationService authorizationService)
        {
            _authorizationService = authorizationService;
            _customerSet.Add(new Customer {AuthorizationService = _authorizationService, ID = 1, FirstName = "Jim", LastName = "Smith", AccessGroup = AccessGroup.Administrator });
            _customerSet.Add(new Customer {AuthorizationService = _authorizationService, ID = 2, FirstName = "John", LastName = "Johnson", AccessGroup = AccessGroup.Administrator });
            _customerSet.Add(new Customer {AuthorizationService = _authorizationService, ID = 3, FirstName = "Hank", LastName = "Rivers", AccessGroup = AccessGroup.Salesperson });
        }

        public Customer GetCustomer(int id)
        {
            return (from c in _customerSet
                    where c.ID == id
                    select c).SingleOrDefault();
        }
    }

    public class MockRepository : IRepository
    {
        private List<Customer> _customerSet = new List<Customer>();
        private AuthorizationService _authorizationService;

        public MockRepository(AuthorizationService authorizationService)
        {
            _authorizationService = authorizationService;
            _customerSet.Add(new Customer { AuthorizationService = _authorizationService, ID = 1, FirstName = "Test1AdministratorFirstName", LastName = "Test1AdministratorLastName", AccessGroup = AccessGroup.Administrator });
            _customerSet.Add(new Customer { AuthorizationService = _authorizationService, ID = 2, FirstName = "Test2SalespersonFirstName", LastName = "Test2SalesPersonLastName", AccessGroup = AccessGroup.Salesperson });
        }

        public Customer GetCustomer(int id)
        {
            return (from c in _customerSet
                            where c.ID == id
                            select c).SingleOrDefault();
        }

    }

    public interface IRepository
    {
        Customer GetCustomer(int id);
    }

    public enum AccessGroup
    {
        Administrator,
        Salesperson
    }
}

AND PER REQUEST, HERE IS THE ORIGINAL CODE:

using System;
using System.Collections.Generic;

namespace TestSimpleDependencyInjection1
{
    class Program
    {
        static void Main(string[] args)
        {
            AuthorizationService authorizationService = new AuthorizationService();

            //real example
            Repository repository = new Repository();
            for (int id = 1; id <= 3; id++)
            {
                Customer customer = new Customer(id, authorizationService, repository);
                customer.Display();
            }
            Console.WriteLine();

            //mock test example
            MockRepository mockRepository = new MockRepository();
            Customer mockCustomerAdministrator = new Customer(1, authorizationService, mockRepository);
            Customer mockCustomerSalesperson = new Customer(2, authorizationService, mockRepository);
            UnitTester.Assert("Administrators have access", mockCustomerAdministrator.GetAuthorizationMessage(), "Access Granted");
            UnitTester.Assert("Salespeople do not have access", mockCustomerAdministrator.GetAuthorizationMessage(), "Access Granted");

            Console.ReadLine();
        }
    }

    public static class UnitTester
    {
        public static void Assert(string title, string value, string expectedResult)
        {
            Console.WriteLine(value == expectedResult ? String.Format("{0}: test succeeded", title) : String.Format("{0}: TEST FAILED!", title));
        }
    }

    public class Customer
    {
        private AuthorizationService authorizationService;
        private IRepository repository;

        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public AccessGroup AccessGroup { get; set; }

        public string GetAuthorizationMessage()
        {
            return authorizationService.GetAccessMessage(this);
        }

        public Customer(int id, AuthorizationService authorizationService, IRepository repository)
        {
            this.authorizationService = authorizationService;
            this.repository = repository;

            CustomerDB customerDB = repository.GetCustomerDB(id);
            this.ID = customerDB.ID;
            this.FirstName = customerDB.FirstName;
            this.LastName = customerDB.LastName;
            this.AccessGroup = customerDB.AccessGroup;
        }

        public void Display()
        {
            Console.WriteLine("Customer: {1}, {0} ({2}): {3}", this.FirstName, this.LastName, this.AccessGroup, this.GetAuthorizationMessage());
        }
    }

    public class AuthorizationService
    {
        public string GetAccessMessage(Customer customer)
        {
            return customer.AccessGroup == AccessGroup.Administrator ? "Access Granted" : "Access Denied";
        }
    }


    public class Repository : IRepository
    {
        private List<CustomerDB> _customerDBSet = new List<CustomerDB>();

        public Repository()
        {
            _customerDBSet.Add(new CustomerDB { ID = 1, FirstName = "Jim", LastName = "Smith", AccessGroup = AccessGroup.Administrator });
            _customerDBSet.Add(new CustomerDB { ID = 2, FirstName = "John", LastName = "Johnson", AccessGroup = AccessGroup.Administrator });
            _customerDBSet.Add(new CustomerDB { ID = 3, FirstName = "Hank", LastName = "Rivers", AccessGroup = AccessGroup.Salesperson });
        }

        public CustomerDB GetCustomerDB(int id)
        {
            CustomerDB customerDBchosen = null;
            //this should be done with LINQ (couldn't get it CustomerDB to implement IEnumerable correctly)
            foreach (CustomerDB customerDB in _customerDBSet)
            {
                if (customerDB.ID == id)
                {
                    customerDBchosen = customerDB;
                    break;
                }
            }
            return customerDBchosen;
        }
    }

    public class MockRepository : IRepository
    {
        public CustomerDB GetCustomerDB(int id)
        {
            switch (id)
            {
                case 1:
                    return new CustomerDB { ID = 1, FirstName = "Test1AdministratorFirstName", LastName = "Test1AdministratorLastName", AccessGroup = AccessGroup.Administrator };
                case 2:
                    return new CustomerDB { ID = 2, FirstName = "Test2SalespersonFirstName", LastName = "Test2SalesPersonLastName", AccessGroup = AccessGroup.Salesperson };
                default:
                    return null;
            }
        }
    }

    public interface IRepository
    {
        CustomerDB GetCustomerDB(int id);
    }

    public class CustomerDB
    {
        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public AccessGroup AccessGroup { get; set; }
    }

    public enum AccessGroup
    {
        Administrator,
        Salesperson
    }
}
+5  A: 

Your code violates the single responsibility principal in a pretty serious way.

http://en.wikipedia.org/wiki/Single_responsibility_principle

Your Customer object shouldn't be loading itself from a repository, the repository should be loading the Customer object and handing it back to the caller.

I would expect something more like:

Customer customer = repository.GetCustomer(3);
jonnii
easy enough, I refactored the code along those lines, right, that is simpler for this test (got rid of CustomerDB). What I'm really trying to get a sense of though of how to get this code to the next level of IoC by using a central "container" which it doesn't yet use.
Edward Tanguay
+1  A: 

usage of Switch statements in most cases violate SRP. Do check this out for ways to eliminate switch statements in your code. The challenge & beauty of writing loosely coupled code is to eliminate if, if else & switch statements.

AB Kolan
you're right, got rid of those
Edward Tanguay
Seeing as how he is just using the switch statement as a mock implementation of the repository I wouldn't bother about it at all.
Caleb Vear