1. Bölüm 1
Belgenin PDF dosyası, |BURADA| adresinde mevcuttur.
Belgedeki örnekler |BURADA| adresinde mevcuttur.
1.1. Introduction
Makalenin amaçları:
- 3 katmanlı bir web uygulaması yazmak [interface utilisateur, métier, accès aux données]
- uygulamayı Spring ile yapılandırmak IOC
- üç katmandan birinin veya birkaçının uygulamasını değiştirerek farklı sürümler yazmak.
Kullanılan araçlar:
- Geliştirme için Visual Studio.net - ekteki 3.1 numaralı paragrafa bakınız;
- Çalıştırma için Cassini web sunucusu - ekteki 3.2 numaralı paragrafa bakınız;
- Birim testleri için Nunit – bkz. ek, paragraf 3.4;
- Web uygulamasının katmanlarının entegrasyonu ve yapılandırması için Spring – bkz. ek, paragraf 3.3;
Başlangıç-orta-ileri düzey ölçeğinde, bu belge [intermédiaire-avancé] bölümünde yer almaktadır. Belgeyi anlamak için çeşitli ön koşullar gereklidir. Bunlardan bazıları, benim yazdığım belgelerde ele alınmıştır. Bu durumda, söz konusu belgeleri kaynak olarak belirtirim. Elbette bu sadece bir öneridir ve okuyucu kendi tercih ettiği belgeleri kullanabilir.
- VB.net dili: [Örneklerle VB.NET diline giriş (2004) ];
- VB.net ile web programlama: [ASP.NET 1.1 ile Web Geliştirme (2004)];
- Spring'in IoC özelliğinin kullanımı: [.NET için Spring IoC (2005)] ;
- Spring.net belgeleri: [Spring.NET | Homepage ]
Bu belge, Java [Struts, Spring ve Java kullanılarak üç katmanlı mimariler ve MVC mimarileri (2005)] için yazılmış bir belgenin ana hatlarını ele almaktadır. VB.NET'te, Java ile yazılmış üç katmanlı MVC web uygulamasını geliştiriyoruz. Burada vurgulamak istediğimiz nokta, Java ve .NET geliştirme platformlarının birbirine yeterince yakın olması nedeniyle, bu iki alandan birinde edinilen becerilerin diğerinde de yeniden kullanılabileceğidir.
MVC ASP.NET geliştirme alanında yaygın olarak kabul görmüş bir çözüm bulunmuyor gibi görünüyor. Aşağıdaki çözüm, [ASP.NET 1.1 ile Web Geliştirme (2004)] belgesinde tanıtılan yöntemi temel almaktadır. Bu yöntem, J2EE geliştirme alanında yaygın olarak kullanılan kavramları kullanma avantajına sahip olsa da, yalnızca olduğu gibi, yani c.a.d geliştirme yöntemlerinden biri olarak değerlendirilmelidir. MVC'ten ASP.NET'e bir geliştirme yöntemi yaygın olarak kabul gördüğü anda, o zaman bu yöntem benimsenmelidir. Geliştirme aşamasında olan Spring'in .NET sürümü, ilk çözüm olabilir.
1.2. webarticles uygulaması
Burada, basitleştirilmiş bir e-ticaret web uygulamasının bileşenlerini sunuyoruz. Bu uygulama, web kullanıcılarına şunları sağlayacaktır:
- bir veritabanından gelen ürün listesini görüntülemek
- bazı ürünleri elektronik sepetlerine ekleyebilme
- sepeti onaylayabilmelerini sağlayacaktır. Bu onay işleminin tek etkisi, satın alınan ürünlerin stok bilgilerinin veritabanında güncellenmesidir.
Kullanıcıya sunulan farklı görünümler şunlar olacaktır:
- Satışta olan ürünlerin listesini gösteren "LISTE" görünümü ![]() | - Bir ürün hakkında ek bilgiler veren "[INFOS]" görünümü: ![]() |
- Müşterinin sepetinin içeriğini gösteren [PANIER] ve [PANIERVIDE] görünümleri
![]() | ![]() |
- Uygulamadaki tüm hataları bildiren [ERREURS] görünümü

1.3. Uygulamanın genel mimarisi
Aşağıdaki üç katmanlı yapıya sahip bir uygulama geliştirmek istiyoruz:
![]() |
- Arayüzlerin kullanılması sayesinde üç katman birbirinden bağımsız hale getirilmiştir
- Farklı katmanların entegrasyonu Spring ile gerçekleştirilir
- Her katman ayrı ad alanlarına sahiptir: web (UI katmanı), domain (iş katmanı) ve dao (veri erişim katmanı).
Uygulama, MVC mimarisine (Model - Görünüm - Denetleyici) uygun olacaktır. Yukarıdaki katman şemasını ele alırsak, MVC mimarisi bu şemaya şu şekilde entegre edilir:
![]() |
Bir istemciden gelen isteğin işlenmesi aşağıdaki adımlara göre gerçekleşir:
- müşteri, denetleyiciye bir istek gönderir. Bu denetleyici, burada özel bir rol üstlenecek bir .aspx sayfası olacaktır. Müşterilerin tüm isteklerini bu sayfa üzerinden alır. Bu, uygulamanın giriş kapısıdır. MVC'teki C harfidir.
- Denetleyici bu talebi işler. Bunu yapmak için iş katmanının yardımına ihtiyaç duyabilir; bu, MVC yapısında M modeli olarak adlandırılır.
- Denetleyici, iş katmanından bir yanıt alır. Müşterinin talebi işlenmiştir. Bu, birkaç farklı yanıtı içerebilir. Klasik bir örnek,
- isteğin düzgün bir şekilde işlenememesi durumunda bir hata sayfası
- aksi takdirde bir onay sayfası
- denetleyici, müşteriye gönderilecek yanıtı (= görünüm) seçer. Bu, çoğunlukla dinamik öğeler içeren bir sayfadır. Denetleyici, bu öğeleri görünüme sağlar.
- Görünüm, müşteriye gönderilir. Bu, MVC'teki V'dir.
1.4. Model
Burada MVC'teki M'yi inceliyoruz. Model burada aşağıdaki öğelerden oluşur:
- iş sınıfları
- veri erişim sınıfları
- veritabanı
1.4.1. Veritabanı
Veritabanı, yalnızca ARTICLES adlı bir tablo içermektedir. Bu tablo, aşağıdaki SQL komutlarıyla oluşturulmuştur:
CREATE TABLE ARTICLES (
ID INTEGER NOT NULL,
NOM VARCHAR(20) NOT NULL,
PRIX NUMERIC(15,2) NOT NULL,
STOCKACTUEL INTEGER NOT NULL,
STOCKMINIMUM INTEGER NOT NULL
);
/* kısıtlamalar */
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_ID check (ID>0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_PRIX check (PRIX>=0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_STOCKACTUEL check (STOCKACTUEL>=0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_STOCKMINIMUM check (STOCKMINIMUM>=0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_NOM check (NOM<>'');
ALTER TABLE ARTICLES ADD CONSTRAINT UNQ_NOM UNIQUE (NOM);
/* birincil anahtar */
ALTER TABLE ARTICLES ADD CONSTRAINT PK_ARTICLES PRIMARY KEY (ID);
bir ürünü benzersiz bir şekilde tanımlayan birincil anahtar | |
ürün adı | |
ürünün fiyatı | |
mevcut stok durumu | |
yeniden tedarik siparişinin verilmesi gereken stok seviyesi |
1.4.2. Modelin ad alanları
M modeli burada iki ad alanı şeklinde sunulmaktadır:
- istia.st.articles.dao: [dao] katmanının veri erişim sınıflarını içerir
- istia.st.articles.domain: [domain] katmanının iş sınıflarını içerir
Bu ad alanlarının her biri, kendisine ait bir "assembly" dosyası içinde oluşturulacaktır:
contenu | rôle | |
- [IArticlesDao]: [dao] katmanına erişim arayüzü. [domain] katmanı yalnızca bu arayüzü görür. Başka bir arayüz görmez. - [Article]: bir ürünü tanımlayan sınıf - [ArticlesDaoArrayList]: [IArticlesDao] arayüzünü, [ArrayList] | veri erişim katmanı - tamamen [dao] katmanında yer alır web uygulamasının | |
- [IArticlesDomain]: [domain] katmanına erişim arayüzüdür. Web katmanının gördüğü tek arayüzdür. Başka bir arayüz görmez. - [AchatsArticles]: [IArticlesDomain]'i uygulayan bir sınıf - [Achat]: Bir müşterinin satın alma işlemini temsil eden sınıf - [Panier]: bir müşterinin tüm satın alımlarını temsil eden sınıf | web üzerindeki satın alma modelini temsil eder web'deki satın alma modelini temsil eder - tamamen web uygulamasının web uygulamasının 3-katmanlı mimarisinin |
1.4.3. [dao] katmanı
[dao] katmanı aşağıdaki öğeleri içerir:
-
[IArticlesDao]: [dao] katmanına erişim arayüzü
-
[Article]: bir ürünü tanımlayan sınıf
-
[ArticlesDaoArrayList]: [IArticlesDao] arayüzünün, [ArrayList] sınıfı ile gerçekleştirilmesini sağlayan sınıf
[dao] katmanındaki [Visual Studio] projesinin yapısı şöyledir:

Açıklamalar:
- [dao] projesi, [bibliothèque de classes] türündedir
- Sınıflar, [istia] klasörünü kök olarak alan bir dizin yapısına yerleştirilmiştir. Hepsi [istia.st.articles.dao] ad alanında yer almaktadır.
1.4.3.1. [Article] sınıfı
Bir öğeyi tanımlayan sınıf şöyledir:
Imports System
Namespace istia.st.articles.dao
Public Class Article
' özel alanlar
Private _id As Integer
Private _nom As String
Private _prix As Double
Private _stockactuel As Integer
Private _stockminimum As Integer
' ürün kimliği
Public Property id() As Integer
Get
Return _id
End Get
Set(ByVal Value As Integer)
If Value <= 0 Then
Throw New Exception("Le champ id [" + Value.ToString + "] est invalide")
End If
Me._id = Value
End Set
End Property
' ürün adı
Public Property nom() As String
Get
Return _nom
End Get
Set(ByVal Value As String)
If Value Is Nothing OrElse Value.Trim.Equals("") Then
Throw New Exception("Le champ nom [" + Value + "] est invalide")
End If
Me._nom = Value
End Set
End Property
' ürün fiyatı
Public Property prix() As Double
Get
Return _prix
End Get
Set(ByVal Value As Double)
If Value < 0 Then
Throw New Exception("Le champ prix [" + Value.ToString + "] est invalide")
End If
Me._prix = Value
End Set
End Property
' ürünün mevcut stoğu
Public Property stockactuel() As Integer
Get
Return _stockactuel
End Get
Set(ByVal Value As Integer)
If Value < 0 Then
Throw New Exception("Le champ stockActuel [" + Value.ToString + "] est invalide")
End If
Me._stockactuel = Value
End Set
End Property
' ürün minimum stoğu
Public Property stockminimum() As Integer
Get
Return _stockminimum
End Get
Set(ByVal Value As Integer)
If Value < 0 Then
Throw New Exception("Le champ stockMinimum [" + Value.ToString + "] est invalide")
End If
Me._stockminimum = Value
End Set
End Property
' varsayılan üretici
Public Sub New()
End Sub
' özelliklere sahip üretici
Public Sub New(ByVal id As Integer, ByVal nom As String, ByVal prix As Double, ByVal stockactuel As Integer, ByVal stockminimum As Integer)
Me.id = id
Me.nom = nom
Me.prix = prix
Me.stockactuel = stockactuel
Me.stockminimum = stockminimum
End Sub
' ürün tanımlama yöntemi
Public Overrides Function ToString() As String
Return "[" + id.ToString + "," + nom + "," + prix.ToString + "," + stockactuel.ToString + "," + stockminimum.ToString + "]"
End Function
End Class
End Namespace
Bu sınıf şunları sunar:
- bir öğenin 5 bilgisini belirlemeye yarayan bir oluşturucu: [id, nom, prix, stockactuel, stockminimum]
- bu 5 bilgiyi okumak ve yazmak için kullanılan genel özellikler.
- ürüne girilen verilerin doğrulanması. Verilerde hata olması durumunda bir istisna atılır.
- bir makalenin değerini karakter dizisi biçiminde almayı sağlayan toString yöntemi. Bu, genellikle bir uygulamanın hata ayıklamasında kullanışlıdır.
1.4.3.2. [IArticlesDao] arayüzü
[IArticlesDao] arayüzü şu şekilde tanımlanmıştır:
Imports System
Imports System.Collections
Namespace istia.st.articles.dao
Public Interface IArticlesDao
' tüm ürünlerin listesi
Function getAllArticles() As IList
' ürün ekler
Function ajouteArticle(ByVal unArticle As Article) As Integer
' ürünü sil
Function supprimeArticle(ByVal idArticle As Integer) As Integer
' ürünü düzenler
Function modifieArticle(ByVal unArticle As Article) As Integer
' ürün arar
Function getArticleById(ByVal idArticle As Integer) As Article
' tüm ürünleri sil
Sub clearAllArticles()
' bir ürünün stokunu değiştirir
Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
End Interface
End Namespace
Arayüzdeki farklı yöntemlerin işlevleri şunlardır:
veri kaynağındaki tüm makaleleri döndürür | |
veri kaynağını boşaltır | |
birincil anahtarıyla tanımlanan [Article] nesnesini döndürür | |
veri kaynağına bir öğe eklemeye izin verir | |
veri kaynağındaki bir öğeyi değiştirmeye olanak tanır | |
veri kaynağından bir öğeyi silmeye olanak tanır | |
veri kaynağındaki bir ürünün stokunu değiştirmeye olanak tanır |
Arayüz, istemci programlarına yalnızca imzalarıyla tanımlanan bir dizi yöntem sunar. Bu yöntemlerin gerçekte nasıl uygulanacağıyla ilgilenmez. Bu, uygulamaya esneklik kazandırır. İstemci programı, arayüzün belirli bir uygulamasına değil, arayüze çağrı yapar.
![]() |
Belirli bir uygulamanın seçimi, bir Spring yapılandırma dosyası aracılığıyla yapılacaktır. Web uygulamasını test etmek için yalnızca veri erişim arayüzünün önemli olduğunu, uygulama sınıfının ise önemli olmadığını göstermek amacıyla, öncelikle veri kaynağını basit bir [ArrayList] nesnesi ile uygulayacağız. Daha sonra, SGBD tabanlı bir çözüm sunacağız.
1.4.3.3. [ArticlesDaoArrayList] uygulama sınıfı
[ArticlesDaoArrayList] uygulama sınıfı şu şekilde tanımlanmıştır:
Imports System
Imports System.Collections
Namespace istia.st.articles.dao
Public Class ArticlesDaoArrayList
Implements istia.st.articles.dao.IArticlesDao
Private articles As New ArrayList
Private Const nbArticles As Integer = 4
' varsayılan üretici
Public Sub New()
' bazı ürünleri üretir
For i As Integer = 1 To nbArticles
articles.Add(New Article(i, "article" + i.ToString, i * 10, i * 10, i * 10))
Next
End Sub
' tüm ürünlerin listesi
Public Function getAllArticles() As IList Implements IArticlesDao.getAllArticles
' ürün listesini döndürür
SyncLock Me
Return articles
End SyncLock
End Function
' tüm ürünlerin silinmesi
Public Sub clearAllArticles() Implements IArticlesDao.clearAllArticles
' ürün listesini boşaltma
SyncLock Me
articles.Clear()
End SyncLock
End Sub
' anahtarıyla tanımlanan bir öğeyi alma
Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDao.getArticleById
' listeden ürünü arar
SyncLock Me
Dim ipos As Integer = posArticle(articles, idArticle)
If ipos <> -1 Then
Return CType(articles(ipos), Article)
Else
Return Nothing
End If
End SyncLock
End Function
' ürün listesine bir ürün ekle
Public Function ajouteArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.ajouteArticle
' ürünü ürün listesine ekleniyor
SyncLock Me
' ürünün daha önce mevcut olup olmadığı kontrol ediliyor
Dim ipos As Integer = posArticle(articles, unArticle.id)
If ipos <> -1 Then
Throw New Exception("L'article d'id [" + unArticle.id.ToString + "] existe déjà")
End If
' ürün ekleniyor
articles.Add(unArticle)
' sonuç döndürülür
Return 1
End SyncLock
End Function
' bir ürünü düzenlemek
Public Function modifieArticle(ByVal articleNouveau As Article) As Integer Implements IArticlesDao.modifieArticle
' bir ürünü düzenliyoruz
SyncLock Me
' var olup olmadığı kontrol edilir
Dim ipos As Integer = posArticle(articles, articleNouveau.id)
' eğer yoksa
If ipos = -1 Then Return 0
' mevcut ise - makaleyi düzenliyoruz
articles(ipos) = articleNouveau
' sonuç verilir
Return 1
End SyncLock
End Function
' anahtarıyla tanımlanan bir makaleyi silmek
Public Function supprimeArticle(ByVal idArticle As Integer) As Integer Implements IArticlesDao.supprimeArticle
' bir ürünün silinmesi
SyncLock Me
' var olup olmadığı kontrol edilir
Dim ipos As Integer = posArticle(articles, idArticle)
' mevcut değilse
If ipos = -1 Then Return 0
' mevcutsa - silinir
articles.RemoveAt(ipos)
' sonuç döndürülür
Return 1
End SyncLock
End Function
' anahtarıyla tanımlanan bir ürünün stokunu değiştir
Public Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer Implements IArticlesDao.changerStockArticle
' bir ürünün stokunu değiştir
SyncLock Me
' var olup olmadığını kontrol edelim
Dim ipos As Integer = posArticle(articles, idArticle)
' mevcut değilse
If ipos = -1 Then Return 0
' ürün varsa - mümkünse stokunu değiştir
Dim unArticle As Article = CType(articles(ipos), Article)
' stok yeterli ise değiştirilir
If unArticle.stockactuel + mouvement >= 0 Then
unArticle.stockactuel += mouvement
Return 1
Else
Return 0
End If
End SyncLock
End Function
' anahtarıyla tanımlanan bir ürünü aranır
Private Function posArticle(ByVal listArticles As ArrayList, ByVal idArticle As Integer) As Integer
' [idArticle] ürününün listedeki konumunu döndürür veya bulunmazsa -1'i döndürür
Dim unArticle As Article
For i As Integer = 0 To listArticles.Count - 1
unArticle = CType(listArticles(i), Article)
If unArticle.id = idArticle Then
Return i
End If
Next
' bulunamadı
Return -1
End Function
End Class
End Namespace
Açıklamalar:
- Veri kaynağı, [ArrayList] türündeki özel alan [articles] ile simüle edilir
- Sınıf oluşturucu, varsayılan olarak veri kaynağında 4 madde oluşturur.
- Veri kaynağına eşzamanlı erişim sorunlarını önlemek için tüm veri erişim yöntemleri senkronize edilmiştir. Herhangi bir anda, belirli bir yönteme yalnızca tek bir iş parçacığı erişebilir.
- [posArticle] yöntemi, numarasıyla tanımlanan bir öğenin [ArrayList] kaynağındaki [0..N] konumunu öğrenmeyi sağlar. Makale mevcut değilse, yöntem -1 konumunu döndürür. Bu yöntem, diğer yöntemler tarafından tekrar tekrar kullanılır.
- [ajouteArticle] yöntemi, ürün listesine bir ürün eklemeye olanak tanır. Eklenen ürün sayısını 1 olarak döndürür. Ürün zaten mevcutsa, bir istisna atılır.
- [modifieArticle] yöntemi, mevcut bir ürünü düzenlemeye olanak tanır. Bu yöntem, ürün varsa değiştirilen ürün sayısını 1 olarak, yoksa 0 olarak döndürür.
- [supprimeArticle] yöntemi, mevcut bir ürünü silmeye yarar. Bu yöntem, ürün silindiğinde ürün sayısını 1 olarak, yoksa 0 olarak döndürür.
- [getAllArticles] yöntemi, tüm makalelerin listesini verir.
- [getArticleById] yöntemi, numarasıyla tanımlanan bir ürünü almayı sağlar. Ürün mevcut değilse [nothing] değeri döndürülür.
- Kodda gerçek anlamda bir zorluk yoktur. Okuyucunun kodu inceleyip anlamasına bırakıyoruz.
1.4.3.4. [dao] katmanının derlemesinin oluşturulması
Visual Studio projesi, [webarticles-dao.dll] derlemesini oluşturacak şekilde yapılandırılmıştır. Bu derleme, projenin [bin] klasöründe oluşturulur:
![]() | ![]() |
1.4.3.5. [dao] katmanının NUnit testleri
Java'da sınıflar, [Junit] çerçeve yapısı ile test edilir. .NET'te ise Nunit çerçeve yapısı aynı birim test olanaklarını sunar:

Test için Visual Studio projesinin yapısı şu şekildedir:

Açıklamalar:
- [tests] projesi, [bibliothèque de classes] türündedir
- [NUnit] testleri, [nunit.framework.dll] derlemesine bir referans gerektirir
- [NUnit] test sınıfı, Spring aracılığıyla test edilecek nesnenin bir örneğini alır. Ayrıca
- [bin] klasöründe, Spring sınıf dosyaları
- [References] içinde, [bin] klasöründeki [Spring-Core.dll] derlemesine bir referans bulunur
- [bin] klasöründe bir yapı,
- Test sınıfı, [dao] katmanındaki [webarticles-dao.dll] derlemesine ihtiyaç duyar. Bu derleme, [bin] klasörüne yerleştirilmiş ve referansı proje referanslarına eklenmiştir.
[NUnit] test sınıfı, [NUnit.Framework] ad alanındaki sınıflara erişim gerektirir. Bu nedenle, burada şu içe aktarma komutu bulunur:
[NUnit.Framework] ad alanı, projenin referanslarına eklenmesi gereken [nunit.framework.dll] "derlemesinde" bulunur:
![]() | ![]() |
[Nunit] kurulumu yapılmışsa, [nunit.framework.dll] derlemesi önerilen listede yer almalıdır. Derlemeyi projeye eklemek için üzerine çift tıklamanız yeterlidir:

[dao] katmanına ait [NUnit] test sınıfı şu şekilde olabilir:
Imports System
Imports System.Collections
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports System.Threading
Imports Spring.Objects.Factory.Xml
Imports System.IO
Namespace istia.st.articles.tests
<TestFixture()> _
Public Class NunitTestArticlesArrayList
' test edilecek nesne
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' Spring nesne oluşturucusunun bir örneğini alır
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config.xml", FileMode.Open))
' articlesdao nesnesinin örneklenmesi istenir
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
<Test()> _
Public Sub testGetAllArticles()
' görsel kontrol
listArticles()
End Sub
<Test()> _
Public Sub testClearAllArticles()
' tüm makaleler siliniyor
articlesDao.clearAllArticles()
' tüm makaleler isteniyor
Dim articles As IList = articlesDao.getAllArticles
' kontrol: sayı 0 olmalıdır
Assert.AreEqual(0, articles.Count)
End Sub
<Test()> _
Public Sub testAjouteArticle()
' tüm ürünlerin silinmesi
articlesDao.clearAllArticles()
' doğrulama: ürün tablosu boş olmalı
Dim articles As IList = articlesDao.getAllArticles
Assert.AreEqual(0, articles.Count)
' iki ürün ekleniyor
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' doğrulama: iki ürün olmalı
articles = articlesDao.getAllArticles
Assert.AreEqual(2, articles.Count)
' görsel kontrol
listArticles()
End Sub
<Test()> _
Public Sub testSupprimeArticle()
' tüm ürünlerin silinmesi
articlesDao.clearAllArticles()
' doğrulama: ürün tablosu boş olmalıdır
Dim articles As IList = articlesDao.getAllArticles
Assert.AreEqual(0, articles.Count)
' iki ürün ekleniyor
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' kontrol: 2 ürün bulunmalıdır
articles = articlesDao.getAllArticles
Assert.AreEqual(2, articles.Count)
' 4 numaralı ürün siliniyor
articlesDao.supprimeArticle(4)
' kontrol: 1 ürün kalmalıdır
articles = articlesDao.getAllArticles
Assert.AreEqual(1, articles.Count)
' gözle kontrol
listArticles()
End Sub
<Test()> _
Public Sub testModifieArticle()
' tüm maddelerin silinmesi
articlesDao.clearAllArticles()
' kontrol
Dim articles As IList = articlesDao.getAllArticles
Assert.AreEqual(0, articles.Count)
' 2 ürünün eklenmesi
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' doğrulama
articles = articlesDao.getAllArticles
Assert.AreEqual(2, articles.Count)
' 3 numaralı ürünün aranması
Dim unArticle As Article = articlesDao.getArticleById(3)
' doğrulama
Assert.AreEqual(unArticle.nom, "article3")
' 4. maddenin aranması
unArticle = articlesDao.getArticleById(4)
' doğrulama
Assert.AreEqual(unArticle.nom, "article4")
' 4. maddenin değiştirilmesi
articlesDao.modifieArticle(New Article(4, "article4", 44, 44, 44))
' doğrulama
unArticle = articlesDao.getArticleById(4)
Assert.AreEqual(unArticle.prix, 44, 0.000001)
' görsel kontrol
listArticles()
End Sub
<Test()> _
Public Sub testGetArticleById()
' madde silme
articlesDao.clearAllArticles()
' doğrulama
Dim articles As IList = articlesDao.getAllArticles
Assert.AreEqual(0, articles.Count)
' 2 maddenin eklenmesi
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' doğrulama
articles = articlesDao.getAllArticles
Assert.AreEqual(2, articles.Count)
' 3. maddenin aranması
Dim unArticle As Article = articlesDao.getArticleById(3)
' doğrulama
Assert.AreEqual(unArticle.nom, "article3")
' 4. maddenin aranması
unArticle = articlesDao.getArticleById(4)
' doğrulama
Assert.AreEqual(unArticle.nom, "article4")
End Sub
' ekran listesi
Private Sub listArticles()
Dim articles As IList = articlesDao.getAllArticles
For i As Integer = 0 To articles.Count - 1
Console.WriteLine(CType(articles(i), Article).ToString)
Next
End Sub
<Test()> _
Public Sub testArticleAbsent()
' tüm ürünlerin silinmesi
articlesDao.clearAllArticles()
' 1. madde arama
Dim article As article = articlesDao.getArticleById(1)
' doğrulama
Assert.IsNull(article)
' mevcut olmayan bir ürünün düzenlenmesi
Dim i As Integer = articlesDao.modifieArticle(New article(1, "1", 1, 1, 1))
' hiçbir satır değiştirilmedi
Assert.AreEqual(i, 0)
' mevcut olmayan bir maddenin silinmesi
i = articlesDao.supprimeArticle(1)
' hiçbir satır silinmedi
Assert.AreEqual(0, i)
End Sub
<Test()> _
Public Sub testChangerStockArticle()
' tüm makalelerin silinmesi
articlesDao.clearAllArticles()
' bir makale eklendi
Dim nbArticles As Integer = articlesDao.ajouteArticle(New Article(3, "article3", 30, 101, 3))
Assert.AreEqual(nbArticles, 1)
' bir makale eklendi
nbArticles = articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
Assert.AreEqual(nbArticles, 1)
' 100 konu oluşturuldu
Dim taches(99) As Thread
For i As Integer = 0 To taches.Length - 1
' i başlığı oluşturuluyor
taches(i) = New Thread(New ThreadStart(AddressOf décrémente))
' iş parçacığının adı belirleniyor
taches(i).Name = "tache_" & i
' i konulu başlığın yürütülmesi başlatılıyor
taches(i).Start()
Next
' tüm iş parçacıklarının bitmesini bekleme
For i As Integer = 0 To taches.Length - 1
taches(i).Join()
Next
' kontroller - 3 numaralı ürünün stokunda 1 adet olması gerekir
Dim unArticle As Article = articlesDao.getArticleById(3)
Assert.AreEqual(unArticle.nom, "article3")
Assert.AreEqual(1, unArticle.stockactuel)
' 4 numaralı ürünün stoğu azaltılır
Dim erreur As Boolean = False
Dim nbLignes As Integer = articlesDao.changerStockArticle(4, -100)
' kontrol: stokunda değişiklik olmamış olmalı
Assert.AreEqual(0, nbLignes)
' gözle kontrol
listArticles()
End Sub
Public Sub décrémente()
' iş parçacığı başlatıldı
System.Console.Out.WriteLine(Thread.CurrentThread.Name + " lancé")
' iş parçacığı stok miktarını azaltıyor
articlesDao.changerStockArticle(3, -1)
' iş parçacığı tamamlandı
System.Console.Out.WriteLine(Thread.CurrentThread.Name + " terminé")
End Sub
End Class
End Namespace
Açıklamalar:
- [IArticlesDao] arayüzünü, bu arayüzün uygulama sınıfından bağımsız olarak test edecek bir program yazmak istedik. Bu nedenle, test programından uygulama sınıfının adını gizlemek için Spring'i kullandık.
- <Setup()> öznitelik yöntemi, Spring'den test edilecek [articlesdao] nesnesine ilişkin bir referans alır. Bu nesne, aşağıdaki [spring-config.xml] dosyasında tanımlanmıştır:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoArrayList, webarticles-dao"/>
</objects>
Bu dosya, [IArticlesDao] arayüzünün [istia.st.articles.dao.ArticlesDaoArrayList] uygulama sınıfının adını ve [ webarticles-dao.dll]'te nerede bulunabileceğini belirtir. Örnekleme için parametre gerekmediğinden, burada herhangi bir parametre tanımlanmamıştır.
- Testlerin çoğu anlaşılması kolaydır. Okuyucunun yorumları okuması tavsiye edilir.
- [testChangerStockArticle] yöntemi ise biraz açıklama gerektirir. Bu yöntem, belirli bir ürünün stok miktarını azaltmakla görevli 100 iş parçacığı oluşturur.
<Test()> _
Public Sub testChangerStockArticle()
' tüm ürünlerin silinmesi
articlesDao.clearAllArticles()
' ürün ekleniyor
Dim nbArticles As Integer = articlesDao.ajouteArticle(New Article(3, "article3", 30, 101, 3))
Assert.AreEqual(nbArticles, 1)
' bir ürün eklendi
nbArticles = articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
Assert.AreEqual(nbArticles, 1)
' 100 iş parçacığı oluşturuldu
Dim taches(99) As Thread
For i As Integer = 0 To taches.Length - 1
' i başlığı oluşturuluyor
taches(i) = New Thread(New ThreadStart(AddressOf décrémente))
' iş parçacığının adı belirleniyor
taches(i).Name = "tache_" & i
' i konulu başlığın yürütülmesi başlatılıyor
taches(i).Start()
Next
' tüm iş parçacıklarının bitmesini bekleme
For i As Integer = 0 To taches.Length - 1
taches(i).Join()
Next
' kontroller - 3 numaralı ürünün stokta 1 adet olması gerekir
Dim unArticle As Article = articlesDao.getArticleById(3)
Assert.AreEqual(unArticle.nom, "article3")
Assert.AreEqual(1, unArticle.stockactuel)
' 4 numaralı ürünün stoğu azaltılıyor
Dim erreur As Boolean = False
Dim nbLignes As Integer = articlesDao.changerStockArticle(4, -100)
' kontrol: stokunda bir değişiklik olmamış olmalı
Assert.AreEqual(0, nbLignes)
' gözle kontrol
listArticles()
End Sub
Burada veri kaynağına eşzamanlı erişimlerin test edilmesi amaçlanmaktadır. Stok güncellemesinden sorumlu yöntem şöyledir:
Public Sub décrémente()
' iş parçacığı başlatıldı
System.Console.Out.WriteLine(Thread.CurrentThread.Name + " lancé")
' iş parçacığı stok miktarını azaltıyor
articlesDao.changerStockArticle(3, -1)
' iş parçacığı tamamlandı
System.Console.Out.WriteLine(Thread.CurrentThread.Name + " terminé")
End Sub
3 numaralı ürünün stokundan bir birim düşer. [testChangerStockArticle] yöntem koduna bakıldığında şunu görürüz:
- 3 numaralı ürünün stoğu 101 olarak başlatılır
- 100 iş parçacığı bu stoğu birer birer azaltacaktır
- bu nedenle, tüm iş parçacıklarının yürütülmesi tamamlandığında stok 1 olmalıdır
Ayrıca, yine bu yöntem içinde, 4 numaralı ürünün stokunu negatif bir değere getirmeye çalışıyoruz. Bu işlem başarısız olmalıdır.
[dao] katmanını test etmek için, [tests] projesinin [bin] klasöründe DLL ve [tests-webarticles-dao.dll] dosyalarını oluşturuyoruz:
![]() | ![]() |
Ardından, [Nunit-Gui] uygulamasını kullanarak bu DLL dosyasını yükleyip testleri gerçekleştiriyoruz:

Soldaki pencerede, test edilen yöntemlerin listesi görülmektedir. Her yöntemin adının önündeki noktanın rengi, yöntemin başarılı (yeşil) veya başarısız (kırmızı) olduğunu gösterir. Bu belgeyi ekranda görüntüleyen okuyucu, tüm testlerin başarılı olduğunu görebilir. Bundan sonra, [dao] katmanının çalışır durumda olduğunu varsayacağız.
1.4.4. [domain] katmanı
[domain] katmanı aşağıdaki öğeleri içerir:
-
[IArticlesDomain]: [domain] katmanına erişim arayüzü
-
[Achat]: bir satın alma işlemini tanımlayan sınıf
-
[Panier]: Alışveriş sepetini tanımlayan sınıf
-
[AchatsArticles]: [IArticlesDomain] arayüzünün uygulama sınıfı
[domain] katmanındaki [Visual Studio] çözümünün yapısı şöyledir:

Açıklamalar:
- [domain] projesi, [bibliothèque de classes] türündedir
- Sınıflar, [istia] klasörünü kök olarak alan bir dizin yapısına yerleştirilmiştir. Hepsi [istia.st.articles.domain] ad alanında yer almaktadır.
- [dao] katmanına ait DLL, yeni projenin [bin] klasörüne yerleştirilmiştir. Ayrıca, bu DLL, projeye referans olarak eklenmiştir.
1.4.4.1. [IArticlesDomain] arayüzü
[IArticlesDomain] arayüzü, [métier] katmanını [web] katmanından ayırır. [web] katmanı, bu arayüz aracılığıyla [métier/domain] katmanına erişir ve bunu gerçekleştiren sınıfla ilgilenmez. Arayüz, iş katmanına erişim için aşağıdaki eylemleri tanımlar:
Imports Article = istia.st.articles.dao.Article
Namespace istia.st.articles.domain
Public Interface IArticlesDomain
' yöntemler
Sub acheter(ByVal panier As Panier)
Function getAllArticles() As IList
Function getArticleById(ByVal idArticle As Integer) As Article
ReadOnly Property erreurs() As ArrayList
End Interface
End Namespace
ilişkili veri kaynağındaki [Article] nesne listesini döndürür | |
[idArticle] ile tanımlanan [Article] nesnesini döndürür | |
müşterinin sepetini, satın alınan ürünlerin stoklarından satın alınan miktarı düşerek onaylar - stok yetersizse başarısız olabilir | |
meydana gelen hataların listesini döndürür - hata yoksa boş |
1.4.4.2. [Achat] sınıfı
[Achat] sınıfı, müşterinin bir satın alma işlemini temsil eder:
Imports istia.st.articles.dao
Namespace istia.st.articles.domain
Public Class Achat
' özel alanlar
Private _article As article
Private _qte As Integer
' varsayılan oluşturucu
Public Sub New()
End Sub
' parametreli oluşturucu
Public Sub New(ByVal unArticle As article, ByVal qte As Integer)
' özellikler aracılığıyla
Me.article = unArticle
Me.qte = qte
End Sub
' satın alınan ürün
Public Property article() As article
Get
Return _article
End Get
Set(ByVal Value As article)
_article = Value
End Set
End Property
' satın alınan miktar
Public Property qte() As Integer
Get
Return _qte
End Get
Set(ByVal Value As Integer)
If Value < 0 Then
Throw New Exception("Quantité [" + Value.ToString + "] invalide")
End If
_qte = Value
End Set
End Property
' toplam satın alma tutarı
Public ReadOnly Property totalAchat() As Double
Get
Return _qte * _article.prix
End Get
End Property
' kimlik
Public Overrides Function ToString() As String
Return "[" + _article.ToString + "," + _qte.ToString + "]"
End Function
End Class
End Namespace
Açıklamalar:
- [Achat] sınıfı aşağıdaki özelliklere ve yöntemlere sahiptir:
satın alınan ürün | |
satın alınan miktar | |
satın alma tutarı | |
nesnenin kimlik dizesi |
- Bir satın alma işlemini tanımlayan [article, qte] özelliklerini başlatmaya yarayan bir oluşturucuya sahiptir.
1.4.4.3. [Panier] sınıfı
[Panier] sınıfı, müşterinin tüm satın alımlarını temsil eder:
Namespace istia.st.articles.domain
Public Class Panier
' özel alanlar
Private _achats As New ArrayList
Private _totalPanier As Double = 0
' varsayılan üretici
Public Sub New()
End Sub
' satın alma listesi
Public ReadOnly Property achats() As ArrayList
Get
Return _achats
End Get
End Property
' toplam satın alma tutarı
Public ReadOnly Property totalPanier() As Double
Get
Return _totalPanier
End Get
End Property
' yöntemler
Public Sub ajouter(ByVal unAchat As Achat)
' satın alma işleminin daha önce var olup olmadığı kontrol edilir
Dim iAchat As Integer = posAchat(unAchat.article.id)
If iAchat <> -1 Then
' bulundu
Dim achatCourant As Achat = CType(_achats(iAchat), Achat)
achatCourant.qte += unAchat.qte
Else
' bulunamadı
_achats.Add(unAchat)
End If
' sepet toplamı artırılıyor
_totalPanier += unAchat.totalAchat
End Sub
' bir ürünü sepetten çıkar
Public Sub enlever(ByVal idAchat As Integer)
' ürün aranıyor
Dim iachat As Integer = posAchat(idAchat)
' bulunduysa, kaldırılıyor
If iachat <> -1 Then
Dim achatCourant As Achat = CType(_achats(iachat), Achat)
' sepetten çıkarılır
_achats.RemoveAt(iachat)
' sepet toplamını azalt
_totalPanier -= achatCourant.totalAchat
End If
End Sub
Private Function posAchat(ByVal idArticle As Integer) As Integer
' satın alma listesinde bir satın alma arar
' listede bulunduğu konumu döndürür veya bulunmazsa -1 döndürür
Dim achatCourant As Achat
Dim trouvé As Boolean = False
Dim i As Integer = 0
While Not trouvé AndAlso i < _achats.Count
' mevcut satın alma
achatCourant = CType(_achats(i), Achat)
' aranan ürünle karşılaştırma
If achatCourant.article.id = idArticle Then
Return i
End If
'bir sonraki satın alma
i += 1
End While
' bulunamadı
Return -1
End Function
' kimlik işlevi
Public Overrides Function ToString() As String
Return _achats.ToString
End Function
End Class
End Namespace
Açıklamalar:
- [Panier] sınıfı aşağıdaki özelliklere ve yöntemlere sahiptir:
Müşterinin satın alma listesi - [Achat] türündeki nesnelerin listesi | |
alışveriş listesine bir satın alma ekler | |
idAchat ürününü alışveriş listesinden çıkar | |
sepet içindeki alışverişlerin toplam tutarı | |
sepetin kimlik dizesini döndürür |
- [posAchat] yöntemi, satın alınan ürünün numarasıyla tanımlanan bir satın alma işleminin satın alma listesindeki konumunu elde etmeyi sağlayan bir yardımcı yöntemdir. Alışveriş listesi, bir ürünün birden fazla kez satın alınması durumunda listede yalnızca bir sıra işgal edeceği şekilde yönetilir. Böylece bir satın alma işlemi, satın alınan ürünün numarasıyla tanımlanabilir. [posAchat] yöntemi, aranan satın alma işlemi mevcut değilse -1 değerini döndürür.
- [ajouter] yöntemi, satın alma listesine yeni bir satın alma işlemi ekler. Bu, satın alınan ürün listede henüz mevcut değilse listeye yeni bir giriş eklemek, halihazırda mevcutsa satın alınan miktarı artırmak anlamına gelir.
- [enlever] yöntemi, satın alma listesinden bir numara ile tanımlanan bir satın alma işlemini kaldırmaya yarar. Satın alma işlemi mevcut değilse, yöntem hiçbir hata mesajı vermez ve hiçbir işlem yapmaz.
- [totalPanier] satın alma tutarı, satın alma işlemlerinin eklenmesi ve çıkarılması sırasında korunur.
1.4.4.4. [AchatsArticles] sınıfı
[IArticlesDomain] arayüzü, aşağıdaki [AchatsArticles] sınıfı tarafından uygulanacaktır:
Imports istia.st.articles.dao
Namespace istia.st.articles.domain
Public Class AchatsArticles
Implements IArticlesDomain
'özel alanlar
Private _articlesDao As IArticlesDao
Private _erreurs As ArrayList
' yapıcı
Public Sub New(ByVal articlesDao As IArticlesDao)
_articlesDao = articlesDao
End Sub
' hata listesi
Public ReadOnly Property erreurs() As ArrayList Implements IArticlesDomain.erreurs
Get
Return _erreurs
End Get
End Property
' makaleler listesi
Public Function getAllArticles() As IList Implements IArticlesDomain.getAllArticles
' tüm ürünlerin listesi
Try
Return _articlesDao.getAllArticles
Catch ex As Exception
_erreurs = New ArrayList
_erreurs.Add("Erreur d'accès aux données : " + ex.Message)
End Try
End Function
' numarasına göre bir ürünü bul
Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDomain.getArticleById
' belirli bir ürün
Try
Return _articlesDao.getArticleById(idArticle)
Catch ex As Exception
_erreurs = New ArrayList
_erreurs.Add("Erreur d'accès aux données : " + ex.Message)
End Try
End Function
' sepet satın alma
Public Sub acheter(ByVal panier As Panier) Implements IArticlesDomain.acheter
' sepet satın alma - satın alınan ürünlerin stoklarından düşülmesi gerekir
_erreurs = New ArrayList
Dim achat As achat
Dim achats As ArrayList = panier.achats
For i As Integer = achats.Count - 1 To 0 Step -1
' i numaralı ürünün stokundan düşme
achat = CType(achats(i), achat)
Try
If _articlesDao.changerStockArticle(achat.article.id, -achat.qte) = 0 Then
' işlem gerçekleştirilemedi
_erreurs.Add("L'achat " + achat.ToString + " n'a pu se faire - Vérifiez les stocks")
Else
' işlem gerçekleştirildi - satın alma sepetten kaldırıldı
panier.enlever(achat.article.id)
End If
Catch ex As Exception
_erreurs = New ArrayList
_erreurs.Add("Erreur d'accès aux données : " + ex.Message)
End Try
Next
End Sub
End Class
End Namespace
Açıklamalar:
- Bu sınıf, [IArticlesDomain] arayüzünün dört yöntemini uygular. İki özel alanı vardır:
veri erişim nesnesi | |
olası hataların listesi. Bu sınıfa, [erreurs] adlı genel özellik aracılığıyla erişilebilir |
- Sınıfın bir örneğini oluşturmak için, verilere erişim sağlayan nesneyi belirtmek gerekir:
- [getAllArticles] ve [getArticleById] yöntemleri, [dao] katmanındaki aynı isimli yöntemlere dayanır
- [acheter] yöntemi, sepetin satın alınmasını onaylar. Bu onay işlemi, satın alınan ürünlerin stoklarından sayısını azaltmaktan ibarettir. Bir ürünün satın alınması, ancak stok durumu elverdiği takdirde mümkündür. Aksi takdirde satın alma reddedilir: ürün sepetinde kalır ve [erreurs] listesinde bir hata bildirilir. Onaylanan bir satın alma işlemi sepetten çıkarılır ve ilgili ürünün stokundan satın alınan miktar düşülür.
1.4.4.5. [domain] katmanının derlemesinin oluşturulması
Visual Studio projesi, [webarticles-domain.dll] derlemesini oluşturacak şekilde yapılandırılmıştır. Bu derleme, projenin [bin] klasöründe oluşturulur:
![]() | ![]() |
1.4.4.6. NUnit katmanının [domain] testleri
Test amaçlı Visual Studio projesinin yapısı şu şekildedir:

Açıklamalar:
- [tests] projesi, [bibliothèque de classes] türündedir
- [NUnit] testleri, [nunit.framework.dll] derlemesine bir referans gerektirir
- [NUnit] test sınıfı, Spring aracılığıyla test edilecek nesnenin bir örneğini alır. Ayrıca
- [bin] klasöründe, Spring sınıf dosyaları
- [References] içinde, [bin] klasöründeki [Spring-Core.dll] derlemesine bir referans
- [bin] klasöründe bir yapılandırma dosyası bulunur
- Test sınıfı, [dao] katmanındaki [webarticles-dao.dll] derlemesine ve [domain] katmanındaki [webarticles-domain.dll] derlemesine ihtiyaç duyar. Bunlar [bin] klasörüne yerleştirilmiş ve referansları proje referanslarına eklenmiştir.
[domain] katmanındaki NUnit test sınıfı şu şekilde olabilir:
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports istia.st.articles.domain
Imports Spring.Objects.Factory.Xml
Imports System.IO
Namespace istia.st.articles.tests
<TestFixture()> _
Public Class NunitTestArticlesDomain
' test edilecek nesne
Private articlesDomain As IArticlesDomain
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' Spring nesne oluşturucusunun bir örneği alınır
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config.xml", FileMode.Open))
' articles DAO nesnesinin örneklenmesi isteniyor
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
' ardından articlesdomain nesnesinin örneğini oluşturulur
articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDomain)
End Sub
<Test()> _
Public Sub getAllArticles()
' görsel kontrol
listArticles()
End Sub
<Test()> _
Public Sub getArticleById()
' makalelerin silinmesi
articlesDao.clearAllArticles()
' kontrol
Dim articles As IList = articlesDomain.getAllArticles
Assert.AreEqual(0, articles.Count)
' 2 ürünün eklenmesi
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' kontrol
articles = articlesDomain.getAllArticles
Assert.AreEqual(2, articles.Count)
' 3. maddenin aranması
Dim unArticle As Article = articlesDomain.getArticleById(3)
' doğrulama
Assert.AreEqual(unArticle.nom, "article3")
' 4. maddenin aranması
unArticle = articlesDao.getArticleById(4)
' doğrulama
Assert.AreEqual(unArticle.nom, "article4")
End Sub
<Test()> _
Public Sub acheterPanier()
' ürünlerin silinmesi
articlesDao.clearAllArticles()
' doğrulama
Dim articles As IList = articlesDomain.getAllArticles
Assert.AreEqual(0, articles.Count)
' 2 ürünün eklenmesi
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' doğrulama
articles = articlesDomain.getAllArticles
Assert.AreEqual(2, articles.Count)
' iki ürün içeren bir sepet oluşturma
Dim panier As New panier
panier.ajouter(New Achat(New Article(3, "article3", 30, 30, 3), 10))
panier.ajouter(New Achat(New Article(4, "article4", 40, 40, 4), 10))
' doğrulamalar
Assert.AreEqual(700, panier.totalPanier, 0.000001)
Assert.AreEqual(2, panier.achats.Count)
' sepetin onaylanması
articlesDomain.acheter(panier)
' doğrulamalar
Assert.AreEqual(0, articlesDomain.erreurs.Count)
Assert.AreEqual(0, panier.achats.Count)
' 3. ürün arama
Dim unArticle As Article = articlesDomain.getArticleById(3)
' doğrulama
Assert.AreEqual(unArticle.stockactuel, 20)
' ürün arama 4
unArticle = articlesDao.getArticleById(4)
' doğrulama
Assert.AreEqual(unArticle.stockactuel, 30)
' yeni sepet
panier.ajouter(New Achat(New Article(3, "article3", 30, 30, 3), 100))
' sepeti onayla
articlesDomain.acheter(panier)
' doğrulamalar
Assert.AreEqual(1, articlesDomain.erreurs.Count)
' ürün arama 3
unArticle = articlesDomain.getArticleById(3)
' doğrulama
Assert.AreEqual(unArticle.stockactuel, 20)
End Sub
<Test()> _
Public Sub testRetirerAchats()
' ARTICLES içeriğinin silinmesi
articlesDao.clearAllArticles()
' ARTICLES tablosunu okur
Dim articles As IList = articlesDao.getAllArticles()
Assert.AreEqual(0, articles.Count)
' ekleme
Dim article3 As New Article(3, "article3", 30, 30, 3)
articlesDao.ajouteArticle(article3)
Dim article4 As New Article(4, "article4", 40, 40, 4)
articlesDao.ajouteArticle(article4)
' ARTICLES tablosunu okur
articles = articlesDomain.getAllArticles()
Assert.AreEqual(2, articles.Count)
' iki ürün içeren bir sepet oluşturuluyor
Dim monPanier As New Panier
monPanier.ajouter(New Achat(article3, 10))
monPanier.ajouter(New Achat(article4, 10))
' kontroller
Assert.AreEqual(700.0, monPanier.totalPanier, 0.000001)
Assert.AreEqual(2, monPanier.achats.Count)
' daha önce satın alınmış bir ürünü ekleme
monPanier.ajouter(New Achat(article3, 10))
' doğrulamalar
' toplam 1000'e çıkarılmalıdır
Assert.AreEqual(1000.0, monPanier.totalPanier, 0.000001)
' sepette hala 2 ürün var
Assert.AreEqual(2, monPanier.achats.Count)
' 3 numaralı ürünün adedi 20'ye değiştirilmelidir
Dim unAchat As Achat = CType(monPanier.achats(0), Achat)
Assert.AreEqual(20, unAchat.qte)
' 3 numaralı ürün sepetten çıkarılsın
monPanier.enlever(3)
' kontroller
' toplam tutar 400'e ayarlanmalı
Assert.AreEqual(400.0, monPanier.totalPanier, 0.000001)
' Sepette sadece 1 ürün olmalı
Assert.AreEqual(1, monPanier.achats.Count)
' Bu, 4 numaralı ürün olmalı
Assert.AreEqual(4, CType(monPanier.achats(0), Achat).article.id)
End Sub
' ekran listesi
Private Sub listArticles()
Dim articles As IList = articlesDomain.getAllArticles
For i As Integer = 0 To articles.Count - 1
Console.WriteLine(CType(articles(i), Article).ToString)
Next
End Sub
End Class
End Namespace
Yorumlar:
- [IArticlesDomain] arayüzünü, bu arayüzün uygulama sınıfından bağımsız olarak test edecek bir program yazmak istedik. Bu nedenle, test programından uygulama sınıfının adını gizlemek için Spring'i kullandık.
- <Setup()> öznitelik yöntemi, Spring'den test edilecek [articlesdomain] ve [articlesdao] nesnelerine ilişkin bir referans alır. Bu nesneler, aşağıdaki [spring-config.xml] dosyasında tanımlanmıştır:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoArrayList, webarticles-dao" />
<object id="articlesdomain" type="istia.st.articles.domain.AchatsArticles, webarticles-domain">
<constructor-arg index="0">
<ref object="articlesdao" />
</constructor-arg>
</object>
</objects>
Bu dosyada şunlar belirtilmiştir:
- (devamı)
- [articlesdao] tekil nesnesi için, [istia.st.articles.dao.ArticlesDaoArrayList] uygulama sınıfının adı ve [ webarticles-dao.dll]'te bulunabileceği yer belirtilmiştir. Nesne oluşturma işlemi parametre gerektirmediğinden, burada herhangi bir parametre tanımlanmamıştır.
- [articlesdomain] singleton'u için, [istia.st.articles.domain.AchatsArticles] uygulama sınıfının adı ve bunun bulunduğu yer [ webarticles-domain.dll]'tir. [AchatsArticles] sınıfı, tek parametreli bir oluşturucuya sahiptir: [dao] katmanına erişimi yöneten singleton. Burada bu singleton, daha önce tanımlanan [articlesdao] singletonu olarak tanımlanmıştır.
- Test sınıfı, test edilecek [articlesdomain] sınıfının bir örneğini ve veri erişim sınıfı [articlesdao]'in bir örneğini alır. Bu son nokta tartışmalıdır. Teorik olarak, test sınıfının, bilmesi bile gerekmeyen [dao] katmanına erişime ihtiyacı olmamalıdır. Burada, bu "etik" kuralı göz ardı ettik; bu kurala uymak için [IArticlesDomain] arayüzümüzde yeni yöntemler oluşturmamız gerekirdi.
[domain] katmanını test etmek için, [tests] projesinin [bin] klasöründe DLL ve [tests-webarticles-domain.dll] dosyalarını oluşturuyoruz:
![]() | ![]() |
Ardından, [Nunit-Gui] uygulamasını kullanarak bu DLL dosyasını yükleyip testleri gerçekleştiriyoruz:

Bu belgeyi ekranda görüntüleyen okuyucu, tüm testlerin başarılı olduğunu görebilir. Bundan sonra, [domain] katmanının çalışır durumda olduğunu varsayacağız.
1.4.5. Sonuç
Şu üç katmanlı web uygulamasını oluşturmak istediğimizi hatırlayalım:
![]() |
Uygulamamızın M modeli MVC artık yazılmış ve test edilmiştir. Bu model bize iki dosyada sunulmaktadır: DLL ve [webarticles-dao.dll, webarticles-domain.dll]. Artık son katmana, yani C denetleyicisini ve V görünümlerini içeren [web] katmanına geçebiliriz. Öncelikle, [Développement WEB avec ASP.NET 1.1 ] belgesinde sunulan bir yöntemi ele alacağız
- C denetleyicisi, iki dosya tarafından sağlanır: [global.asax, main.aspx]
- V görünümleri ise aspx sayfaları tarafından sağlanmaktadır
1.5. [web] katmanı
Web uygulamasının MVC mimarisi şu şekilde olacaktır:
![]() |
[domain] iş sınıfları, [dao] veri erişim sınıfları ve veri kaynağı | |
ASPX sayfaları | |
tüm HTTP istemci istekleri aşağıdaki iki denetleyiciden geçer: global.asax: uygulamanın ilk başlatılmasıyla ilgili olayları yönetir main.aspx: her bir müşterinin isteğini ayrı ayrı işler |
1.5.1. Görünümler
Görseller, belgenin başında sunulanlarla aynıdır:
liste.aspx | Görünümler, uygulamanın [vues] klasöründe toplanmıştır ![]() | |
infos.aspx | ||
panier.aspx | ||
paniervide.aspx | ||
erreurs.aspx |
1.5.2. Denetleyiciler
Daha önce belirtildiği gibi, denetleyici iki unsurdan oluşacaktır:
- [global.asax,global.asax.vb]: esas olarak uygulamayı başlatmak ve farklı istemciler arasında paylaşılacak tüm verileri uygulamanın bağlamına yerleştirmek için kullanılır
- [main.aspx, main.aspx.vb]: Asıl denetleyici; istemcilerin HTTP isteklerini işleyen bileşendir.
Müşterilerin farklı istekleri [main.aspx] denetleyicisine yönlendirilecek ve müşterinin talep ettiği eylemi belirten [action] adlı bir parametre içerecektir:
istek | anlam | denetleyicinin eylemi | olası yanıtlar |
müşteri şunların listesini istiyor ürünlerin listesini | - iş katmanından ürün listesini talep ediyor katmanından | katmanından ürün listesini talep ediyor - [ERREURS] | |
Müşteri, görünümde görüntülenen görüntülenen ürünlerden biri hakkında [LISTE] | - iş katmanından ürünü talep eder | - [INFOS] - [ERREURS] | |
müşteri bir ürün satın alır | - iş katmanından ürünü talep eder ve ürünü müşterinin sepetine ekler | - [INFOS] (miktar hatası varsa) - [LISTE] hata yoksa | |
müşteri sepetinden bir sepetinden bir ürünü silmek istiyor | - oturumdaki sepeti alır ve değiştirir | - [PANIER] - [PANIERVIDE] - [ERREURS] | |
müşteri, sepetini | - oturumdan alışveriş sepetini alır | - [PANIER] - [PANIERVIDE] - [ERREURS] | |
müşteri alışverişini tamamladı ve ödeme aşamasına geçiyor | - satın alınan ürünlerin stok bilgilerini satın alınan ürünlerin stokları güncellenir - satın alımı onaylanan ürünlerin silinir | - [LISTE] - [ERREURS] |
1.5.3. Uygulamanın yapılandırılması
Uygulamayı, aşağıdaki gibi değişikliklere karşı olabildiğince esnek olacak şekilde yapılandırmaya çalışacağız:
- farklı görünümlerin URL'lerinin değiştirilmesi
- [IArticlesDao] ve [IArticlesDomain] arayüzlerini uygulayan sınıfların değiştirilmesi
- SGBD, veritabanı ve ürün tablosundaki değişiklikler
1.5.3.1. URL değişiklikleri
URL görünümlerinin adları, diğer bazı parametrelerle birlikte [web.config] uygulama yapılandırma dosyasına eklenecektir:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
..
<appSettings>
<add key="urlMain" value="/webarticles/main.aspx"/>
<add key="urlInfos" value="vues/infos.aspx"/>
<add key="urlErreurs" value="vues/erreurs.aspx"/>
<add key="urlListe" value="vues/liste.aspx"/>
<add key="urlPanier" value="vues/panier.aspx"/>
<add key="urlPanierVide" value="vues/paniervide.aspx"/>
</appSettings>
</configuration>
1.5.3.2. Arayüzlerin uygulama sınıflarındaki değişiklik
Üç katmanlı mimarilerde, katmanlar birbirinden bağımsız olmalıdır. Bu bağımsızlık şu şekilde sağlanır:
- katmanlar, somut sınıflar aracılığıyla değil, arayüzler aracılığıyla birbirleriyle iletişim kurar
- bir katmanın kodu, kullanmak amacıyla asla başka bir katmanın sınıfını kendisi örneklendirmez. Sadece kullanmak istediği katmanın arayüzünün bir uygulama örneğini harici bir araçtan (burada Spring) ister. Bunun için, uygulama sınıfının adını bilmesine gerek olmadığını, sadece referansını almak istediği Spring singleton'unun adını bilmesi yeterli olduğunu biliyoruz.
Uygulamamızda Spring, web uygulamasının [web.config] dosyasında şu şekilde yapılandırılacaktır:
<?xml version="1.0" encoding="iso-8859-1" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
<resource uri="config://spring/objects" />
</context>
<objects>
<object id="articlesDao" type="istia.st.articles.dao.ArticlesDaoArrayList, webarticles-dao" />
<object id="articlesDomain" type="istia.st.articles.domain.AchatsArticles, webarticles-domain">
<constructor-arg index="0">
<ref object="articlesDao" />
</constructor-arg>
</object>
</objects>
</spring>
<appSettings>
<add key="urlMain" value="/webarticles/main.aspx"/>
<add key="urlInfos" value="vues/infos.aspx"/>
<add key="urlErreurs" value="vues/erreurs.aspx"/>
<add key="urlListe" value="vues/liste.aspx"/>
<add key="urlPanier" value="vues/panier.aspx"/>
<add key="urlPanierVide" value="vues/paniervide.aspx"/>
</appSettings>
</configuration>
[métier] katmanına erişmek için, [web] katmanındaki bir sınıf, [articlesDomain] singletonunu talep edebilir. Spring, bu durumda [istia.st.articles.domain.AchatsArticles] türünde bir nesne oluşturacaktır. Bu örneklendirme için, [articlesDao] türünde bir nesneye, yani [istia.st.articles.dao.ArticlesDaoArrayList] türünde bir nesneye ihtiyaç duyar. Spring daha sonra böyle bir nesne oluşturacaktır. İşlemin sonunda, [articlesDomain] singletonunu talep eden [web] katmanı, kendisini veri kaynağına bağlayan tüm zincire sahip olacaktır:
![]() |
1.5.3.3. SGBD veya veritabanıyla ilgili değişiklikler
SGBD'in bulunmadığı bir test uygulamasında olduğumuz için bu konu burada göz ardı edilecektir. İkinci aşamada, SGBD'e dayanan bir [dao] katmanının uygulanmasını ele alacağız.
1.5.4. <asp:> etiket kütüphanesi
Hata listesini görüntüleyen [ERREURS] görünümünü ele alalım:

[ERREURS] görünümü, [main.aspx] denetleyicisinin istek bağlamına [context.Items("erreurs")] adıyla yerleştirdiği hata listesini görüntülemekle görevlidir. Böyle bir sayfayı yazmanın birkaç yolu vardır. Burada sadece hataların görüntülenmesi kısmıyla ilgileniyoruz.
Hatırlayalım ki, bir ASPX sayfası, bir HTML sunum bölümü ve sunum bölümünün görüntülemesi gereken verileri hazırlayan bir .NET kod bölümünden oluşur. Bu iki bölüm, aynı [aspx] dosyasında (WebMatrix çözümü) veya iki ayrı dosyada bulunabilir: sunum için [aspx], kod için [aspx.vb]. İkinci çözüm, Visual Studio’da kullanılan çözümdür. İşleri daha da karmaşık hale getiren bir diğer husus ise, HTML sunum bölümünün de .NET kodunu içerebilmesidir; bu durum, [contrôleur] ve [présentation] arasındaki ayrımı bulanıklaştırma eğilimindedir. Bu çözüm genellikle kesinlikle tavsiye edilmez. [présentation] bölümündeki tüm kodu kaldırmak için etiket kütüphaneleri oluşturulması gerekmiştir. Bu kütüphaneler, kodu HTML etiketlerine benzer etiketler şeklinde “gizler”. [ERREURS] sayfası için iki olası çözüm sunuyoruz.
İlk çözümümüzde, sayfanın [présentation] bölümünde .NET kodu kullanılmaktadır. ASPX sayfası, [erreurs.aspx.vb] denetleyici bölümünde istek içindeki hata listesini alır:
Protected erreurs As ArrayList
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
' hatalar alınır
erreurs = CType(context.Items("erreurs"), ArrayList)
End Sub
ve ardından [présentation, erreurs.aspx] bölümünde görüntüler:
<h2>Les erreurs suivantes se sont produites :</h2>
<ul>
<%
for i as integer=0 to erreurs.count-1
response.write("<li>" & erreurs(i).ToString & "</li>")
next
%>
</ul>
İkinci çözüm, ASP.NET'in <asp:> etiket kütüphanesindeki <asp:repeater> etiketini kullanır. ASPX sayfasını grafiksel olarak oluşturursak, bu etiket tasarım formuna sürüklenebilen bir sunucu bileşeni olarak kullanılabilir. ASPX kodunu elle yazarsak, buna etiket kütüphanesi denebilir.
<asp:> etiket kütüphanesi kullanıldığında, önceki [ERREURS] görünümündeki ASPX kodu şu şekilde olur:
<asp:Repeater id="rptErreurs" runat="server">
<HeaderTemplate>
<h3>Les erreurs suivantes se sont produites :
</h3>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li>
<%# Container.DataItem %>
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
Etiket
etiketi, bir veri kaynağındaki farklı öğeler üzerinde HTML desenini tekrarlamak için kullanılır. Etiketin farklı öğeleri şunlardır:
veri kaynağındaki öğeler görüntülenmeden önce gösterilecek HTML deseni | |
veri kaynağındaki her bir öğe için tekrarlanacak HTML deseni. [<%# Container.DataItem %>] ifadesi, veri kaynağındaki geçerli öğenin değerini görüntülemek için kullanılır | |
HTML kalıbı, veri kaynağındaki öğeler görüntülendikten sonra görüntülenmelidir |
Veri kaynağı genellikle sayfanın [contrôleur] bölümünde etiketle ilişkilendirilir:
Protected WithEvents rptErreurs As System.Web.UI.WebControls.Repeater
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
..
' hataları rptErreurs ile ilişkilendiriliyor
With rptErreurs
.DataSource = context.Items("erreurs")
.DataBind()
End With
End Sub
Veri kaynağı önceden biliniyorsa (örneğin mevcut bir veritabanı), bu bağlantı sayfa tasarımı aşamasında da kurulabilir.
Görünümlerimizde, bir veri kaynağını tablo biçiminde görüntülemeye olanak tanıyan başka bir etiket kullanacağız: <asp:datagrid>.
1.5.5. [webarticles] uygulamasının Visual Studio çözüm yapısı
Bir web uygulaması, çok sayıda öğeden oluşan bir yapbozdur. Uygulamaya bir mimari kazandırmak genellikle bu öğelerin sayısını artırır. MVC altında yer alan [webarticles] uygulamasının yapısı şu şekildedir:
![]() | ![]() | ![]() |
![]() |
Açıklamalar:
- [web] projesi, mantıken beklenebileceği gibi [Application web ASP.NET] türü değil, [bibliothèque de classes] türündedir. [Application web ASP.NET] türü, geliştirme makinesinde veya uzak bir makinede IIS web sunucusunun bulunmasını gerektirir. IIS sunucusu, Windows XP Aile Sürümü makinelerde standart olarak bulunmaz. Oysa birçok PC, bu sürümle birlikte satılmaktadır. Windows XP kullanan okuyucuların incelenen uygulamayı çalıştırabilmelerini sağlamak amacıyla, Microsoft'tan ücretsiz olarak temin edilebilen Cassini Web sunucusunu (bkz. ek) kullanacağız ve [Application web ASP.NET] projesini bir [bibliothèque de classes] projesiyle değiştireceğiz. Bu durum, eklerde açıklanan bazı dezavantajlara yol açmaktadır.
- Uygulama tarafından kullanılan DLL dosyaları şunlardır:
veri erişim katmanındaki sınıfları bir araya getirir | |
iş mantığı katmanındaki sınıfları bir araya getirir | |
web, domain ve DAO katmanlarını entegre etmemizi sağlayan Spring sınıflarını içerir | |
günlük sınıfları - Spring tarafından kullanılır |
Bu DLL dosyaları, [bin] klasörüne yerleştirilir ve proje referanslarına eklenir.
1.5.6. ASPX görünümleri
Daha önce önerildiği gibi, ASPX görünümlerimizde <asp:> etiket kütüphanesini kullanacağız.
1.5.6.1. [entete.ascx] kullanıcı bileşeni
Farklı görünümler arasında bir tutarlılık sağlamak amacıyla, bu görünümler uygulama adını ve menüyü gösteren aynı başlığı paylaşacaktır:
![]() | ![]() |
Menü dinamiktir ve denetleyici tarafından belirlenir. Denetleyici, ASPX sayfasına iletilen isteğe, değeri Hashtable() türünde bir dizi olan "actions" anahtar özniteliğini ekler. Bu dizinin her bir elemanı, başlıktaki menü seçeneklerinden birini oluşturmaya yarayan bir sözlüktür. Her sözlük iki anahtara sahiptir:
- href: menü seçeneğiyle ilişkili URL
- link: menü metni
Başlığı bir kullanıcı denetimi haline getireceğiz. Bir kullanıcı denetimi, sayfanın bir bölümünü (görünüm ve ilgili kod) daha sonra diğer sayfalarda yeniden kullanılabilen bir bileşene sığdırır. Burada, [entete] bileşenini uygulamanın diğer görünümlerinde yeniden kullanmak istiyoruz. Görünüm kodu [entete.ascx]'te, ilgili kontrol kodu ise [entete.ascx.vb]'te yer alacaktır. Görünüm kodu, menü seçenekleri tablosunu görüntülemek için bir <asp:repeater> bileşeni kullanacaktır:
![]() |
n° | type | nom | rôle |
1 | tekrarlayıcı | rptMenu veri kaynağı: iki anahtarlı bir sözlük tablosu iki anahtarlı: href, bağlantı | menü seçeneklerini görüntüle |
Sayfanın görüntüleme kodu şu şekilde olacaktır:
Yorumlar:
- [repeater] bileşeni 6-14. satırlarda tanımlanmıştır
- tekrarlayıcıyla ilişkili veri kaynağındaki her bir öğe, iki anahtarlı bir sözlükten oluşur: href - 9. satır ve link - 10. satır
İlgili kontrol kodu şu şekilde olacaktır:
Açıklamalar:
- [EnteteWebArticles] türündeki bileşen, 7. satırda yer alan, yazma erişimi olan [actions] adlı bir genel özelliğe sahiptir
- Bu özellik, [rptMenu] adlı <asp:repeater> bileşenine (satır 10) uygulama denetleyicisi tarafından hesaplanan seçenekler tablosunu (satır 11-12) atamaya olanak tanır.
Uygulamanın diğer görünümleri, [entete.ascx] tarafından tanımlanan başlığı kullanacaktır. Örneğin, [erreurs.aspx] sayfası, aşağıdaki kod yardımıyla bu başlığı dahil edecektir:
Açıklamalar:
- 1. satır, <WA:entete> etiketinin [entete.ascx] dosyası ile tanımlanan bileşene eşleştirilmesi gerektiğini belirtir. [TagPrefix] ve [TagName] öznitelikleri serbesttir.
- Bu işlem tamamlandıktan sonra, bileşenin sayfanın sunum koduna eklenmesi 9. satırla gerçekleştirilir. Çalıştırma sırasında bu etiket, onu içeren ASPX sayfasının koduna, [entete.ascx] sayfasının kodunu dahil edecektir. [erreurs.aspx.vb] kontrol kodu, bu bileşeni başlatacaktır. Bunu şu şekilde yapabilir:
Açıklamalar:
- 6. satır, oluşturulan bileşenin türü olan [EnteteWebArticles] türünde bir nesne oluşturur
- 11. satır, bu nesnenin [actions] özelliğini başlatır
1.5.6.2. [liste.aspx] görünümü
1.5.6.2.1. Introduction
Bu görünüm, satışa sunulan ürünlerin listesini gösterir:
![]() | ![]() |
Bu görünüm, /main?action=liste veya /main?action=validationpanier sorgusunun ardından görüntülenir. Denetleyicinin sorgu öğeleri şunlardır:
Hashtable() nesnesi - menü seçenekleri tablosu | |
ArrayList [Article] türündeki nesneler | |
String nesnesi - sayfanın altında görüntülenecek mesaj |
Makaleler tablosundaki (HTML) her bir bağlantı ([Infos]), URL biçiminde bir [?action=infos&id=ID] değerine sahiptir; burada ID, görüntülenen makalenin id alanıdır.
1.5.6.2.2. Sayfanın bileşenleri
![]() |
n° | type | nom | rôle |
kullanıcı bileşeni | başlık | başlığı göster | |
DataGrid | DataGridArticles 3 - ilgili sütun: başlık: Ad, alan: ad 4 - ilgili sütun: başlık: Fiyat, alan: fiyat 5 - köprü sütunu: metin: Bilgiler, URL alanı: id, URL biçimi: /webarticles/main.aspx?action=infos&id={0} | satıştaki ürünleri göster | |
etiket | lblMessage | mesajı görüntüle |
Bu özellikleri tanımlamak için izlenecek adımları hatırlayalım:
- Visual Studio'da, [DataGrid] öğesini seçerek özellikler sayfasına erişin:

- Yukarıdaki [Mise en forme automatique] bağlantısını kullanarak görüntülenen tablonun biçimini yönetebiliriz
- ve içeriğini yönetmek için [Générateur de propriétés] bağlantısını kullanın
1.5.6.2.3. Görselleştirme kodu [liste.aspx]
Açıklamalar:
- 9. satır sayfanın başlığını tanımlar
- 12-24. satırlar, [DataGrid]'in özelliklerini tanımlar
- 26. satır, [lblMessage] etiketini tanımlar
1.5.6.2.4. Denetleyici kodu [liste.aspx.vb]
Açıklamalar:
- Sayfanın bileşenleri 13-15. satırlarda yer almaktadır. Diğer bileşenlerde buna gerek kalmazken, burada [new] operatörü ile [EnteteWebArticles] nesnesini oluşturmamız gerektiği unutulmamalıdır. Bu nesne açıkça oluşturulmasaydı, [entete] nesnesinin hiçbir şeye referans vermediğini belirten bir yürütme hatası alınıyordu. Bu konunun daha ayrıntılı incelenmesi gerekirdi. Ancak incelenmemiştir.
- sayfanın [entete] bileşenini başlatmak için başlık menüsünün seçenek tablosu bağlamdan alınır - 20. satır
- ürün listesi, [entete] bileşenini başlatmak için bağlamdan alınır – satır 22
- [DataGridArticles] bileşenini başlatmak için - satır 24-27
- [lblMessage] bileşeni, bağlama yerleştirilen bir mesajla başlatılır - satır 29
1.5.6.3. [infos.aspx] görünümü
1.5.6.3.1. Introduction
Bu sayfa, bir ürünle ilgili bilgileri gösterir ve ürünün satın alınmasına da olanak tanır:

Bu sayfa, /main?action=infos&id=ID isteği veya satın alınan miktar hatalı olduğunda /main?action=achat&id=ID isteği sonrasında görüntülenir. Denetleyicinin isteğindeki öğeler şunlardır:
Hashtable() nesnesi - menü seçenekleri tablosu | |
[Article] türü nesne - görüntülenecek ürün | |
String nesnesi - miktar hatası durumunda görüntülenecek mesaj | |
String nesnesi - giriş alanında görüntülenecek değer [Qte] |
[msg] ve [qte] alanları, miktarda giriş hatası olması durumunda kullanılır:

Bu sayfa, [Acheter] düğmesi ile gönderilen bir form içerir. URL'in hedefi POST'tir; [?action=achat&id=ID] ise satın alınan ürünün kimliği olan ID'tir.
1.5.6.3.2. Sayfanın bileşenleri
![]() |
n° | type | nom | rôle |
kullanıcı bileşeni | başlık | başlığı göster | |
sabit | litID | ürün numarasını göster | |
DataGrid | DataGridArticle 3 - ilgili sütun: başlık: Ad, alan: ad 4 - ilişkili sütun: başlık: Fiyat, alan: fiyat 5 - ilişkili sütun: başlık: Mevcut stok, alan: stockActuel 6 - ilişkili sütun: başlık: Minimum stok, alan: stockMinimum | bir ürünü görüntüle | |
HTML Gönder | formu gönder | ||
HTML Giriş runat=sunucu | txtQte | satın alınan miktarı girin | |
etiket | lblMsgQte | olası hata mesajı |
1.5.6.3.3. Görüntüleme kodu [infos.aspx]
Yorumlar:
- başlık sayfaya dahil edilmiştir - 9. satır
- [litId] sabiti 10. satırda tanımlanmıştır
- DataGrid ve [DataGridArticles], 12-34. satırlarda tanımlanmıştır
- form, 36-46. satırlarda tanımlanmıştır. Türü POST'tir.
- POST'in hedefi, [strAction] değişkeni tarafından sağlanır - 36. satır. Bu değişken, denetleyici tarafından tanımlanmalıdır.
- Satın alınan miktar giriş alanı 41. satırda tanımlanmıştır. Bu, bir HTML sunucu bileşenidir (runat=server). Kod tarafında, bu alana bir nesne aracılığıyla erişilir.
- 42. satır, girilen miktarla ilgili olası bir hata mesajını içerecek olan [lblMsgQte] etiketini tanımlar
1.5.6.3.4. [infos.aspx.vb] kontrol kodu
Açıklamalar:
- Sayfanın bileşenleri 10-14. satırlarda tanımlanmıştır
- Sınıf, formdaki POST öğesinin hedefini tanımlamak için kullanılan [strAction] adlı bir genel özellik tanımlar - 17-25. satırlar
- Görüntülenecek makale, uygulama bağlamından alınır - satır 30
- başlık menüsünün seçenek tablosu, sayfanın [entete] bileşenini başlatmak için bağlamdan alınır - satır 32
- 33-39. satırlar: [DataGridArticle] bileşeni, yalnızca 30. satırda alınan ürünü içeren [ArrayList] türündeki bir veri kaynağına bağlanır
- [lblMsgQte, txtQte] bileşenleri, bağlamdan alınan bilgilerle başlatılır - satır 42-45
- [straction] özelliği de bağlamdan alınan bir bilgiyle başlatılır – satır 47. Bu değişken, sayfada bulunan HTML formunun [action] özniteliğini oluşturmak için kullanılır:
1.5.6.4. [panier.aspx] görünümü
1.5.6.4.1. Introduction
Bu görünüm, sepetin içeriğini gösterir:

Bu görünüm, /main?action=panier veya /main?action=retirerachat&id=ID isteği sonrasında görüntülenir. Denetleyicinin isteğindeki öğeler şunlardır:
Hashtable() nesnesi - menü seçenekleri tablosu | |
[Panier] türü nesne - görüntülenecek alışveriş sepeti |
Sepetteki satın alma işlemlerinin HTML tablosundaki her bir [Retirer] bağlantısı, URL biçiminde bir [?action=retirerachat&id=ID]'e sahiptir; burada ID, sepetten çıkarılmak istenen ürünün [id] alanıdır.
1.5.6.4.2. Sayfanın bileşenleri
![]() |
n° | type | nom | rôle |
kullanıcı bileşeni | başlık | başlığı göster | |
DataGrid | DataGridAchats 3- ilgili sütun - başlık: Ürün, alan: ad 4 - ilişkili sütun - başlık: Adet, alan: adet 5 - ilişkili sütun - başlık: Fiyat, alan: fiyat 6 - ilişkili sütun - başlık: Toplam, alan: toplam, biçimlendirme {0:C} 7 - köprü sütunu - Metin: İade, URL: id, URL biçimi: /webarticles/main.aspx?action=retirerachat&id={0} | satın alınan ürünlerin listesini göster | |
etiket | lblTotal | ödenecek tutarı göster |
1.5.6.4.3. Görüntüleme kodu [panier.aspx]
Açıklamalar
- 9. satır başlığı içerir
- 12-27. satırlarda, [DataGridAchats] bileşeni tanımlanmıştır
- 29. satırda, [lblTotal] bileşeni tanımlanmıştır
1.5.6.4.4. [panier.aspx.vb] kontrol kodu
Açıklamalar:
- Sayfanın bileşenleri 11-13. satırlarda tanımlanmıştır
- [entete] bileşeninin başlatılması, daha önce incelenen sayfalardakiyle aynıdır - satır 17
- Görüntülenecek alışveriş sepeti oturumdan alınır - 19. satır
- [DataGridAchats] bileşeni kullanılarak bu sepetin görüntülenmesi sorunlara yol açmaktadır. Sorun, bileşenin başlatılmasından kaynaklanmaktadır. Bileşenin sütunlarını hatırlayalım:
- [Article] sütunu, veri kaynağındaki [nom] alanıyla ilişkilidir
- [Qté] sütunu, veri kaynağındaki [qte] alanıyla ilişkilidir
- [Prix] sütunu, veri kaynağındaki [prix] alanıyla ilişkilidir
- veri kaynağındaki [total] alanıyla ilişkili [Total] sütunu
Elimizdeki veri kaynağı, alışveriş sepeti ve alışveriş listesidir. Alışveriş listesi, [DataGrid]'in veri kaynağı olacaktır. Yalnızca [DataGrid]'in satırlarını dolduracak olan [Achat] nesneleri, [DataGrid] tarafından beklenen [nom, qte, prix, total] özelliklerine sahip değildir. Bu nedenle burada, özellikle [DataGrid] için, öğeleri [DataGrid] tarafından beklenen özelliklere sahip bir veri kaynağı oluşturulur. Bu öğeler, bu amaçla oluşturulan ve [Achat] sınıfından türetilen [LigneAchat] sınıfına ait olacaktır (satır 36-66)
- [LigneAchat) sınıfı tanımlandıktan sonra, [DataGridAchats] veri kaynağı oturumda bulunan sepetten oluşturulur - satır 20-30
- Alışveriş tutarı, [Panier] sınıfının [totalPanier] özelliği sayesinde görüntülenir - satır 32
1.5.6.5. [paniervide.aspx] görünümü
1.5.6.5.1. Introduction
Bu görünüm, sepetin boş olduğunu belirten bilgileri gösterir:

Bu görünüm, /main?action=panier veya /main?action=retirerachat&id=ID sorgusunun ardından görüntülenir. Denetleyicinin sorgu öğeleri şunlardır:
Hashtable() nesnesi - menü seçenekleri tablosu |
1.5.6.5.2. Sayfanın bileşenleri
![]() |
n° | type | nom | rôle |
kullanıcı bileşeni | başlık | başlığı göster |
1.5.6.5.3. [paniervide.aspx] sunum kodu
Yorumlar:
- Başlık 9. satıra eklenmiştir
1.5.6.5.4. Denetim kodu [paniervide.aspx.vb]
Yorumlar:
- Sayfanın tek dinamik bileşenini başlatmakla yetiniyoruz - 10. satır
1.5.6.6. [erreurs.aspx] görünümü
1.5.6.6.1. Introduction
Bu görünüm, hata durumunda görüntülenir:

Bu mesaj, hataya yol açan tüm isteklerin ardından görüntülenir; ancak yanlış miktarla yapılan satın alma işlemi hariçtir; bu işlem, [INFOS] görünümü tarafından işlenir. Denetleyicinin isteğindeki öğeler şunlardır:
Hashtable() nesnesi - menü seçenekleri tablosu | |
ArrayList nesnesi, görüntülenecek hata mesajlarını temsil eden [String] nesneleri |
1.5.6.6.2. Sayfanın bileşenleri
![]() |
n° | type | nom | rôle |
kullanıcı bileşeni | başlık | başlığı göster | |
tekrarlayıcı | rptErreurs | hata listesini göster |
1.5.6.6.3. Görüntüleme kodu [erreurs.aspx]
Yorumlar:
- Başlık 9. satırda tanımlanmıştır
- [rptErreurs] bileşeni 13-19. satırlarda tanımlanmıştır. İçeriği, [ArrayList] türündeki bir veri kaynağından ve [String] nesnelerinden alınmaktadır.
1.5.6.6.4. Denetim kodu [erreurs.aspx.vb]
Açıklamalar:
- [entete] bileşeni her zamanki gibi 9. ve 14. satırlarda başlatılır
- [rptErreurs] bileşeni, bağlamda bulunan [ArrayList] türündeki hata listesiyle başlatılır - 16-19. satırlar
1.5.7. global.asax ve main.aspx denetleyicileri
Web uygulamamızın kalbini, yani denetleyiciyi yazmamız gerekiyor. Denetleyicinin görevi şunlardır:
- istemciden gelen isteği almak,
- işletme sınıflarını kullanarak müşterinin talep ettiği eylemi işlemek,
- yanıt olarak uygun görünümü göndermek.
1.5.7.1. [global.asax.vb] denetleyicisi
Uygulama ilk isteğini aldığında, [global.asax.vb] dosyasındaki [Application_Start] prosedürü çalıştırılır. Bu, tek seferlik bir işlemdir. [Application_Start] prosedürünün amacı, web uygulaması için gerekli olan ve tüm istemci iş parçacıkları tarafından salt okunur olarak paylaşılacak nesneleri başlatmaktır. Bu paylaşılan nesneler iki yere yerleştirilebilir:
- denetleyicinin özel alanları
- uygulamanın yürütme bağlamı (Application)
[global.asax.vb] uygulamasının [Application_Start] yöntemi aşağıdaki işlemleri gerçekleştirecektir:
- [web.config] dosyasında, uygulamanın düzgün çalışması için gerekli parametrelerin mevcut olup olmadığını kontrol edecektir. Bu parametreler 1.5.3. paragrafında açıklanmıştır.
- Olası hata listesini bir [ArrayList erreurs] nesnesi biçiminde uygulamanın bağlamına ekleyecektir. Hata yoksa bu liste boş olacaktır, ancak yine de mevcut olacaktır.
- Hata varsa, [Application_Start] yöntemi burada sonlanır. Aksi takdirde, denetleyicinin ihtiyaçları için kullanacağı iş nesnesi olacak [IArticlesDomain] türünde bir singleton'un referansını ister. 1.5.3.2 bölümünde açıklandığı gibi, denetleyici bu singleton'u Spring çerçevesinden talep edecektir. Bu örneklendirme işlemi çeşitli hatalara yol açabilir. Böyle bir durumda, hatalar yine uygulama bağlamındaki [erreurs] nesnesinde saklanacaktır.
[global.asax.vb] denetleyicisi, yeni bir müşteri geldiğinde her seferinde yürütülen [Session_Start] adlı bir prosedüre sahiptir. Bu prosedürde, müşteri için boş bir sepet oluşturulur. Bu sepet, söz konusu müşterinin istekleri boyunca korunur. Kod şu şekilde olabilir:
Açıklamalar:
- [web.config]'te beklenen parametreler bir dizide tanımlanmıştır - 18. satır
- bu parametreler [web.config]'te aranır. Varsa, uygulama bağlamında saklanır; yoksa [erreurs] hata listesine bir hata kaydedilir - satır 21-33
- Hata yoksa, uygulamanın [domain] katmanına erişimi yöneten [articlesDomain] singleton'una ilişkin bir referans Spring'den istenir - satır 35-47. Olası hatalar [erreurs]'e kaydedilir.
- Hatalar, uygulama bağlamında kaydedilir – satır 49
- Hata oluşursa prosedürden çıkılır – satır 51
- Üç sözlükten oluşan bir dizi oluşturulur. Her birinin iki anahtarı vardır: href ve link. Bu dizi, olası üç menü seçeneğini temsil eder - satır 52-71
- Bu dizi, uygulama bağlamında saklanır - satır 73
- Her yeni müşteri için [Session_Start] prosedürü yürütülür. Burada, müşterinin oturumunda boş bir sepet oluşturulur - satır 78-81
1.5.7.2. [main.aspx.vb] denetleyicisi
[main.aspx.vb] denetleyicisi, müşterilerden gelen tüm istekleri görür. Aslında bu isteklerin tümü [/webarticles/main.aspx?action=XX] biçimindedir. Bir istek şu şekilde işlenir:
- uygulama bağlamındaki [erreurs] nesnesi kontrol edilir. Bu nesne boş değilse, bu durum uygulamanın başlatılması sırasında hatalar oluştuğu ve uygulamanın çalışamayacağı anlamına gelir. Bu durumda yanıt olarak [ERREURS] görünümü gönderilir.
- İsteğin [action] parametresi alınacak ve kontrol edilecektir. Bilinen bir eyleme karşılık gelmiyorsa, uygun bir hata mesajıyla birlikte [ERREURS] görünümü gönderilir.
- [action] parametresi geçerliyse, istemcinin isteği işlenmek üzere eyleme özgü bir prosedüre aktarılır:
yöntem | istek | işleme | olası yanıtlar |
GET /main?action=liste | - iş sınıfına ait makalelerin listesini iste iş sınıfından - görüntülemek | [LISTE] veya [ERREURS] | |
GET /main?action=infos&id=ID | - id=ID numaralı makaleyi iş sınıfına - görüntülemek | [INFOS] veya [ERREURS] | |
POST /main?action=satınalma&id=ID - satın alınan miktar, gönderilen parametreler arasında yer alır | - id=ID olan ürünü iş sınıfına - bunu müşteri oturumuna | [LISTE] veya [INFOS] veya [ERREURS] | |
GET /main?action=retirerachat&id=ID | - id=ID olan ürünü müşterinin sepetindeki müşteri oturumunun | [PANIER] | |
GET /main?action=panier | - müşteri oturumunun müşteri oturumu | [PANIER] veya [PANIERVIDE] | |
GET /main?action=sepetonay | - Veritabanında sepet içinde bulunan tüm ürünlerin azaltın GET veya [ERREURS] | [LISTE] veya [ERREURS] |
[main.aspx.vb] denetleyicisinin iskeleti şu şekilde olabilir:
Açıklamalar:
- Sınıfın, yöntemler arasında paylaşılacak iki özel alanı vardır - 15-16. satırlar:
- articlesDomain: [domain] katmanına erişim sağlayan tekil nesne
- seçenekler: menü seçenekleri sözlüklerinin dizisi
- [Page_Load] yordamı:
- sınıfın iki özel alanını başlatacaktır
- istekten [action] parametresini alacak ve bu eylemin işleme yöntemini çalıştıracaktır.
1.5.7.3. [Page_Load] yöntemi
Bu olay, sayfada gerçekleşen ilk olaydır. Kod şu şekildedir:
Açıklamalar:
- Sayfa her yüklendiğinde, [global.asax] tarafından yapılan uygulama başlatma işleminin başarıyla tamamlandığından emin olunur.
- Bunun için, uygulama bağlamından [global.asax] tarafından buraya yerleştirilen hata listesi alınır - satır 4
- Bu liste boş değilse, [ERREURS] görünümünü görüntülenir - satır 6-10
- [global.asax] tarafından uygulama bağlamına yerleştirilen [articlesDomain] tekil nesnesini alırız ve sınıfın çeşitli yöntemleri tarafından kullanılabilmesi için [articlesDomain] özel alanına kaydederiz - satır 12
- menü seçenekleri dizisi için de benzer bir işlem yapılır - satır 14
- istekten [action] parametresi alınır - satır 16
- İstenen eyleme karşılık gelen yöntemi çalıştırıyoruz. Öngörülmemiş bir eylem, [liste] eylemi olarak kabul edilir - satır 16-36
1.5.7.4. [liste] eyleminin işlenmesi
Bu, ürün listesinin görüntülenmesidir:

Kod şu şekildedir:
Açıklamalar:
- Olası hatalar [ArrayList] dosyasına kaydedilir - 4. satır
- ürün listesi, [articlesDomain] tekil nesnesinden istenir - satır 5-12
- Hata varsa, [ERREURS] görünümü gönderilir - satır 13-19
- aksi takdirde [LISTE] görünümü gönderilir - satır 20-24
1.5.7.5. [infos] eyleminin işlenmesi
Müşteri belirli bir ürün hakkında bilgi talep etti:

Kod şu şekildedir:
Yorumlar:
- Olası hatalar [ArrayList] dosyasına kaydedilir - 4. satır
- İstenen maddenin kimliği, istekte alınır - satır 6
- bu kimlik doğrulanır. Kimlik mevcut olmalı ve bir tamsayı olmalıdır. Aksi takdirde, uygun hata mesajıyla birlikte [ERREURS] görünümü gönderilir - satır 7-27
- kimlik doğrulandıktan sonra, ürün [articlesDomain] tekil nesnesinden istenir. Bir istisna oluşursa, [ERREURS] görünümü gönderilir - satır 29-39
- ürün bulunamazsa, [ERREURS] görünümü gönderilir - satır 41-48
- ürün bulunursa, kullanıcının oturumuna eklenir ve ardından [INFOS] görünümünde görüntülenir - satır 50-56
1.5.7.6. [achat] eyleminin işlenmesi
Müşteri, [INFOS] görünümünde görüntülenen ürünü satın almıştır.

Kod şu şekildedir:
Yorumlar:
- oturumda bulunan makale alınır - 5. satır
- eğer orada değilse (oturumun süresi dolmuş olabilir), [LISTE] görünümü görüntülenir - satır 7-10
- satın alınan miktar sorgudan alınır - satır 12
- geçerliliği kontrol edilir - satır 13-29
- Geçerli değilse, duruma göre [LISTE] görünümü - satır 16 veya [INFOS] görünümü - satır 24-28 gönderilir
- her şey normalse, satın alma işlemi sepete kaydedilir - satır 31-32
- ardından [LISTE] görünümü gönderilir - satır 34
1.5.7.7. [panier] eyleminin işlenmesi
Müşteri çeşitli satın alımlar yaptı ve sepetini görmek istiyor:

Kod şu şekildedir:
Açıklamalar:
- Alışveriş sepeti oturumdan alınır - 4. satır. Burada bir şeyin alındığını doğrulamıyoruz. Oturumun süresi dolmuş olabileceğinden bunu yapmak gerekir.
- Alışveriş sepeti boşsa, [PANIERVIDE] görünümü gönderilir - 6-10. satırlar
- Aksi takdirde [PANIER] görünümü gönderilir - satır 11-14
1.5.7.8. [retirerachat] eyleminin işlenmesi
Müşteri, sepetinden bir ürünü çıkarmak istiyor:

Kod şu şekildedir:
Açıklamalar:
- Sepet, oturumdan alınır - 4. satır. Burada bir şeyin alındığını doğrulamıyoruz. Oturumun süresi dolmuş olabileceğinden bunu yapmak gerekir.
- İstemden, kaldırılacak ürünün [id] kimlik numarası alınır - 8. satır.
- İlgili satın alma işlemi sepetten kaldırılır - 10. satır
- Satın alınan ürünün kimlik numarasının geçerliliği burada kontrol edilmemiştir. Eğer kimlik numarasının türü geçersizse, bir istisna oluşur ve 11-13. satırlarda işlenir. Eğer geçerli ancak mevcut değilse, [panier.enlever] yöntemi (10. satır) hiçbir işlem yapmaz.
- Yeni sepet görüntülenir - satır 16
1.5.7.9. [validerpanier] eyleminin işlenmesi
Müşteri sepetini onaylamak istiyor:

Kod şu şekildedir:
Açıklamalar:
- Alışveriş sepeti oturumdan alınır - 6. satır. Burada bir şeyin alındığını doğrulamıyoruz. Oturumun süresi dolmuş olabileceğinden bunu yapmak gerekir.
- Oturumun süresi dolmuşsa, sepet için [nothing] işaretçisi elde edilir ve [acheter] yöntemi (9. satır) bir istisna oluşturur ve [ERREURS] görünümü gönderilir. Ancak hata mesajı kullanıcı için pek açıklayıcı olmayacaktır.
- 8-16. satırlarda, oturumdan alınan sepetin geçerliliği kontrol edilmeye çalışılır. Talep edilen miktar, ilgili ürünün stok miktarını aşarsa bazı satın alımlar onaylanmayabilir. Bu durumlar, [acheter] yöntemi tarafından bir hata listesinde kaydedilir ve bu liste 11. satırda alınır.
- Hata varsa, [ERREURS] görünümü gönderilir – satır 18-23
- aksi takdirde [LISTE] görünümü gönderilir - satır 25-26
1.6. Conclusion
Burada MVC modeline göre bir uygulama geliştirdik. Java'da (Struts, Spring, ...). [Spring.net] projesi yakında böyle bir çerçeve sunacaktır. Bu gelişme gerçekleşene kadar, önceki yöntem orta ölçekli uygulamalar için uygulanabilir bir MVC geliştirme imkanı sunmaktadır.































