Aracılığıyla paylaş


4. Yineleme – Uygulamanın gevşek bir şekilde bağlanmasını sağlama (VB)

Microsoft tarafından

Kodu İndir

Bu dördüncü yinelemede, Contact Manager uygulamasının bakımını ve değiştirilmesini kolaylaştırmak için çeşitli yazılım tasarım desenlerinden yararlanıyoruz. Örneğin, uygulamamızı Depo desenini ve Bağımlılık Ekleme düzenini kullanacak şekilde yeniden düzenleyeceğiz.

MVC Uygulaması ASP.NET Kişi Yönetimi Oluşturma (VB)

Bu öğretici serisinde, baştan sona bir Kişi Yönetimi uygulamasının tamamını oluşturuyoruz. Contact Manager uygulaması kişi listesi için kişi bilgilerini (adlar, telefon numaraları ve e-posta adresleri) depolamanıza olanak tanır.

Uygulamayı birden çok yineleme üzerinden derleyeceğiz. Her yinelemede uygulamayı aşamalı olarak iyileştiriyoruz. Bu çoklu yineleme yaklaşımının amacı, her değişikliğin nedenini anlamanıza olanak tanımaktır.

  • Yineleme #1 - Uygulamayı oluşturun. İlk yinelemede, Contact Manager'ı mümkün olan en basit şekilde oluştururuz. Temel veritabanı işlemleri için destek ekliyoruz: Oluşturma, Okuma, Güncelleştirme ve Silme (CRUD).

  • Yineleme #2 - Uygulamanın güzel görünmesini sağlayın. Bu yinelemede, MVC görünümü ana sayfasını ve basamaklı stil sayfasını varsayılan ASP.NET değiştirerek uygulamanın görünümünü iyileştireceğiz.

  • 3. Yineleme - Form doğrulaması ekleyin. Üçüncü yinelemede temel form doğrulamasını ekleyeceğiz. Gerekli form alanlarını tamamlamadan kişilerin form göndermesini engelleriz. Ayrıca e-posta adreslerini ve telefon numaralarını doğrularız.

  • Yineleme #4 - Uygulamayı gevşek bir şekilde birleştirin. Bu dördüncü yinelemede, Contact Manager uygulamasının bakımını ve değiştirilmesini kolaylaştırmak için çeşitli yazılım tasarım desenlerinden yararlanıyoruz. Örneğin, uygulamamızı Depo desenini ve Bağımlılık Ekleme düzenini kullanacak şekilde yeniden düzenleyeceğiz.

  • Yineleme #5 - Birim testleri oluşturun. Beşinci yinelemede, birim testleri ekleyerek uygulamamızın bakımını ve değiştirilmesini kolaylaştırıyoruz. Veri modeli sınıflarımızla dalga geçiyoruz ve denetleyicilerimiz ve doğrulama mantığımız için birim testleri oluşturuyoruz.

  • Yineleme #6 - Test temelli geliştirmeyi kullanın. Bu altıncı yinelemede, önce birim testleri yazıp birim testlerine kod yazarak uygulamamıza yeni işlevler ekleyeceğiz. Bu yinelemede kişi grupları ekleyeceğiz.

  • Yineleme #7 - Ajax işlevselliğini ekleyin. Yedinci yinelemede Ajax için destek ekleyerek uygulamamızın yanıt hızını ve performansını iyileştiriyoruz.

Bu Yineleme

Contact Manager uygulamasının bu dördüncü yinelemesinde, uygulamayı daha gevşek bir şekilde birleştiğinden emin olmak için yeniden düzenleyeceğiz. Bir uygulama gevşek bir şekilde bağlandığında, uygulamanın diğer bölümlerinde kodu değiştirmenize gerek kalmadan uygulamanın bir bölümündeki kodu değiştirebilirsiniz. Gevşek bir şekilde bağlanmış uygulamalar, değişmeye daha dayanıklıdır.

Şu anda, Contact Manager uygulaması tarafından kullanılan tüm veri erişimi ve doğrulama mantığı denetleyici sınıflarında yer almaktadır. Bu kötü bir fikir. Uygulamanızın bir bölümünü değiştirmeniz gerektiğinde, hataları uygulamanızın başka bir bölümüne ekleme riskiyle karşılaşırsınız. Örneğin, doğrulama mantığınızı değiştirirseniz veri erişiminize veya denetleyici mantığınıza yeni hatalar ekleme riskiyle karşılaşırsınız.

Not

(SRP), bir sınıfın değiştirmek için hiçbir zaman birden fazla nedeni olmamalıdır. Denetleyici, doğrulama ve veritabanı mantığının karıştırılması, Tek Sorumluluk İlkesi'nin çok büyük bir ihlalidir.

Uygulamanızı değiştirmenizin birkaç nedeni olabilir. Uygulamanıza yeni bir özellik eklemeniz gerekebilir, uygulamanızdaki bir hatayı düzeltmeniz veya uygulamanızın bir özelliğinin uygulanma şeklini değiştirmeniz gerekebilir. Uygulamalar nadiren statiktir. Zamanla büyüme ve sessize alma eğilimindedirler.

Örneğin, veri erişim katmanınızı uygulama şeklinizi değiştirmeye karar ve ettiğinizi düşünün. Contact Manager uygulaması şu anda veritabanına erişmek için Microsoft Entity Framework kullanıyor. Ancak, ADO.NET Data Services veya NHibernate gibi yeni veya alternatif bir veri erişim teknolojisine geçiş yapmaya karar vekleyebilirsiniz. Ancak, veri erişim kodu doğrulama ve denetleyici kodundan yalıtılmadığından, veri erişimiyle doğrudan ilgili olmayan diğer kodları değiştirmeden uygulamanızdaki veri erişim kodunu değiştirmenin hiçbir yolu yoktur.

Öte yandan, bir uygulama gevşek bir şekilde birleştirildiğinde, uygulamanın diğer bölümlerine dokunmadan uygulamanın bir bölümünde değişiklik yapabilirsiniz. Örneğin, doğrulama veya denetleyici mantığınızı değiştirmeden veri erişim teknolojileri arasında geçiş yapabilirsiniz.

Bu yinelemede, Contact Manager uygulamamızı daha gevşek bir şekilde bağlanmış bir uygulamada yeniden düzenlememizi sağlayan çeşitli yazılım tasarım desenlerinden yararlanıyoruz. bitirdiğimizde, Contact Manager daha önce yapmadığı hiçbir şeyi yapmaz. Ancak, gelecekte uygulamayı daha kolay değiştirebileceğiz.

Not

Yeniden düzenleme, bir uygulamayı mevcut işlevleri kaybetmeyecek şekilde yeniden yazma işlemidir.

Depo Yazılım Tasarım Desenini Kullanma

İlk değişikliğimiz, Depo deseni adı verilen bir yazılım tasarım deseninin avantajlarından yararlanmaktır. Veri erişim kodumuzu uygulamamızın geri kalanından yalıtmak için Depo desenini kullanacağız.

Depo düzenini uygulamak için aşağıdaki iki adımı tamamlamamız gerekir:

  1. Arabirim oluşturma
  2. Arabirimini uygulayan somut bir sınıf oluşturma

İlk olarak, gerçekleştirmemiz gereken tüm veri erişim yöntemlerini açıklayan bir arabirim oluşturmamız gerekir. IContactManagerRepository arabirimi Listeleme 1'de yer alır. Bu arabirim beş yöntemi açıklar: CreateContact(), DeleteContact(), EditContact(), GetContact ve ListContacts().

Listeleme 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

Ardından, IContactManagerRepository arabirimini uygulayan somut bir sınıf oluşturmamız gerekir. Veritabanına erişmek için Microsoft Entity Framework'i kullandığımız için EntityContactManagerRepository adlı yeni bir sınıf oluşturacağız. Bu sınıf Liste 2'de yer alır.

Listeleme 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

EntityContactManagerRepository sınıfının IContactManagerRepository arabirimini uyguladığına dikkat edin. sınıfı, bu arabirim tarafından açıklanan yöntemlerin beşini de uygular.

Neden bir arabirimle uğraşmamız gerektiğini merak ediyor olabilirsiniz. Neden hem bir arabirim hem de bunu uygulayan bir sınıf oluşturmamız gerekiyor?

Tek bir istisna dışında, uygulamamızın geri kalanı somut sınıfla değil arabirimle etkileşim kurar. EntityContactManagerRepository sınıfı tarafından kullanıma sunulan yöntemleri çağırmak yerine IContactManagerRepository arabirimi tarafından kullanıma sunulan yöntemleri çağıracağız.

Bu şekilde, uygulamamızın geri kalanını değiştirmeye gerek kalmadan arabirimini yeni bir sınıfla uygulayabiliriz. Örneğin, gelecekteki bir tarihte IContactManagerRepository arabirimini uygulayan bir DataServicesContactManagerRepository sınıfı uygulamak isteyebiliriz. DataServicesContactManagerRepository sınıfı, Microsoft Entity Framework yerine veritabanına erişmek için ADO.NET Veri Hizmetleri'ni kullanabilir.

Uygulama kodumuz beton EntityContactManagerRepository sınıfı yerine IContactManagerRepository arabirimine göre programlanmışsa kodumuzun geri kalanını değiştirmeden somut sınıfları değiştirebiliriz. Örneğin, veri erişimimizi veya doğrulama mantığımızı değiştirmeden EntityContactManagerRepository sınıfından DataServicesContactManagerRepository sınıfına geçebiliriz.

Somut sınıflar yerine arabirimlere (soyutlamalar) karşı programlamak, uygulamamızı değiştirmek için daha dayanıklı hale getirir.

Not

Yeniden Düzenle, Arabirimi Ayıkla menü seçeneğini belirleyerek Visual Studio'da somut bir sınıftan hızla bir arabirim oluşturabilirsiniz. Örneğin, önce EntityContactManagerRepository sınıfını oluşturabilir ve ardından Ayıklama Arabirimi'ni kullanarak IContactManagerRepository arabirimini otomatik olarak oluşturabilirsiniz.

Bağımlılık Ekleme Yazılımı Tasarım Desenini Kullanma

Veri erişim kodumuzu ayrı bir Depo sınıfına geçirdiğimize göre, Contact denetleyicimizi bu sınıfı kullanacak şekilde değiştirmemiz gerekir. Denetleyicimizdeki Depo sınıfını kullanmak için Bağımlılık Ekleme adlı yazılım tasarım deseninin avantajlarından yararlanacağız.

Değiştirilen Kişi denetleyicisi Liste 3'te yer alır.

Liste 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

Liste 3'teki Kişi denetleyicisinin iki oluşturucuya sahip olduğuna dikkat edin. İlk oluşturucu, IContactManagerRepository arabiriminin somut bir örneğini ikinci oluşturucuya geçirir. Contact denetleyici sınıfı Oluşturucu Bağımlılık Ekleme'yi kullanır.

EntityContactManagerRepository sınıfının kullanıldığı tek ve tek yer ilk oluşturucudadır. sınıfının geri kalanı beton EntityContactManagerRepository sınıfı yerine IContactManagerRepository arabirimini kullanır.

Bu, gelecekte IContactManagerRepository sınıfının uygulamalarını değiştirmeyi kolaylaştırır. EntityContactManagerRepository sınıfı yerine DataServicesContactRepository sınıfını kullanmak istiyorsanız ilk oluşturucuyu değiştirmeniz gerekir.

Oluşturucu Bağımlılığı ekleme ayrıca Contact denetleyici sınıfını çok test edilebilir hale getirir. Birim testlerinizde, IContactManagerRepository sınıfının sahte bir uygulamasını geçirerek Contact denetleyicisinin örneğini oluşturabilirsiniz. Bağımlılık Ekleme'nin bu özelliği, Contact Manager uygulaması için birim testleri oluştururken bir sonraki yinelemede bizim için çok önemli olacaktır.

Not

Contact controller sınıfını IContactManagerRepository arabiriminin belirli bir uygulamasından tamamen ayrıştırmak istiyorsanız, StructureMap veya Microsoft Entity Framework (MEF) gibi Bağımlılık Eklemeyi destekleyen bir çerçeveden yararlanabilirsiniz. Bağımlılık Ekleme çerçevesinden yararlanarak kodunuzda somut bir sınıfa başvurmanız gerekmez.

Hizmet Katmanı Oluşturma

Doğrulama mantığımızın, Liste 3'teki değiştirilmiş denetleyici sınıfındaki denetleyici mantığımızla hala karma olduğunu fark etmiş olabilirsiniz. Veri erişim mantığımızı yalıtmanın iyi bir fikir olmasıyla aynı nedenle, doğrulama mantığımızı yalıtmak iyi bir fikirdir.

Bu sorunu çözmek için ayrı bir hizmet katmanı oluşturabiliriz. Hizmet katmanı, denetleyicimiz ve depo sınıflarımız arasına ekleyebileceğimiz ayrı bir katmandır. Hizmet katmanı, tüm doğrulama mantığımız dahil olmak üzere iş mantığımızı içerir.

ContactManagerService, Liste 4'te yer alır. Contact denetleyici sınıfından doğrulama mantığını içerir.

Listeleme 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

ContactManagerService oluşturucusunun geçerlilik sözlüğü gerektirdiğine dikkat edin. Hizmet katmanı bu ValidationDictionary aracılığıyla denetleyici katmanıyla iletişim kurar. Aşağıdaki bölümde, Dekoratör desenini tartışırken ValidationDictionary'i ayrıntılı olarak ele alıyoruz.

Ayrıca ContactManagerService'in IContactManagerService arabirimini uyguladığına da dikkat edin. Her zaman somut sınıflar yerine arabirimlere karşı programlamaya çalışmanız gerekir. Contact Manager uygulamasındaki diğer sınıflar ContactManagerService sınıfıyla doğrudan etkileşim kurmaz. Bunun yerine, tek bir özel durum dışında, Contact Manager uygulamasının geri kalanı IContactManagerService arabirimine göre programlanır.

IContactManagerService arabirimi Listeleme 5'te yer alır.

Listeleme 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

Değiştirilen Contact controller sınıfı Liste 6'da yer alır. Kişi denetleyicisinin artık ContactManager deposuyla etkileşim kurmadığını göreceksiniz. Bunun yerine, Kişi denetleyicisi ContactManager hizmetiyle etkileşim kurar. Her katman diğer katmanlardan mümkün olduğunca yalıtılır.

Listeleme 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

Uygulamamız artık Tek Sorumluluk İlkesi 'nin (SRP) bir altyapısını çalıştırmaz. Liste 6'daki Kişi denetleyicisi, uygulama yürütme akışını denetleme dışında her sorumluluktan alındı. Tüm doğrulama mantığı Kişi denetleyicisinden kaldırıldı ve hizmet katmanına gönderildi. Tüm veritabanı mantığı depo katmanına gönderildi.

Dekoratör Desenini Kullanma

Hizmet katmanımızı denetleyici katmanımızdan tamamen ayrıştırabilmek istiyoruz. Prensip olarak, MVC uygulamamıza başvuru eklemeye gerek kalmadan hizmet katmanımızı denetleyici katmanımızdan ayrı bir derlemede derleyebilmemiz gerekir.

Ancak hizmet katmanımızın doğrulama hata iletilerini denetleyici katmanına geri geçirebilmesi gerekir. Hizmet katmanının denetleyiciyle hizmet katmanını bağlamadan doğrulama hata iletilerini iletmesini nasıl sağlayabiliriz? Dekoratör deseni adlı bir yazılım tasarım deseninin avantajlarından yararlanabiliriz.

Denetleyici, doğrulama hatalarını göstermek için ModelState adlı bir ModelStateDictionary kullanır. Bu nedenle ModelState'i denetleyici katmanından hizmet katmanına geçirmek isteyebilirsiniz. Ancak hizmet katmanında ModelState kullanmak, hizmet katmanınızı ASP.NET MVC çerçevesinin bir özelliğine bağımlı hale getirir. Bir gün hizmet katmanını ASP.NET MVC uygulaması yerine wpf uygulamasıyla kullanmak isteyebileceğiniz için bu durum kötü olabilir. Bu durumda, ModelStateDictionary sınıfını kullanmak için ASP.NET MVC çerçevesine başvurmak istemezsiniz.

Dekoratör düzeni, arabirim uygulamak için mevcut bir sınıfı yeni bir sınıfa sarmalamanızı sağlar. Contact Manager projemiz, Liste 7'de yer alan ModelStateWrapper sınıfını içerir. ModelStateWrapper sınıfı, Listing 8'de arabirimini uygular.

Listeleme 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

Listeleme 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

Liste 5'e yakından bakarsanız ContactManager hizmet katmanının yalnızca IValidationDictionary arabirimini kullandığını görürsünüz. ContactManager hizmeti ModelStateDictionary sınıfına bağımlı değildir. Contact denetleyici ContactManager hizmetini oluşturduğunda, denetleyici ModelState'ini şöyle sarmalar:

_service = new ContactManagerService(New ModelStateWrapper(ModelState))

Özet

Bu yinelemede, Contact Manager uygulamasına yeni bir işlev eklemedik. Bu yinelemenin amacı, Bakımı ve değiştirilmesini kolaylaştırmak için Contact Manager uygulamasını yeniden düzenlemekti.

İlk olarak, Depo yazılım tasarım desenini uyguladık. Tüm veri erişim kodunu ayrı bir ContactManager deposu sınıfına geçirdik.

Ayrıca doğrulama mantığımızı denetleyici mantığımızdan yalıtdık. Tüm doğrulama kodumuzu içeren ayrı bir hizmet katmanı oluşturduk. Denetleyici katmanı hizmet katmanıyla, hizmet katmanı ise depo katmanıyla etkileşim kurar.

Hizmet katmanını oluşturduğumuzda ModelState'i hizmet katmanımızdan yalıtmak için Dekoratör deseninden yararlandık. Hizmet katmanımızda ModelState yerine IValidationDictionary arabirimine karşı programlandık.

Son olarak, Bağımlılık Ekleme deseni adlı bir yazılım tasarım deseninin avantajından yararlandık. Bu düzen, somut sınıflar yerine arabirimlere (soyutlamalar) karşı programlama yapmamızı sağlar. Bağımlılık Ekleme tasarım desenini uygulamak da kodumuzu daha test edilebilir hale getirir. Sonraki yinelemede projemize birim testleri ekleyeceğiz.