sobota, 22 października 2011

Prism czyli DI

DI czyli Dependency Injection (wstrzykiwanie zależności) to technika usuwająca zależności pomiędzy komponentami. Definicję można znaleźć np. w wikipedi czy też w wielu innych źródłach, jednak dla osób wolących zobaczyć w przykładzie o co chodzi pokażę na przykładzie w jaki sposób można skorzystać z dobrodziejstwa DI.

W celu zaprezentowania działania DI stworzymy prostą aplikację konsolową. Będzie to prosta klasa realizująca wzorzec Repository:

Code:
    public class Person 
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int BirthYear { get; set; }

        public override string ToString()
        {
            return string.Format("{0} {1} {2}", FirstName, LastName, BirthYear);
        }
    }


Code:
    public interface IRepository<T>
    {
        void Add(T entity);
        void Remove(T entity);
        IEnumerable<T> GetElements();
    }


Code:
    public interface IPersonRepository : IRepository<Person>
    {

    }


Code:
    public class PersonRepository : IPersonRepository
    {
        private List<Person> lPersonList = new List<Person>(); 

        public void Add(Person entity)
        {
            lPersonList.Add(entity);
        }

        public void Remove(Person entity)
        {
            lPersonList.Remove(entity);
        }

        public IEnumerable<Person> GetElements()
        {
            return lPersonList;
        }
    }

Funkcja main będzie zawierać następujący kod:

Code:
        static void Main(string[] args)
        {
            var unityContainer = new UnityContainer();
            unityContainer.RegisterType<IPersonRepository, PersonRepository>();

            var personRepository = unityContainer.Resolve<IPersonRepository>();
            personRepository.Add(new Person {FirstName = "Jacek", BirthYear = 2000, LastName = "Kowalski"});
            foreach (var person in personRepository.GetElements())
            {
                Console.WriteLine(person);
            }
        }

Na początku tworzymy nasz kontener. Następnie  następuje rejestracja interfejsu wraz z implementacją. Zabieg ten mówi: jeżeli użytkownik zażąda zwrócenia obiektu implementującego IPersonRepository - użytkownik otrzyma konkretną implementację w postaci obiektu klasy PersonRepository.
Następnie następuje pobranie obiektu na podstawie interfejsu - Resolve. Użytkownikowi zostaje zwrócony obiekt klasy PersonRepository na którym operuje jak na zwykłym obiekcie.

Przykład przedstawiony powyżej nie jest wymyślnym przykładem i nie obrazuje możliwości jakie daje DI w bardziej złożonym projekcie. Dlatego spróbujemy trochę bardziej skomplikować przykład:
Do naszego repozytorium dodamy interfejs odpowiedzialny za obliczanie całkowitego wynagrodzenia na podstawie wymyślonego wzoru:


Code:
    public interface IEducationalYears
    {
        int EducationYears(Person persons);
    }


Code:
    public class EducationalYears : IEducationalYears
    {
        public int EducationYears(Person person)
        {
            int sum = 0;
            int age = DateTime.Now.Year - person.BirthYear;
            sum += age > 18 ? 11 : age - 7;

            return sum;
        }
    }


Code:
    public interface IPersonRepository : IRepository<Person>
    {
        decimal CalculateTotalWage();
    }


Code:
public class PersonRepository : IPersonRepository
    {
        private List<Person> lPersonList = new List<Person>();
        private IEducationalYears _educationalYears;

        public PersonRepository(IEducationalYears educationalYears)
        {
            _educationalYears = educationalYears;
        }

        public void Add(Person entity)
        {
            lPersonList.Add(entity);
        }

        public void Remove(Person entity)
        {
            lPersonList.Remove(entity);
        }

        public IEnumerable<Person> GetElements()
        {
            return lPersonList;
        }

        public decimal CalculateTotalWage()
        {
            decimal sum = decimal.Zero;
            foreach (var person in lPersonList)
            {
                sum += _educationalYears.EducationYears(person) * 125;
            }

            return sum;
        }
    }


Code:
        static void Main(string[] args)
        {
            var unityContainer = new UnityContainer();
            unityContainer.RegisterType<IPersonRepository, PersonRepository>();
            unityContainer.RegisterType<IEducationalYears, EducationalYears>();

            var personRepository = unityContainer.Resolve<IPersonRepository>();
            personRepository.Add(new Person {FirstName = "Jacek", BirthYear = 2000, LastName = "Kowalski"});
            foreach (var person in personRepository.GetElements())
            {
                Console.WriteLine(person);
            }
            Console.WriteLine("Totale wage: {0}", personRepository.CalculateTotalWage());
        }

Jak widać w funkcji main dodaliśmy tylko linijki odpowiedzialne za wyświetlenie wartości wynagrodzenia oraz zarejestrowaliśmy nowy typ w kontenerze.
Dzięki temu nie musimy w konstruktorze podawać konkretnego obiektu. Zaleta ta daje nam niezależność w przypadku rozszerzania funkcjonalności klas. Wystarczy że w konstruktorze dodamy odpowiednie obiekty, a w kontenerze zarejestrujemy nowe typy.

http://pl.wikipedia.org/wiki/Wstrzykiwanie_zale%C5%BCno%C5%9Bci

Brak komentarzy:

Prześlij komentarz