Iteracja 4 — Luźne sprzężenie aplikacji (VB)
autor: Microsoft
W tej czwartej iteracji korzystamy z kilku wzorców projektowych oprogramowania, aby ułatwić konserwację i modyfikowanie aplikacji Contact Manager. Na przykład refaktoryzujemy naszą aplikację, aby używać wzorca repozytorium i wzorca wstrzykiwania zależności.
Tworzenie aplikacji MVC do zarządzania kontaktami ASP.NET (VB)
W tej serii samouczków utworzymy całą aplikację do zarządzania kontaktami od początku do końca. Aplikacja Contact Manager umożliwia przechowywanie informacji kontaktowych — nazw, numerów telefonów i adresów e-mail — dla listy osób.
Tworzymy aplikację za pośrednictwem wielu iteracji. Wraz z każdą iteracją stopniowo ulepszamy aplikację. Celem tego podejścia iteracji wielokrotnej jest umożliwienie zrozumienia przyczyny każdej zmiany.
Iteracja #1 — tworzenie aplikacji. W pierwszej iteracji tworzymy Menedżera kontaktów w najprostszy możliwy sposób. Dodajemy obsługę podstawowych operacji bazy danych: tworzenie, odczyt, aktualizowanie i usuwanie (CRUD).
Iteracja #2 — sprawia, że aplikacja wygląda ładnie. W tej iteracji poprawiamy wygląd aplikacji, modyfikując domyślną stronę wzorcową widoku MVC ASP.NET oraz kaskadowy arkusz stylów.
Iteracja 3 — dodawanie walidacji formularza. W trzeciej iteracji dodamy podstawową walidację formularza. Uniemożliwiamy użytkownikom przesyłanie formularza bez wypełniania wymaganych pól formularza. Weryfikujemy również adresy e-mail i numery telefonów.
Iteracja #4 — luźno sprzężenie aplikacji. W tej czwartej iteracji korzystamy z kilku wzorców projektowych oprogramowania, aby ułatwić konserwację i modyfikowanie aplikacji Contact Manager. Na przykład refaktoryzujemy naszą aplikację, aby używać wzorca repozytorium i wzorca wstrzykiwania zależności.
Iteracja 5 — tworzenie testów jednostkowych. W piątej iteracji ułatwiamy konserwację i modyfikowanie aplikacji przez dodawanie testów jednostkowych. Wyśmiewamy nasze klasy modelu danych i tworzymy testy jednostkowe dla naszych kontrolerów i logiki walidacji.
Iteracja 6 — używanie programowania opartego na testach. W tej szóstej iteracji do naszej aplikacji dodamy nowe funkcje, pisząc najpierw testy jednostkowe i pisząc kod względem testów jednostkowych. W tej iteracji dodajemy grupy kontaktów.
Iteracja #7 — dodawanie funkcji Ajax. W siódmej iteracji poprawiamy czas odpowiedzi i wydajność naszej aplikacji, dodając obsługę Ajax.
Ta iteracja
W tej czwartej iteracji aplikacji Contact Manager refaktoryzujemy aplikację, aby aplikacja była luźniej sprzężona. Gdy aplikacja jest luźno połączona, można zmodyfikować kod w jednej części aplikacji bez konieczności modyfikowania kodu w innych częściach aplikacji. Luźno powiązane aplikacje są bardziej odporne na zmiany.
Obecnie wszystkie logiki dostępu do danych i walidacji używane przez aplikację Contact Manager są zawarte w klasach kontrolerów. To jest zły pomysł. Za każdym razem, gdy musisz zmodyfikować jedną część aplikacji, ryzykujesz wprowadzenie usterek do innej części aplikacji. Jeśli na przykład zmodyfikujesz logikę walidacji, ryzyko wprowadzenia nowych usterek do logiki dostępu do danych lub kontrolera.
Uwaga
(SRP), klasa nigdy nie powinna mieć więcej niż jednego powodu do zmiany. Mieszanie logiki kontrolera, walidacji i bazy danych jest ogromnym naruszeniem zasady o pojedynczej odpowiedzialności.
Istnieje kilka powodów, dla których może być konieczne zmodyfikowanie aplikacji. Może być konieczne dodanie nowej funkcji do aplikacji, może być konieczne naprawienie usterki w aplikacji lub może być konieczne zmodyfikowanie sposobu implementacji funkcji aplikacji. Aplikacje są rzadko statyczne. Mają tendencję do wzrostu imutacji w czasie.
Załóżmy na przykład, że decydujesz się zmienić sposób implementowania warstwy dostępu do danych. W tej chwili aplikacja Contact Manager używa programu Microsoft Entity Framework do uzyskiwania dostępu do bazy danych. Możesz jednak zdecydować się na migrację do nowej lub alternatywnej technologii dostępu do danych, takiej jak ADO.NET Data Services lub NHibernate. Jednak ponieważ kod dostępu do danych nie jest odizolowany od kodu walidacji i kontrolera, nie ma możliwości modyfikowania kodu dostępu do danych w aplikacji bez modyfikowania innego kodu, który nie jest bezpośrednio związany z dostępem do danych.
Gdy aplikacja jest luźno połączona, z drugiej strony możesz wprowadzić zmiany w jednej części aplikacji bez dotykania innych części aplikacji. Na przykład można przełączać technologie dostępu do danych bez modyfikowania logiki walidacji lub kontrolera.
W tej iteracji korzystamy z kilku wzorców projektowych oprogramowania, które umożliwiają refaktoryzację aplikacji Contact Manager do bardziej luźno powiązanej aplikacji. Gdy skończymy, Menedżer kontaktów nie zrobi nic, czego wcześniej nie zrobił. Jednak będziemy mogli łatwiej zmienić aplikację w przyszłości.
Uwaga
Refaktoryzacja to proces ponownego zapisywania aplikacji w taki sposób, że nie traci żadnych istniejących funkcji.
Korzystanie ze wzorca projektowania oprogramowania repozytorium
Pierwszą zmianą jest skorzystanie ze wzorca projektowego oprogramowania nazywanego wzorcem repozytorium. Użyjemy wzorca repozytorium, aby odizolować kod dostępu do danych od pozostałej części naszej aplikacji.
Zaimplementowanie wzorca repozytorium wymaga wykonania następujących dwóch kroków:
- Tworzenie interfejsu
- Tworzenie konkretnej klasy implementujące interfejs
Najpierw musimy utworzyć interfejs opisujący wszystkie metody dostępu do danych, które należy wykonać. Interfejs IContactManagerRepository znajduje się na liście 1. W tym interfejsie opisano pięć metod: CreateContact(), DeleteContact(), EditContact(), GetContact i ListContacts().
Lista 1 — Models\IContactManagerRepository.vb
Public Interface IContactManagerRepository
Function CreateContact(ByVal contactToCreate As Contact) As Contact
Sub DeleteContact(ByVal contactToDelete As Contact)
Function EditContact(ByVal contactToUpdate As Contact) As Contact
Function GetContact(ByVal id As Integer) As Contact
Function ListContacts() As IEnumerable(Of Contact)
End Interface
Następnie musimy utworzyć konkretną klasę, która implementuje interfejs IContactManagerRepository. Ponieważ do uzyskiwania dostępu do bazy danych używamy programu Microsoft Entity Framework, utworzymy nową klasę o nazwie EntityContactManagerRepository. Ta klasa jest zawarta w liście 2.
Lista 2 — Models\EntityContactManagerRepository.vb
Public Class EntityContactManagerRepository
Implements IContactManagerRepository
Private _entities As New ContactManagerDBEntities()
Public Function GetContact(ByVal id As Integer) As Contact Implements IContactManagerRepository.GetContact
Return (From c In _entities.ContactSet _
Where c.Id = id _
Select c).FirstOrDefault()
End Function
Public Function ListContacts() As IEnumerable(Of Contact) Implements IContactManagerRepository.ListContacts
Return _entities.ContactSet.ToList()
End Function
Public Function CreateContact(ByVal contactToCreate As Contact) As Contact Implements IContactManagerRepository.CreateContact
_entities.AddToContactSet(contactToCreate)
_entities.SaveChanges()
Return contactToCreate
End Function
Public Function EditContact(ByVal contactToEdit As Contact) As Contact Implements IContactManagerRepository.EditContact
Dim originalContact = GetContact(contactToEdit.Id)
_entities.ApplyPropertyChanges(originalContact.EntityKey.EntitySetName, contactToEdit)
_entities.SaveChanges()
Return contactToEdit
End Function
Public Sub DeleteContact(ByVal contactToDelete As Contact) Implements IContactManagerRepository.DeleteContact
Dim originalContact = GetContact(contactToDelete.Id)
_entities.DeleteObject(originalContact)
_entities.SaveChanges()
End Sub
End Class
Zwróć uwagę, że klasa EntityContactManagerRepository implementuje interfejs IContactManagerRepository. Klasa implementuje wszystkie pięć metod opisanych przez ten interfejs.
Możesz się zastanawiać, dlaczego musimy przeszkadzać za pomocą interfejsu. Dlaczego musimy utworzyć zarówno interfejs, jak i klasę, która ją implementuje?
Z jednym wyjątkiem pozostała część naszej aplikacji będzie współdziałać z interfejsem, a nie konkretną klasą. Zamiast wywoływać metody uwidocznione przez klasę EntityContactManagerRepository, wywołamy metody uwidocznione przez interfejs IContactManagerRepository.
Dzięki temu możemy zaimplementować interfejs z nową klasą bez konieczności modyfikowania pozostałej części aplikacji. Na przykład w przyszłości możemy zaimplementować klasę DataServicesContactManagerRepository, która implementuje interfejs IContactManagerRepository. Klasa DataServicesContactManagerRepository może używać ADO.NET Data Services w celu uzyskania dostępu do bazy danych zamiast programu Microsoft Entity Framework.
Jeśli nasz kod aplikacji jest zaprogramowany względem interfejsu IContactManagerRepository zamiast konkretnej klasy EntityContactManagerRepository, możemy przełączyć konkretne klasy bez modyfikowania pozostałej części kodu. Na przykład możemy przełączyć się z klasy EntityContactManagerRepository na klasę DataServicesContactManagerRepository bez modyfikowania logiki dostępu do danych lub walidacji.
Programowanie względem interfejsów (abstrakcji) zamiast konkretnych klas sprawia, że nasza aplikacja jest bardziej odporna na zmiany.
Uwaga
Interfejs można szybko utworzyć na podstawie konkretnej klasy w programie Visual Studio, wybierając opcję menu Refaktoryzacja, Wyodrębnij interfejs. Na przykład można najpierw utworzyć klasę EntityContactManagerRepository, a następnie użyć interfejsu Extract, aby automatycznie wygenerować interfejs IContactManagerRepository.
Korzystanie ze wzorca projektowego oprogramowania wstrzykiwania zależności
Teraz, gdy przeprowadziliśmy migrację kodu dostępu do danych do oddzielnej klasy repozytorium, musimy zmodyfikować kontroler kontaktów, aby używał tej klasy. Skorzystamy ze wzorca projektowego oprogramowania o nazwie Wstrzykiwanie zależności, aby użyć klasy Repository w naszym kontrolerze.
Zmodyfikowany kontroler kontaktów znajduje się na liście 3.
Lista 3 — Controllers\ContactController.vb
Public Class ContactController
Inherits System.Web.Mvc.Controller
Private _repository As IContactManagerRepository
Sub New()
Me.New(new EntityContactManagerRepository())
End Sub
Sub New(repository As IContactManagerRepository)
_repository = repository
End Sub
Protected Sub ValidateContact(contactToValidate As Contact)
If contactToValidate.FirstName.Trim().Length = 0 Then
ModelState.AddModelError("FirstName", "First name is required.")
End If
If contactToValidate.LastName.Trim().Length = 0 Then
ModelState.AddModelError("LastName", "Last name is required.")
End If
If (contactToValidate.Phone.Length > 0 AndAlso Not Regex.IsMatch(contactToValidate.Phone, "((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
ModelState.AddModelError("Phone", "Invalid phone number.")
End If
If (contactToValidate.Email.Length > 0 AndAlso Not Regex.IsMatch(contactToValidate.Email, "^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
ModelState.AddModelError("Email", "Invalid email address.")
End If
End Sub
Function Index() As ActionResult
Return View(_repository.ListContacts())
End Function
Function Create() As ActionResult
Return View()
End Function
<AcceptVerbs(HttpVerbs.Post)> _
Function Create(<Bind(Exclude:="Id")> ByVal contactToCreate As Contact) As ActionResult
' Validation logic
ValidateContact(contactToCreate)
If Not ModelState.IsValid Then
Return View()
End If
' Database logic
Try
_repository.CreateContact(contactToCreate)
Return RedirectToAction("Index")
Catch
Return View()
End Try
End Function
Function Edit(ByVal id As Integer) As ActionResult
Return View(_repository.GetContact(id))
End Function
<AcceptVerbs(HttpVerbs.Post)> _
Function Edit(ByVal contactToEdit As Contact) As ActionResult
' Validation logic
ValidateContact(contactToEdit)
If Not ModelState.IsValid Then
Return View()
End If
' Database logic
Try
_repository.EditContact(contactToEdit)
Return RedirectToAction("Index")
Catch
Return View()
End Try
End Function
Function Delete(ByVal id As Integer) As ActionResult
Return View(_repository.GetContact(id))
End Function
<AcceptVerbs(HttpVerbs.Post)> _
Function Delete(ByVal contactToDelete As Contact) As ActionResult
Try
_repository.DeleteContact(contactToDelete)
Return RedirectToAction("Index")
Catch
Return View()
End Try
End Function
End Class
Zwróć uwagę, że kontroler kontaktów na liście 3 ma dwa konstruktory. Pierwszy konstruktor przekazuje konkretne wystąpienie interfejsu IContactManagerRepository do drugiego konstruktora. Klasa kontrolera kontaktów używa wstrzykiwania zależności konstruktora.
Jedyną klasą EntityContactManagerRepository jest klasa i tylko w pierwszym konstruktorze. Pozostała część klasy używa interfejsu IContactManagerRepository zamiast konkretnej klasy EntityContactManagerRepository.
Ułatwia to przełączanie implementacji klasy IContactManagerRepository w przyszłości. Jeśli chcesz użyć klasy DataServicesContactRepository zamiast klasy EntityContactManagerRepository, zmodyfikuj pierwszy konstruktor.
Wstrzykiwanie zależności konstruktora sprawia również, że klasa kontrolera Kontakt jest bardzo testowalna. W testach jednostkowych można utworzyć wystąpienie kontrolera kontaktów, przekazując pozorną implementację klasy IContactManagerRepository. Ta funkcja wstrzykiwania zależności będzie dla nas bardzo ważna w następnej iteracji podczas kompilowania testów jednostkowych dla aplikacji Contact Manager.
Uwaga
Jeśli chcesz całkowicie rozdzielić klasę kontrolera Kontakt z konkretnej implementacji interfejsu IContactManagerRepository, możesz skorzystać z platformy obsługującej wstrzykiwanie zależności, takie jak StructureMap lub Microsoft Entity Framework (MEF). Korzystając z struktury wstrzykiwania zależności, nigdy nie musisz odwoływać się do konkretnej klasy w kodzie.
Tworzenie warstwy usługi
Być może zauważysz, że nasza logika walidacji jest nadal mieszana z naszą logiką kontrolera w zmodyfikowanej klasie kontrolera w liście 3. Z tego samego powodu warto odizolować logikę dostępu do danych, dlatego warto odizolować logikę weryfikacji.
Aby rozwiązać ten problem, możemy utworzyć oddzielną warstwę usługi. Warstwa usługi to oddzielna warstwa, którą możemy wstawić między naszymi klasami kontrolera i repozytorium. Warstwa usługi zawiera naszą logikę biznesową, w tym całą logikę walidacji.
Element ContactManagerService znajduje się na liście 4. Zawiera logikę walidacji z klasy Kontroler kontaktów.
Lista 4 — Models\ContactManagerService.vb
Public Class ContactManagerService
Implements IContactManagerService
Private _validationDictionary As IValidationDictionary
Private _repository As IContactManagerRepository
Public Sub New(ByVal validationDictionary As IValidationDictionary)
Me.New(validationDictionary, New EntityContactManagerRepository())
End Sub
Public Sub New(ByVal validationDictionary As IValidationDictionary, ByVal repository As IContactManagerRepository)
_validationDictionary = validationDictionary
_repository = repository
End Sub
Public Function ValidateContact(ByVal contactToValidate As Contact) As Boolean
If contactToValidate.FirstName.Trim().Length = 0 Then
_validationDictionary.AddError("FirstName", "First name is required.")
End If
If contactToValidate.LastName.Trim().Length = 0 Then
_validationDictionary.AddError("LastName", "Last name is required.")
End If
If contactToValidate.Phone.Length > 0 AndAlso (Not Regex.IsMatch(contactToValidate.Phone, "((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}")) Then
_validationDictionary.AddError("Phone", "Invalid phone number.")
End If
If contactToValidate.Email.Length > 0 AndAlso (Not Regex.IsMatch(contactToValidate.Email, "^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$")) Then
_validationDictionary.AddError("Email", "Invalid email address.")
End If
Return _validationDictionary.IsValid
End Function
#Region "IContactManagerService Members"
Public Function CreateContact(ByVal contactToCreate As Contact) As Boolean Implements IContactManagerService.CreateContact
' Validation logic
If Not ValidateContact(contactToCreate) Then
Return False
End If
' Database logic
Try
_repository.CreateContact(contactToCreate)
Catch
Return False
End Try
Return True
End Function
Public Function EditContact(ByVal contactToEdit As Contact) As Boolean Implements IContactManagerService.EditContact
' Validation logic
If Not ValidateContact(contactToEdit) Then
Return False
End If
' Database logic
Try
_repository.EditContact(contactToEdit)
Catch
Return False
End Try
Return True
End Function
Public Function DeleteContact(ByVal contactToDelete As Contact) As Boolean Implements IContactManagerService.DeleteContact
Try
_repository.DeleteContact(contactToDelete)
Catch
Return False
End Try
Return True
End Function
Public Function GetContact(ByVal id As Integer) As Contact Implements IContactManagerService.GetContact
Return _repository.GetContact(id)
End Function
Public Function ListContacts() As IEnumerable(Of Contact) Implements IContactManagerService.ListContacts
Return _repository.ListContacts()
End Function
#End Region
End Class
Zwróć uwagę, że konstruktor klasy ContactManagerService wymaga elementu ValidationDictionary. Warstwa usługi komunikuje się z warstwą kontrolera za pośrednictwem tego kontrolki ValidationDictionary. Szczegółowo omówimy element ValidationDictionary w poniższej sekcji podczas omawiania wzorca dekoratora.
Zwróć uwagę, że usługa ContactManagerService implementuje interfejs IContactManagerService. Zawsze należy dążyć do programowania w odniesieniu do interfejsów zamiast konkretnych klas. Inne klasy w aplikacji Contact Manager nie współdziałają bezpośrednio z klasą ContactManagerService. Zamiast tego, z jednym wyjątkiem, pozostała część aplikacji Contact Manager jest programowana względem interfejsu IContactManagerService.
Interfejs IContactManagerService znajduje się na liście 5.
Lista 5 — Models\IContactManagerService.vb
Public Interface IContactManagerService
Function CreateContact(ByVal contactToCreate As Contact) As Boolean
Function DeleteContact(ByVal contactToDelete As Contact) As Boolean
Function EditContact(ByVal contactToEdit As Contact) As Boolean
Function GetContact(ByVal id As Integer) As Contact
Function ListContacts() As IEnumerable(Of Contact)
End Interface
Zmodyfikowana klasa kontrolera kontaktów jest zawarta w liście 6. Zwróć uwagę, że kontroler kontaktów nie wchodzi już w interakcję z repozytorium ContactManager. Zamiast tego kontroler kontaktów współdziała z usługą ContactManager. Każda warstwa jest izolowana jak najwięcej od innych warstw.
Lista 6 — Controllers\ContactController.vb
Public Class ContactController
Inherits System.Web.Mvc.Controller
Private _service As IContactManagerService
Sub New()
_service = new ContactManagerService(New ModelStateWrapper(ModelState))
End Sub
Sub New(service As IContactManagerService)
_service = service
End Sub
Function Index() As ActionResult
Return View(_service.ListContacts())
End Function
Function Create() As ActionResult
Return View()
End Function
<AcceptVerbs(HttpVerbs.Post)> _
Function Create(<Bind(Exclude:="Id")> ByVal contactToCreate As Contact) As ActionResult
If _service.CreateContact(contactToCreate) Then
Return RedirectToAction("Index")
End If
Return View()
End Function
Function Edit(ByVal id As Integer) As ActionResult
Return View(_service.GetContact(id))
End Function
<AcceptVerbs(HttpVerbs.Post)> _
Function Edit(ByVal contactToEdit As Contact) As ActionResult
If _service.EditContact(contactToEdit) Then
Return RedirectToAction("Index")
End If
Return View()
End Function
Function Delete(ByVal id As Integer) As ActionResult
Return View(_service.GetContact(id))
End Function
<AcceptVerbs(HttpVerbs.Post)> _
Function Delete(ByVal contactToDelete As Contact) As ActionResult
If _service.DeleteContact(contactToDelete) Then
return RedirectToAction("Index")
End If
Return View()
End Function
End Class
Nasza aplikacja nie działa już za pomocą zasady odpowiedzialności pojedynczej (SRP). Kontroler kontaktów w liście 6 został pozbawiony każdej odpowiedzialności innej niż kontrolowanie przepływu wykonywania aplikacji. Cała logika walidacji została usunięta z kontrolera kontaktów i wypchnięta do warstwy usługi. Cała logika bazy danych została wypchnięta do warstwy repozytorium.
Używanie wzorca dekoratora
Chcemy mieć możliwość całkowitego oddzielenia warstwy usługi od warstwy kontrolera. W zasadzie powinniśmy mieć możliwość skompilowania warstwy usługi w osobnym zestawie od warstwy kontrolera bez konieczności dodawania odwołania do naszej aplikacji MVC.
Jednak nasza warstwa usługi musi mieć możliwość przekazywania komunikatów o błędach weryfikacji z powrotem do warstwy kontrolera. Jak umożliwić warstwie usługi komunikowanie komunikatów o błędach weryfikacji bez sprzężenia kontrolera i warstwy usługi? Możemy skorzystać ze wzorca projektowego oprogramowania o nazwie deseń dekoratora.
Kontroler używa klasy ModelStateDictionary o nazwie ModelStateState do reprezentowania błędów walidacji. W związku z tym możesz być kuszony, aby przekazać modelState z warstwy kontrolera do warstwy usługi. Jednak użycie elementu ModelState w warstwie usługi sprawi, że warstwa usługi będzie zależna od funkcji platformy ASP.NET MVC. Byłoby to złe, ponieważ pewnego dnia warto użyć warstwy usługi z aplikacją WPF zamiast aplikacji ASP.NET MVC. W takim przypadku nie chcesz odwoływać się do platformy ASP.NET MVC, aby użyć klasy ModelStateDictionary.
Wzorzec dekoratora umożliwia opakowywanie istniejącej klasy w nowej klasie w celu zaimplementowania interfejsu. Nasz projekt Contact Manager zawiera klasę ModelStateWrapper zawartą w liście List 7. Klasa ModelStateWrapper implementuje interfejs w liście 8.
Lista 7 — Models\Validation\ModelStateWrapper.vb
Public Class ModelStateWrapper
Implements IValidationDictionary
Private _modelState As ModelStateDictionary
Public Sub New(ByVal modelState As ModelStateDictionary)
_modelState = modelState
End Sub
Public Sub AddError(ByVal key As String, ByVal errorMessage As String) Implements IValidationDictionary.AddError
_modelState.AddModelError(key, errorMessage)
End Sub
Public ReadOnly Property IsValid() As Boolean Implements IValidationDictionary.IsValid
Get
Return _modelState.IsValid
End Get
End Property
End Class
Lista 8 — Models\Validation\IValidationDictionary.vb
Public Interface IValidationDictionary
Sub AddError(ByVal key As String, ByVal errorMessage As String)
ReadOnly Property IsValid() As Boolean
End Interface
Jeśli przyjrzysz się liście 5, zobaczysz, że warstwa usługi ContactManager używa wyłącznie interfejsu IValidationDictionary. Usługa ContactManager nie jest zależna od klasy ModelStateDictionary. Gdy kontroler kontaktów tworzy usługę ContactManager, kontroler opakowuje jego modelState w następujący sposób:
_service = new ContactManagerService(New ModelStateWrapper(ModelState))
Podsumowanie
W tej iteracji nie dodaliśmy żadnych nowych funkcji do aplikacji Contact Manager. Celem tej iteracji było refaktoryzacja aplikacji Contact Manager, aby ułatwić konserwację i modyfikowanie.
Najpierw zaimplementowaliśmy wzorzec projektowania oprogramowania repozytorium. Przeprowadziliśmy migrację całego kodu dostępu do danych do oddzielnej klasy repozytorium ContactManager.
Odizolowaliśmy również logikę weryfikacji od naszej logiki kontrolera. Utworzyliśmy oddzielną warstwę usługi zawierającą cały nasz kod weryfikacyjny. Warstwa kontrolera współdziała z warstwą usługi, a warstwa usługi współdziała z warstwą repozytorium.
Podczas tworzenia warstwy usługi skorzystaliśmy ze wzorca dekoratora, aby odizolować modelState od warstwy usługi. W naszej warstwie usługi programowaliśmy interfejs IValidationDictionary zamiast ModelState.
Na koniec skorzystaliśmy ze wzorca projektowego oprogramowania o nazwie Wzorzec wstrzykiwania zależności. Ten wzorzec umożliwia programowanie interfejsów (abstrakcji) zamiast konkretnych klas. Implementacja wzorca projektowania iniekcji zależności sprawia również, że nasz kod jest bardziej testowalny. W następnej iteracji dodamy testy jednostkowe do naszego projektu.
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla