Wzorce projektowe (w ASP.NET i nie tylko)

Post on 03-Dec-2014

1.626 views 6 download

description

Omówienie podstawowych wzorców projektowych oraz zasad architektonicznych na przykładzie aplikacji ASP.NET, ale w większości niezależnych od stosowanej technologii. Najważniejsze założenia domain-driven design, SOLID principles, itp.

Transcript of Wzorce projektowe (w ASP.NET i nie tylko)

Wzorce projektowe w ASP.NETBartłomiej ZassMicrosoft

Keep It Simple Stupid (KISS) Don’t Repeat Yourself (DRY) Tell, Don’t Ask (reguła Hollywood)

Wydajemy polecenie obiektom, zamiast pytać o ich stan i wykonywać akcję

You Ain’t Gonna Need It (YAGNI) – tylko to, co niezbędne

Separation of Concerns (SoC)

Główne zasady

Single Responsibility Principle (SRP) Nie monolityczna konstrukcja (scyzoryk), 1 powód do zmiany stanu

Open-Closed Principle (OCP) Klasy powinny być otwarte na rozszerzenia, ale zamknięte na zmiany

(brak problemów podczas modyfikacji klas bazowych) Liskov Substitution Principle (LSP)

Możliwość podmienienia każdej dziedziczącej klasy z klasą bazową, bez wpływu na działanie

Interface Segregation Principle (ISP) Grupowanie interfejsu na mniejsze

Dependency Inversion Principle (DIP) Zależność od abstrakcji, nie konkretnej implementacji Inversion of Control (IoC) – kontener / framework wstrzykujący konkretną implementację

(odwrócenie kontroli) Dependency Injection (DI) – wstrzykiwanie implementacji przez konstruktor, metodę lub

właściwość

Zasady SOLID

Test-driven Development (TDD) Zaczynamy od pisania testu nieistniejącej jeszcze

funkcjonalności Red-Green (od narzędzi)

Domain-driven Design (DDD) Wierne modelowanie logiki biznesowej w kodzie Ten sam język, używany przez biznes

Behavior-driven Design (BDD) Evolucja TDD połączona z DDD Dokumentacja, specyfikacje, testy sprawdzające zgodność

z założeniami

Inne praktyki

Typowa architektura aplikacji

Model Obiekty domenowe, logika, relacje Interfejsy potrzebne do zapisu (Repository) Brak referencji do infrastruktury pod spodem

Repository Referencja do modeli Implementacja interfejsów repozytorium z projektu modelu

Application Service Gateway do aplikacji Komunikacja z frontendem przez DTO ViewModele

Web Komunikacja wyłącznie z Application Service W odpowiedzi ViewModele

Podział na projekty

Organizacja warstwy logiki biznesowej

Proste, proceduralne podejście Np. statyczna klasa

Proste, dobre dla małych aplikacji Mniej wymagające dla programistów

Trudne w utrzymaniu, kiedy aplikacja się rozrasta

Transaction Script

Efektywne, kiedy struktura bazy odzwierciedla model biznesowy Np. blog

Każdy obiekt odpowiada za zapis swojego stanu Narzędzia do generacji kodu z bazy Najpopularniejsze

Ruby on Rails Castle ActiveRecord (.NET)

Problemy, kiedy model model biznesowy „rozjeżdża” się z bazą Tzw. Impedance Mismatch

Active Record

Castle Activerecord - przykład

            Post post = Post.Find(postId);

            Comment comment = new Comment();            comment.Post = post;            comment.Author = Request.Form["Author"];            comment.DateAdded = DateTime.Now;            comment.Text = Request.Form["Comment"];

            comment.Save();

    [ActiveRecord("Comments")]    public class Comment : ActiveRecordBase<Comment>     {               [PrimaryKey]        public int Id { get; set; }

        [BelongsTo("PostID")]        public Post Post { get; set; }

        [Property]        public string Text { get; set; }

        [Property]        public string Author { get; set; }

        [Property]        public DateTime DateAdded { get; set; }    }

Całkowite odzwierciedlenie modelu biznesowego w obiektach Nie koniecznie 1:1 z bazą Nie tylko pojemniki na dane Także logika – np. walidacja, relacje między obiektami

Obiekty nie wiedzą jak się zapisać POCO (Plan Old CLR Object) i PI (Persistence Ignorance)

Domain Service Kiedy akcja nie może być wewnątrz samego obiektu Np. akcje transferu między dwoma kontami (korzysta z IRepository –

same encje nie powinny korzystać bezpośrednio z IRepository) Proste do unit testów!

Domain Model

ViewMapper – generuje ViewModele na podstawie modelu

Messaging Klasa bazowa + odpowiednie Request i Response

Application Service (frontend) Koordynuje wszystkie aktywności aplikacji Deleguje zadania do modelu domenowego Może korzystać z repozytorium, kiedy trzeba (np. nowe

konto) Odbiera wiadomości – wykonuje akcje – odpowiada

wiadomością Transformuje encje modelu na DTO dla warstwy

prezentacji

Domain Model – c.d.

Antywzorzec Podobne do Domain Model Wszelkie akcje nie wewnątrz obiektów, ale poza

modelem Tak naprawdę DTO

Domain Services w stylu Transaction Script Naruszenie zasady „Tell, don’t ask”

Anemic Domain Model

Metodyka bazująca na wzorcu Domain Model Wspólny język deweloperów, ekspertów dziedzinowych

Usprawnia komunikację między wykonawcami i biznesem Ułatwia deweloperom zrozumienie reguł biznesowych

Encje Unikalne, mają identyfikator (np. pesel lub inkrementowany int)

Value Objects Nie mają ID Nie żyją same z siebie (np. Transaction – żyje tylko w powiązaniu z Bank Account)

Aggregate Logicznie pogrupowane obiekty pod względem celu modyfikacji danych

Agreggate Root Jedyne encje, do których jest dostęp z zewnątrz agregatu Np. dla ecommerce – Order (bo OrderLine czy aplikacja Voucheru zawsze przez Order –

walidacja, itp..)

Domain Driven Design

Domain Services Metody nie pasujące do encji lub wymagające dostępu do repozytorium Mogą także zawierać logikę biznesową – jest to część modelu domenowego

Application Services Cienka warstwa pomiędzy modelem i aplikacją Brak logiki biznesowej Nie przechowuje stanu encji Może przechowywać stan workflowu

Repository Odpowiada za zapis danych

Podział na warstwy

DDD – c.d.

Wzorce warstwy logiki biznesowej

Creational pattern (GoF) Ukrywanie skomplikowanego tworzenia obiektów Z reguły klient nie prosi o konkretną klasę

Oczekujemy klasy abstrakcyjnej lub interfejsu Factory decyduje jaka konkretna implementacja ma być

zwrócona

Factory

Factory – c.d. np. Factory zwracające obiekt generujący etykiety

adresowe

Nowe cechy dodawane przez kompozycję Unikamy tworzenia ogromnej liczby dziedziczących klas Np. dodanie zniżki lub przelicznika waluty, logowanie,

strumienie

Decorator

Struktura metody zdefiniowana Cześć metod do zdefiniowania przez dziedziczące

klasy

Template method

Zmiana zachowania przy zmianie stanu wewnętrznego Podmiana wewnętrznych obiektów odpowiadających za

zachowania

State

Pozwala wybierać algorytm w czasie wykonywania

Strategy

Nic nie robi Nie chcemy nie wyrzucać wyjątku, ale zgodne z

interfejsem Np. brak strategii

Null Object

    public class NoBasketDiscount : IBasketDiscountStrategy     {        public decimal GetTotalCostAfterApplyingDiscountTo(Basket basket)        {            return basket.TotalCost;         }         }

Pozwala kolekcje traktować tak jak pojedynczą instancję

Kompozyt

Kompozyt - przykład

        //Component        public interface IGraphic        {            void Print();        }        //Leaf        public class Ellipse : IGraphic        {            //Prints the graphic            public void Print()            {                Console.WriteLine("Ellipse");            }        }

       public class CompositeGraphic : IGraphic        {            //Collection of Graphics.            private readonly List<IGraphic> graphics;

            //Constructor             public CompositeGraphic()            {                //initialize generic Colleciton(Composition)                graphics = new List<IGraphic>();            }            //Adds the graphic to the composition            public void Add(IGraphic graphic)            {                graphics.Add(graphic);            }            //Adds multiple graphics to the composition            public void AddRange(params IGraphic[] graphic)            {                graphics.AddRange(graphic);            }            //Removes the graphic from the composition            public void Delete(IGraphic graphic)            {                graphics.Remove(graphic);            }            //Prints the graphic.            public void Print()            {                foreach (var childGraphic in graphics)                {                    childGraphic.Print();                }            }        }

Specyfikacja – zapisanie prostej reguły (IsSatisfiedBy()) + np. kompozyt – zagregowanie wielu mniejszych

specyfikacji

Kompozyt i specyfikacja

Iterator HasNext, GetNext

Visitor Kiedy potrzebujemy wykonać akcję na danych encjach

skomplikowanej struktury (np. agent, który chodzi po drzewie)

Visitor, Iterator

W C# w zasadzie gotowe – eventy, delegaty Lubi powodować wycieki pamięci

Strong reference Weak Reference

Observer

Lock, podwójna weryfikacja, static, IoC BeforeFieldInit – inicjalizacja bardziej lazy (na początku

metody, która może jej potrzebować) W praktyce – na poniższym przykładzie zawsze Bez BeforeFieldInit – może być zainicjalizowana przed użyciem Sposób: statyczny konstruktor

Singleton

        public static void DoSomething(bool which)        {            if (which)            {                FirstType.Foo();            }            else            {                SecondType.Bar();            }        }

Brak inversion of control

Dependency inversion

Interface Inversion – źle!

Interface Inversion - lepiej

Skąd przychodzą zależności?

Konstruktor

Skąd przychodzą zależności?

Wstrzykiwanie zależności przez kontener IoC StructureMap, Castle Windsor, Spring.NET, Ninject,

PicoContainer.NET, Unity, MEF, … Właściwość, konstruktor

Service Locator Bardziej luźno powiązane Factory*

Dependency Injection

OrderService orderService = serviceLocator.Locate<OrderService>(“OrderServiceWithFedExCourier”);

Kluczowe funkcjonalności te same Różne platformy wspierane Różnice przy bardziej zaawansowanych scenariuszach

Przykład - rozszerzenia – np. nasz kod w trakcie wstrzykiwania, itp. Porównanie wydajności

Najszybszy – Simple Injector

DI – który wybrać?

Layer Super Type Unikanie duplikacji Np. EntityBase<T>, walidacja

Interface Segregation Dzielimy duży interfejs na kilka mniejszych,

pogrupowanych logiczniezamiast jednego dużego i NotImplementedException

Liskov Substitution Principle Działanie klasy dziedziczącej powiąż z założeniami

kontraktu klasy bazowej

Inne wzorce enterprise

Warstwa usług

Wyraźne granice Interfejs jak najbardziej czytelny jak możliwe, ujednolicone

typy komunikatów Usługi są autonomiczne

Metody bezstanowe, zmiany atomic, nie konieczna określona kolejność wywołań

Współdzielenie kontraktu i schema, nie klas Kompatybilność określają polityki (np. WS-Policy)

Najważniejsze zasady SOA

Fasada - uproszczenie skomplikowanego API Adapter – przystosowanie interfejsu jednej klasy do

drugiej

Fasada i adapter

RequestMessage, ResponseMessage, … Extension methods do konwersji modelu na

komunikat response

Document Message i Request-Response

Długotrwały proces, wymagający przechowywania stanu i wielu komunikatów

Unikalny identyfikator po pierwszym wywołaniu Przekazywany przy kolejnych Czas ważności

Wzorzec Reservation

Operacja indempotentna to taka, która może być wywołana wielokrotnie z tymi samymi parametrami wejściowymi (nie wywołując błędu)

Wszystkie żądania modyfikujące stan powinny zawierać unikalny identyfikator Identyfikator sprawdzany przed przetworzeniem komunikatu Jeśli już był przetworzony – zwracana odpowiedź z response store dla danego ID Identyfikator może być zwrócony w odpowiedzi (tzw. correlation ID)

Wzorzec Indempotent

Warstwa dostępu do danych

Reprezentuje kolekcję obiektów Mogą mieć zależności z innymi repozytoriami Całkowicie wyizolowane od modelu domenowego

Interfejs separujący od konkretnej implementacji IQueryable nie zalecane

Osobne dla każdego Aggregate Root

Repository

        public interface IRepository<T>        {            IEnumerable<T> FindAll();            IEnumerable<T> FindAll(int index, int count);            IEnumerable<T> FindBy(Query query);            IEnumerable<T> FindBy(Query query, int index, int count);            T FindBy(Guid Id);            void Add(T entity);            void Save(T entity);            void Remove(T entity);        }

Podobna koncepcja do Repository, ale na niższym poziomie Nie ukrywa, że reprezentuje tabelę z bazy

Z reguły jedno DAO dla jednej tabeli Wykorzystywane często przy Active Record i Transaction

Script

Data Access Objects

        public interface IProductDAO        {            Product Get(int id);            IEnumerable<Product> FindByCategory(int id);            IEnumerable<Product> FindByBrand(int id);            IEnumerable<Product> FindByTopSelling(int count);            void Add(Product product);            void Save(Product product);            void Remove(Product product);        }

Rejestruje zmiany w obiektach biznesowych Dodanie, usunięcie, zmiana

Koordynuje zapis zmian w bazie Transakcja, konflikty, itp.

Zapewnia integralność danych

RegisterAmended – przekazywana encjai repozytorium

Commit wywołuje odpowiednie metody(np. PersistUpdateOf(acc)) na odpowiednich repozyzoriach Opakowane w transakcję

Unit of Work

IAggregateRoot – pusty interfejs Wzorzec Marker Aplikowane do encji będących AggregateRoot

IUnitOfWorkRepository

IUnitOfWork Wstrzykiwane do repozytorium – wiele repozytoriów może uczestniczyć w transakcji

Główne składniki Unit Of Work

        public interface IUnitOfWorkRepository        {            void PersistCreationOf(IAggregateRoot entity);            void PersistUpdateOf(IAggregateRoot entity);            void PersistDeletionOf(IAggregateRoot entity);        }

        public interface IUnitOfWork        {            void RegisterAmended(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);            void RegisterNew(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);            void RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);            void Commit();        }

IDbSet – implementacja Repository Mimo wszystko chcemy większej abstrakcji

DbContext – w zasadzie implementacja UoW Przydaje się do współdzielenia kontekstu między

repozytoriami

UoW i Entity Framework

Lazy Loading Natywnie w .NET – IQueryable

Proxy Maskowanie długotrwałego wywoływania operacji Cache, bezpieczeństwo

synchronizacja, …

Proxy

Upewnia się, że każdy obiekt został załadowany tylko raz Dostarcza tą samą wersję obiektów biznesowych

transakcji Przy dwukrotnym odwoływaniu się do obiektu, ta sama

instancja jest zwracana Z reguły stosowane w ramach jednej transakcji

Identity Map

Reprezentuje zapytanie w języku domenowym Unikamy pisania kodu dla każdego zestawu parametrów, np.:

Pozwala odseparować zapytanie od bazy danych QueryTranslator

Tłumaczy zapytanie na język bazy Analogiczne do providerów LINQ Lepiej działa z procedurami składowanymi

Query Object

        public interface ICustomerRepository        {            IEnumerable<Customer> FindAll();            IEnumerable<Customer> FindAllVIPCustomers();            IEnumerable<Customer> FindByOrder(Guid ID);            IEnumerable<Customer> FindAllCustomersThatHaveOutstandingOrders();        }

Warstwa prezentacji

Najpopularniejszy wzorzec dla Web Forms / Windows Forms Samodzielnie lub ew. frameworki (np. http://webformsmvp.com)

Model Model biznesowy, modyfikowany lub wyświetlany przez widok

View Wyświetla dane modelu pobierane przez Presenter Deleguje akcje użytkownika do Presentera Interfejs przekazywany do presentera (część private get / set)

Presenter Wywoływany przez widok, w celu reakcji na akcje użytkownika Może korzystać ze wstrzykiwanych zależności przez IoC Daje się testować, ma wyodrębniony interfejs, brak zależności z httpcontext

Model-View-Presenter (MVP)

MVP – najpopularniejsze odmiany

MVP Passive View

MVP – diagram sekwencji

Hermetyzacja wykonania akcji Execute, CanExecute

Np. cofnij/ponów, makra, itp.

Command

Kontroler pierwszy ma kontrolę, wykrywa który widok wyświetlić Front Controller Pasywny model, aktywny model Page Controller – każda strona ma swój kontroler (code behind w ASP.NET)

Generalnie najlepiej ASP.NET MVC, ale da się samemu (HttpHandlery) Łatwiejszy w utrzymaniu, ale więcej pracy

Model-View-Controller (MVC) i Front Controller

Command i Front Controller

Chain of Responsibility Łańcuch obiektów typu opakowujących akcję Po kolei – wykonanie akcji lub przekazanie dalej

np. filtry poczty, routing

Specjalna klasa przesyłana przez AppService do widoku (DTO)

Agregacja lub podzbiór danych modelu, potrzebne do wyświetlenia widoku

Mapowanie ręczne lub frameworki AutoMapper (http://automapper.codeplex.com)

ViewModel

Mapper.CreateMap<Order, OrderDto>();OrderDto dto = Mapper.Map<Order, OrderDto>(order);

© 2012 Microsoft Corporation. Wszelkie prawa zastrzeżone. Microsoft, Windows oraz inne nazwy produktów są lub mogą być znakami towarowymi lub zastrzeżonymi znakami towarowymi firmy Microsoft w Stanach Zjednoczonych i innych krajach. Zamieszczone informacje mają charakter wyłącznie informacyjny. FIRMA MICROSOFT NIE UDZIELA ŻADNYCH GWARANCJI (WYRAŻONYCH WPROST LUB DOMYŚLNIE), W TYM TAKŻE USTAWOWEJ RĘKOJMI ZA WADY FIZYCZNE I PRAWNE, CO DO INFORMACJI ZAWARTYCH W TEJ PREZENTACJI.