Makale Özeti

Zamanla, geliştirdiğimiz uygulamalara yeni özellikler katmak isteyebiliriz. Sonradan aklımıza gelen ya da kullanıcının istediği yeni bir özelliği uygulamamıza eklemek için uygulanabilir yöntemlerden biri de ‘Eklenti (plug-in) yaklaşımıdır.

Makale

Eklenti (Plug-in) Mimarisi


Zamanla, geliştirdiğimiz uygulamalara yeni özellikler katmak isteyebiliriz. Sonradan aklımıza gelen ya da kullanıcının istediği yeni bir özelliği uygulamamıza eklemek için uygulanabilir yöntemlerden biri de ‘Eklenti (plug-in) yaklaşımıdır.

Bu yaklaşım sayesinde uygulamamızın bir parçasında yapılacak güncelleme için tüm uygulamayı yeniden derlememiz gerekmez. Ya da uygulamanızın,kaynak kodlarına erişilmeden, başka geliştiriciler tarafından da geliştirilmesini isteyebiliriliriz. Yine, eğer uygulamamız, sürekli değişim gösteren parçacıklar içeriyorsa, bu parçacıkları zamanla yenilemek zorunda kalırız. Eklenti mimarisini kullanarak geliştireceğimiz uygulamalar, tüm bu ihtiyaçları karşılayabilir. Çoğumuz Winampın ya da Photo Shopun eklentilerine aşinayızdır. İşte bu yazıda uygulamalarımıza nasıl eklentiler ekleyebileceğimizi kavramaya çalışacağız.

Eklenti Mimarisi : Tasarım ve Uygulama

Uygulamamıza eklentiler ilave etmek istiyorsak öncelikle bazı kurallar belirlemeliyiz. Bu kurallar eklentiler uygulamamıza nasıl ve hangi katmanda etkiyecek? sorusuna cevap vermelidir. Belli kurallar çerçevesinde, uygulamalarda eklenti yapısını oluşturmanın en iyi yollarından biri bunu Interfaceleri kullanarak yapmaktır. Interface, kısaca belli bir yapıyı oluşturmak için sunulan anlaşmadır. Herhangi bir sınıf bir interface kullanmak isterse interface‘in tüm üyelerini kendine dahil etmelidir. Kabaca bir sınıf bir interface‘i kullanabilmek için interfacein şartlarını yerine getirmelidir. Yine eklenti mimarisini uygulayabilmek için Reflection sınıfından da faydalanacağız. Uygulamamız bittiğinde hem eklenti mimarisinin uygulamalara nasıl entegre edildiği hem de interfacelerin ve Reflection isimuzayının pratikte nasıl kullanabileceği hakkında yeni fikirler edinmiş olacağız.

Uygulamamızın genel yapısını belirlemeye çalışalım. Uygulamamız üç temel bileşenden oluşacak:

  • Eklentilerin yapılarını belirleyen Interfaceler (Interfacaler : Class Library)
  • Eklentileri kullanabilen bir ana uygulama (SunucuUygulama : Windows Application)
  • Eklentiler (eklentiXXXX : Class Library)

Artık uygulamamızı oluşturmaya başlayabiliriz .

Interfaceler
Uygulamamıza ilave edilebilecek eklentilerin hangi özelliklere sahip olması gerektiğini bu bölümde tanımlayacağız. Uygulamamıza ilave edilebilecek eklentilerin, yaptığı işleri tutan string tipinde ad özelliği ve işlevini gerçekleştiren, bir RichTextBoxı parametre olarak alan islem yordamları olmalı. Bunun için yeni bir solution içinde Interfaceler adında yeni bir Class Library projesi oluşturun ve System.Windows.Forms isimuzayına referans verdikten sonra şu interfacei gerçekleştirin:

Imports System.Windows.Forms
Public Interface IEklenti
ReadOnly Property Ad() As String
Sub Islem(ByRef rtbMetin As RichTextBox)
End Interface

Ana uygulama
Ana uygulamamız için mevcut solution içinde yeni bir windows application oluşturun ve az önce oluşturduğumuz proje içinden Interfaceler isimuzayına referans ekleyin. Uygulamamız sadece bir metin kutusu sunacak. Açılışta uygulama klasöründe eklentiler olup olmadığını kontrol edecek ve eğer varsa bunları kullanılır hale getirecek. Uygulama eklentileri olmadan sadece basit bir metin kutusu sunacakken, eklentiler sayesinde seçilen yazıyı kalın ve yatık olarak şekillendirebilme yeteneklerini kazandıracağız.

Formun sınıf adını frmSunucu olarak değiştirdikten sonra forma, metin girmek için rtbMetin adında bir RichTextBox , eklentileri listelemek ve seçmek için lstEklentiler adında bir Listbox ve seçilen eklentinin işlevini gerçekleştirmek için de btnIslem adında bir Buton yerleştirin.

Bu adımlardan sonra , tasarladığımız yapıyı oluşturmak istediğimizde karşımıza bazı sorunlar çıkıyor. Şimdi bu sorunları ve çözümleri başlıklar halinde inceleyelim;

Eklentilerin tespiti
Eklentiler farklı fonksiyon ya da yordamlarla birleştirilerek tek bir dosyada (örneğin dll uzantılı dosyalar, genelde birçok fonksiyonu birarada tutarlar.) tutulabilir. Peki böyle bir durumda gerekli filtrelemeyi nasıl yapacağız? Gerekli filtrelemeyi yapsak bile derleme anında doğrudan referans vermediğimiz eklentileri uygulamamızda nasıl kullanacağız? Tam bu noktada Reflection isimuzayından faydalanacağız. Reflection isimuzayının sunduğu olanaklarla belli dosyaların içinde tutulan sınıfları kontrol edeceğiz:

Public Structure UygunEklentiler
Public AssemblyYolu As String
Public ClassAdi As String
End
Structure

Public Shared Function EklentiBul(ByVal strYol As String, ByVal strInterface As String) As UygunEklentiler()
Dim Eklentiler As ArrayList = New ArrayList
Dim strDLLler() As String, intIndex As Integer
Dim objDLL As [Assembly]

Tüm DLLleri tarayıp yükle
strDLLler = Directory.GetFileSystemEntries(strYol, "*.dll")

For intIndex = 0 To strDLLler.Length - 1
Try
objDLL = [Assembly].LoadFrom(strDLLler(intIndex))
AssemblyIncele(objDLL, strInterface, Eklentiler)
Catch e As Exception
Dlli yüklemede hata oluşursa...
End Try
Next

Bulunan eklentileri döndür
Dim Sonuclar(Eklentiler.Count - 1) As UygunEklentiler

If Eklentiler.Count <> 0 Then
Eklentiler.CopyTo(Sonuclar)
Return Sonuclar
Else
Return Nothing
End If
End Function

Yukarıdaki fonksiyonla belirtilen klasördeki tüm dll dosyalarını AssemblyIncele fonksiyonuyla inceleyeğiz. Uygulamamız için eklenti özelliği taşıyan dll dosyalarını seçebilmek için dosyanın içerdiği üyeler hakkında bilgi edinmemiz gerekir. Kısaca bu dosya belirlediğimiz eklenti yapısını destekliyor mu diye sorgulayacağız:

Private Shared Sub AssemblyIncele(ByVal objDLL As [Assembly], ByVal strInterface As String, ByVal Eklentiler As ArrayList)
Dim objTip As Type
Dim objInterface As Type
Dim Eklenti As UygunEklentiler

Tüm Dllleri tara
For Each objTip In objDLL.GetTypes
Sadece public tiplerle ilgilen
If objTip.IsPublic = True Then
Abstract synyflary dikkate alma

If Not ((objTip.Attributes And TypeAttributes.Abstract) = TypeAttributes.Abstract) Then
Belirlediğimiz Interfacei destekleyip desteklemediğini kontrol et
objInterface = objTip.GetInterface(strInterface, True)

If Not (objInterface Is Nothing) Then
Destekliyor
Eklenti = New UygunEklentiler
Eklenti.AssemblyYolu = objDLL.Location
Eklenti.ClassAdi = objTip.FullName
Eklentiler.Add(Eklenti)
End If
End If
End If

Next

End Sub

Bu sayede gerekli filtremeyi yapmış oluyoruz. Şimdi sıra derleme anında referans verilmemiş bir assembly den örnek oluşturmaya geldi :

Public Shared Function OrnekOlustur(ByVal Eklenti As UygunEklentiler) As Object
Dim objDLL As [Assembly]
Dim objEklenti As Object

Try
Dlli yükle
objDLL = [Assembly].LoadFrom(Eklenti.AssemblyYolu)

Asıl işi yapan CreateInstance metodunu kullarak sınıftan örnek oluştur
objEklenti = objDLL.CreateInstance(Eklenti.ClassAdi)
Catch e As Exception
Return Nothing
End Try

Return objEklenti

End Function

Yeniden uygulamamıza dönelim ve bu üç fonksiyonu tutmak için projemize EklentiServisleri adında bir sınıf ekleyelim. System.Reflection ve System.IO isimuzaylarını sınıfa dahil etmeyi unutmayalım:

Imports System.IO
Imports System.Reflection

Şimdi projeye clsAna adında yeni bir sınıf ekleyin. Bu sınıf eklenti olup olmadını kontrol edecek ve projemizin başlangıç noktası (Project – Properties – Common Properties – General – Startup Object) olacak :

Public Shared Sub Main()

Dim Eklentiler() As EklentiServisleri.UygunEklentiler

Dim objForm As frmSunucu

Eklentilerin listesini al
Eklentiler = EklentiServisleri.EklentiBul(Path.GetDirectoryName(Application.ExecutablePath), "Interfaceler.IEklenti")

If Eklentiler Is Nothing Then
MessageBox.Show("Eklenti bulunamady!", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop)
Exit Sub
End If

Formu yükle ve göster
objForm = New frmSunucu(Eklentiler)
Application.Run(objForm)

End Sub

Şimdi formumuzun arkasında kalan kodları yazmaya devam edebiliriz. İlk önce New yordamımızı clsAna tarafından çağrılmak üzere biraz değiştirelim :

Private Eklentiler() As EklentiServisleri.UygunEklentiler
Public Sub New(ByVal Eklentiler() As EklentiServisleri.UygunEklentiler)
MyBase.New()
Me.Eklentiler = Eklentiler
ListeyiYerlestir()
End Sub

Bulunan eklentileri listboxa yerleştirelim :

Private Sub ListeyiYerlestir()
Dim objEklenti As Interfaceler.IEklenti
Dim intSiraNo As Integer

Uygun eklentileri tara ve listboxa ekle.
For intSiraNo = 0 To Eklentiler.Length - 1
objEklenti = Ctype(EklentiServisleri.OrnekOlustur(Eklentiler(intSiraNo)), Interfaceler.IEklenti)
lstEklentiler.Items.Add(objEklenti.Ad)
Next
lstEklentiler.SelectedIndex = 0
End Sub

Butona basıldığında eklentinin işlemi gerçekleştirilsin:

Private Sub btnIslem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnIslem.Click
Dim objEklenti As Interfaceler.IEklenti
Dim dblResult As Double
Eklentiyi oluştur ve başlat.
objEklenti = Ctype(EklentiServisleri.OrnekOlustur(Eklentiler(lstEklentiler.SelectedIndex)), Interfaceler.IEklenti)

Seçilen eklentinin işlemini yap.
Try
objEklenti.Islem(rtbMetin)
Catch
MessageBox.Show("Hata oluştu.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
Exit Sub
End Try
End Sub

Eklentiler
Ana uygulamamızda yer alan metini yatık ve kalın olarak şekillendirebilecek iki adet eklenti oluşturacağız. Bunun için yeni bir Class Library projesi oluşturun. System.Drawing , System.Windows.Forms ve az önce oluşturduğumuz Interfaceler adlı namespacelere referans ekleyin.

Daha sonra ana uygulamamızın eklentiler için şart koştuğu interfacei desteklediğimizi belirtmeliyiz :

Implements Interfaceler.Ieklenti

Sonra interfacein ad özelliğini karşılamalıyız:

Public ReadOnly Property Ad() As String Implements Interfaceler.IEklenti.Ad
Get
Return "Seçilen yazıyı yatık yapar."
End Get
End Property

Ve son olarak da yazıyı yatık hale getirmek için gerekli kodları interfacein Islem yordamını karşılayarak eklemeliyiz:

Public Sub Yatik(ByRef rtbMetin As RichTextBox) Implements Interfaceler.IEklenti.Islem
Dim GecerliFont As System.Drawing.Font = rtbMetin.SelectionFont
Dim YeniFontSitili As System.Drawing.FontStyle

If rtbMetin.SelectionFont.Italic = True Then
YeniFontSitili = FontStyle.Regular
Else
YeniFontSitili = FontStyle.Italic
End If

rtbMetin.SelectionFont = New Font(GecerliFont.FontFamily, GecerliFont.Size, YeniFontSitili)

End Sub

Gerekli isimuzaylarını sınıfa dahil etmeyi unutmayalım:

Imports System.Drawing
Imports System.Windows.Forms

Kalın özelliği katmak için de kodlarda gerekli değişiklikleri yaparak aynı adımları tekrarlayalım.

Public ReadOnly Property Ad() As String Implements Interfaceler.IEklenti.Ad
Get
Return "Seçili yazıyı kalın yapar."
End Get
End Property

Private Sub Kalin(ByRef rtbMetin As RichTextBox) Implements Interfaceler.IEklenti.Islem
Dim GecerliFont As System.Drawing.Font = rtbMetin.SelectionFont
Dim YeniFontSitili As System.Drawing.FontStyle
If rtbMetin.SelectionFont.Bold = True Then
YeniFontSitili = FontStyle.Regular
Else
YeniFontSitili = FontStyle.Bold
End If
rtbMetin.SelectionFont = New Font(GecerliFont.FontFamily, GecerliFont.Size, YeniFontSitili)
End Sub

Uygulamamızı çalıştırmadan önce dikkat etmemiz gereken bir nokta var. Pratiklik açısından eklentileri uygulama klasöründe aradık. Bu yüzden tüm projelerin çıktılarının (Project – Properties – Configuration Properties – Build – Output Path ) tek bir klasörde toplanması gerekir.

Bu ayarları da yaptığımızda uygulamamızı çalışmaya hazır hale getirmiş bulunuruz.

Değerlendirmelerinizi bekliyorum.

Kâsım GÜLCAN
SQLNedir?com