Skip to content

7. الحل

7.1. مشروع Visual Studio

تعليقات:

  • تم وضع الملفات المطلوبة لـ Spring في المجلد [bin]: Spring.Core.dll، log4net.dll، Spring.Core.xml
  • كما تم وضع ملفات تكوين Spring [spring-config-3tier-*.xml] في المجلد [bin]
  • ستجد تحت الدليل الجذري [istia] الفئات المختلفة للتطبيق

تم تكوين المشروع لإنشاء ملف DLL [spring3tier.dll] في المجلد [bin]:

Image

7.2. الحزمة [istia.st.spring3tier.dao]

واجهة IDao:

Namespace istia.st.spring3tier.dao
    Public Interface IDao
        ' do something in the [dao] layer
        Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer
    End Interface
End Namespace

فئة التنفيذ الأولى:

Namespace istia.st.spring3tier.dao
    Public Class DaoImpl1
        Implements istia.st.spring3tier.dao.IDao

        ' do something in the diaper [dao]
        Public Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDao.doSomethingInDaoLayer

            Return a + b
        End Function
    End Class

End Namespace
  • لا تحتوي الفئة على حقول خاصة
  • تُرجع الطريقة [doSomethingInDaoLayer] مجموع معلماتها كما هو مطلوب.

فئة تنفيذ ثانية:

Namespace istia.st.spring3tier.dao
    Public Class DaoImpl2
        Implements istia.st.spring3tier.dao.IDao

        ' do something in the diaper [dao]
        Public Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDao.doSomethingInDaoLayer

            Return a - b
        End Function
    End Class

End Namespace
  • لا تحتوي الفئة على حقول خاصة
  • تُرجع الطريقة [doSomethingInDaoLayer] الفرق بين معلماتها كما هو مطلوب.

7.3. حزمة [istia.st.spring3tier.domain]

واجهة IDomain:

Namespace istia.st.spring3tier.domain
    Public Interface IDomain
        ' do something in the [domain] layer
        Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer
    End Interface
End Namespace

فئة التنفيذ الأولى IDomainImpl1:

Imports istia.st.spring3tier.dao

Namespace istia.st.spring3tier.domain
    Public Class DomainImpl1
        Implements istia.st.spring3tier.domain.IDomain

        ' private fields
        Private _dao As IDao

        ' associated property
        Public WriteOnly Property dao() As IDao
            Set(ByVal Value As IDao)
                _dao = Value
            End Set
        End Property

        ' default builder
        Public Sub New()
        End Sub

        ' do something in the [domain] layer
        Public Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDomain.doSomethingInDomainLayer
            a += 1
            b += 1
            Return _dao.doSomethingInDaoLayer(a, b)
        End Function
    End Class

End Namespace
  • تحتوي الفئة على حقل خاص يمثل مرجعًا إلى عنصر فريد من نوع [IDao]، والذي يوفر الوصول إلى طبقة [Dao]. سيتم تهيئة هذا الحقل بواسطة Spring (حقن التبعية) عند إنشاء الكائن.
  • تقوم الطريقة [doSomethingInDomainLayer] بزيادة معلماتها ثم تمررها إلى الطريقة [doSomethingInDaoLayer] للكائن الفردي [dao]

فئة تنفيذ ثانية، IDomainImpl2:

Imports istia.st.spring3tier.dao

Namespace istia.st.spring3tier.domain
    Public Class DomainImpl2
        Implements istia.st.spring3tier.domain.IDomain

        ' private fields
        Private _dao As IDao

        ' associated property
        Public WriteOnly Property dao() As IDao
            Set(ByVal Value As IDao)
                _dao = Value
            End Set
        End Property

        ' default builder
        Public Sub New()
        End Sub

        ' do something in the [domain] layer
        Public Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDomain.doSomethingInDomainLayer
            a -= 1
            b -= 1
            Return _dao.doSomethingInDaoLayer(a, b)
        End Function
    End Class

End Namespace
  • تحتوي الفئة على حقل خاص يمثل مرجعًا إلى العنصر الفردي [IDao]، الذي يوفر الوصول إلى طبقة [Dao]. سيتم تهيئة هذا الحقل بواسطة Spring (حقن التبعية) عند إنشاء الكائن.
  • تقوم طريقة [doSomethingInDomainLayer] بخصم قيم معلماتها ثم تمريرها إلى طريقة [doSomethingInDaoLayer] الخاصة بـ [dao] singleton

7.4. حزمة [istia.st.spring3tier.control]

واجهة IControl:

Namespace istia.st.spring3tier.control
    Public Interface IControl
        ' do something in the [control] layer
        Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer
    End Interface
End Namespace

فئة التنفيذ الأولى ControlImpl1:

Imports istia.st.spring3tier.domain

Namespace istia.st.spring3tier.control
    Public Class ControlImpl1
        Implements istia.st.spring3tier.control.IControl

        ' private fields
        Private _domain As IDomain

        ' associated property
        Public WriteOnly Property domain() As IDomain
            Set(ByVal Value As IDomain)
                _domain = Value
            End Set
        End Property

        ' default builder
        Public Sub New()
        End Sub

        ' do something in the [control] layer
        Public Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IControl.doSomethingInControlLayer
            a += 1
            b += 1
            Return _domain.doSomethingInDomainLayer(a, b)
        End Function
    End Class

End Namespace
  • تحتوي الفئة على حقل خاص يمثل مرجعًا إلى العنصر الفريد من النوع [IDomain]، والذي يوفر الوصول إلى طبقة [Domain]. سيتم تهيئة هذا الحقل بواسطة Spring (حقن التبعية) عند إنشاء الكائن.
  • تقوم الطريقة [doSomethingInControlLayer] بزيادة معلماتها ثم تمررها إلى الطريقة [doSomethingInDomainLayer] للكائن الفردي [domain]

فئة تنفيذ ثانية، IControlImpl2:

Imports istia.st.spring3tier.domain

Namespace istia.st.spring3tier.control
    Public Class ControlImpl2
        Implements istia.st.spring3tier.control.IControl

        ' private fields
        Private _domain As IDomain

        ' associated property
        Public WriteOnly Property domain() As IDomain
            Set(ByVal Value As IDomain)
                _domain = Value
            End Set
        End Property

        ' default builder
        Public Sub New()
        End Sub

        ' do something in the [control] layer
        Public Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IControl.doSomethingInControlLayer
            a -= 1
            b -= 1
            Return _domain.doSomethingInDomainLayer(a, b)
        End Function
    End Class

End Namespace
  • تحتوي الفئة على حقل خاص يمثل مرجعًا إلى العنصر الفريد من النوع [IDomain]، والذي يوفر الوصول إلى طبقة [Domain]. سيتم تهيئة هذا الحقل بواسطة Spring (حقن التبعية) عند إنشاء الكائن.
  • تقوم طريقة [doSomethingInControlLayer] بخصم قيم معلماتها ثم تمريرها إلى طريقة [doSomethingInDomainLayer] للكائن الفردي [domain].

7.5. ملفات تكوين [Spring]

يستخدم ملف [spring-config-3tier-1.xml] الإصدار 1 من عمليات التنفيذ:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
     <!-- the dao class -->
    <object id="dao" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
     <!-- the domain class -->
    <object id="domain" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
        <property name="dao">
            <ref object="dao" />
        </property>
    </object>
     <!-- the control class -->
    <object id="control" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
        <property name="domain">
            <ref object="domain" />
        </property>
    </object>
</objects>

يستخدم ملف [spring-config-3tier-2.xml] الإصدار 2 من عمليات التنفيذ:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
     <!-- the dao class -->
    <object id="dao" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
     <!-- the domain class -->
    <object id="domain" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
        <property name="dao">
            <ref object="dao" />
        </property>
    </object>
     <!-- the control class -->
    <object id="control" type="istia.st.spring3tier.control.ControlImpl2, spring3tier">
        <property name="domain">
            <ref object="domain" />
        </property>
    </object>
</objects>

7.6. حزمة الاختبار [istia.st.spring3tier.tests]

اختبار NUnit [NunitTestSpring3tier.vb]:

Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.spring3tier.control
Imports istia.st.spring3tier.domain

Namespace istia.st.springioc.tests

    <TestFixture()> _
    Public Class NunitTestSpring3tier
        ' the singleton factories
        Private factory1 As XmlObjectFactory
        Private factory2 As XmlObjectFactory

        <SetUp()> _
        Public Sub init()
            ' singleton factories are created
            factory1 = New XmlObjectFactory(New FileStream("spring-config-3tier-1.xml", FileMode.Open))
            factory2 = New XmlObjectFactory(New FileStream("spring-config-3tier-2.xml", FileMode.Open))
        End Sub

        <TearDown()> _
        Public Sub destroy()
            ' we destroy the singletons
            factory1.Dispose()
            factory2.Dispose()
            ' we free the singletons factories
            factory1 = Nothing
            factory2 = Nothing
        End Sub

        <Test()> _
        Public Sub test()
            'we retrieve an implementation of the IControl interface
            Dim control1 As IControl = CType(factory1.GetObject("control"), IControl)
            ' we use the
            Dim a1 As Integer = 10, b1 As Integer = 20
            Dim res1 As Integer = control1.doSomethingInControlLayer(a1, b1)
            Assert.AreEqual(34, res1)
            ' we retrieve another implementation of the IControl interface
            Dim control2 As IControl = CType(factory2.GetObject("control"), IControl)
            ' we use the
            Dim a2 As Integer = 10, b2 As Integer = 20
            Dim res2 As Integer = control2.doSomethingInControlLayer(a2, b2)
            Assert.AreEqual(-10, res2)
        End Sub

    End Class
End Namespace

يؤدي تشغيل هذا الاختبار إلى النتائج التالية:

Image

سيلاحظ القراء الذين يشاهدون نسخة ملونة من هذا المستند أن النتائج "خضراء"، مما يشير إلى نجاح الاختبار.

7.7. نوع آخر من ملفات تكوين Spring

يمكن تكوين تطبيق VB.NET باستخدام ملف يسمى [App.config]. يتم وضع هذا الملف في جذر مشروع Visual Studio:

Image

يمكن لـ Spring استخدام ملف التكوين هذا. انظر إلى ملف [App.config] التالي، المستوحى من أمثلة من وثائق [Spring.net]:

<?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>
             <!-- an initial configuration -->
             <!-- the dao class -->
            <object id="dao1" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
             <!-- the domain class -->
            <object id="domain1" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
                <property name="dao">
                    <ref object="dao1" />
                </property>
            </object>
             <!-- the control class -->
            <object id="control1" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
                <property name="domain">
                    <ref object="domain1" />
                </property>
            </object>
             <!-- a second configuration -->
             <!-- the dao class -->
            <object id="dao2" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
             <!-- the domain class -->
            <object id="domain2" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
                <property name="dao">
                    <ref object="dao2" />
                </property>
            </object>
             <!-- the control class -->
            <object id="control2" type="istia.st.spring3tier.control.ControlImpl2, spring3tier">
                <property name="domain">
                    <ref object="domain2" />
                </property>
            </object>
        </objects>
    </spring>
</configuration>

ملاحظة: المعلومات الواردة أدناه مقدمة مع بعض التحفظات. لست متأكدًا من أنني فهمت بشكل صحيح معنى جميع العناصر الموجودة في ملف التكوين أعلاه.


تتطلب صيغة XML لملف [App.config] أن يتبع هذا التنسيق:

<configuration>
....
</configuration>

يمكن تفويض إدارة الأقسام المختلفة في [App.config] إلى برامج خارجية. وهذا ما يتم هنا في قسم [ConfigSections]:

    <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] يجب أن تداره الفئة [Spring.Context.Support.ContextHandler] الموجودة في تجميع [Spring.Core.dll]، وأن القسم المسمى [spring/objects] يجب أن تعالجه الفئة [Spring.Context.Support.DefaultSectionHandler]، الموجودة أيضًا في تجميع [Spring.Core.dll].

القسم [spring/context] هو كما يلي:

    <spring>
        <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
            <resource uri="config://spring/objects" />
        </context>
...
    </spring>

يبدو أن هذا يشير إلى أن قسم [spring/objects] من ملف التكوين يجب أن تتم إدارته بواسطة فئة [Spring.Context.Support.XmlApplicationHandler]، والتي يمكن العثور عليها في تجميع [Spring.Core.dll]. يجب أن تستخدم هذه الفئة مورد XML موجود في [config://spring/objects]، أي قسم [spring/objects] من ملف التكوين الحالي.

في قسم [spring/objects]، نجد صيغة Spring التي أصبحنا الآن على دراية بها.

في قسم <objects>... </objects> من ملف [App.config]، قمنا بتعريف تكوينين محتملين لتطبيقنا ثلاثي المستويات:

  • أحدهما يستخدم الإصدار 1 من تطبيقات الواجهة
  • والآخر يستخدم الإصدار 2 من نفس عمليات التنفيذ تلك

الآن، كيف تستخدم ملف [App.config

يُظهر الكود التالي تطبيق وحدة التحكم الذي يستخدم ملف [App.config] السابق:

Imports System
Imports Spring.Context
Imports System.IO
Imports NUnit.Framework
Imports istia.st.spring3tier.control
Imports istia.st.spring3tier.domain
Imports System.Configuration

Namespace istia.st.springioc.tests

    Module MainTestSpring3tier

        Public Sub main()
            ' the Spring context that will allow us to retrieve singletons
            Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
            ' we retrieve a 1st implementation of the IControl interface
            Dim control1 As IControl = CType(contexte.GetObject("control1"), IControl)
            ' we use the
            Dim a1 As Integer = 10, b1 As Integer = 20
            Console.WriteLine("res1({0},{1})={2}", a1, b1, control1.doSomethingInControlLayer(a1, b1))
            ' we retrieve another implementation of the IControl interface
            Dim control2 As IControl = CType(contexte.GetObject("control2"), IControl)
            ' we use the
            Dim a2 As Integer = 10, b2 As Integer = 20
            Console.WriteLine("res2({0},{1})={2}", a2, b2, control2.doSomethingInControlLayer(a2, b2))
            ' break
            Console.WriteLine("Tapez [entrée] pour continuer...")
            Console.ReadLine()
        End Sub

    End Module
End Namespace

تعليقات:

  • في أمثلة NUnit التي استخدمناها حتى الآن، استخدمنا كائن [XmlObjectFactory] للحصول على الكائنات الفردية التي نحتاجها. هنا، الكائن المستخدم هو من النوع [IApplicationContext]، وهي واجهة Spring. يتم الحصول عليه من [App.config] باستخدام فئة [ConfigurationSettings]، وهي فئة تُستخدم تقليديًا في .NET لمعالجة ملفات التكوين.
  • نطلب المعالج الخاص بقسم [spring/context]. إذا رجعنا إلى ملف [App.config]، نرى أن هذا المعالج من نوع [XmlApplicationContext] وهو مسؤول عن إدارة قسم [spring/objects] في [App.config].
  • بمجرد استرداد كائن [IApplicationContext]، يتم استخدامه تمامًا مثل كائن [XmlObjectFactory] الذي كنا نستخدمه حتى الآن.

البرنامج السابق يسمى [MainTestSpring3tier.vb] ويقع في حزمة [tests]:

Image

تم تكوين مشروع [spring3tier] بحيث [MainTestSpring3tier]:

Image

يؤدي تشغيل المشروع إلى النتائج التالية:

res1(10,20)=34
res2(10,20)=-10
Tapez [entrée] pour continuer...

7.8. الخلاصة

يوفر إطار عمل Spring مرونة حقيقية في كل من بنية التطبيق وتكوينه. استخدمنا مفهوم IoC، وهو أحد ركيزتي Spring. الركيزة الأخرى هي AOP (البرمجة الموجهة نحو الجوانب)، والتي لم نتطرق إليها. تتيح لك إضافة "سلوك" إلى طريقة فئة عبر التكوين دون تعديل كود الطريقة. بعبارات بسيطة، تتيح لك AOP تصفية المكالمات إلى طرق معينة:

  • يمكن تنفيذ المرشح قبل أو بعد الطريقة المستهدفة M، أو كليهما.
  • لا تعرف الطريقة M بوجود هذه المرشحات. يتم تعريفها في ملف تكوين Spring.
  • لا يتم تعديل كود الطريقة M. المرشحات هي فئات Java يجب تنفيذها. يوفر Spring مرشحات محددة مسبقًا، خاصة لإدارة معاملات أنظمة إدارة قواعد البيانات (DBMS).
  • المرشحات هي حبوب (beans)، وبالتالي يتم تعريفها في ملف تكوين Spring كحبوب.

أحد المرشحات الشائعة هو مرشح المعاملات. لنفترض أن هناك طريقة M في طبقة الأعمال تقوم بعملية لا تنفصل عن البيانات (وحدة عمل). وهي تستدعي طريقتين في طبقة DAO، M1 و M2، لتنفيذ هاتين العمليتين.

نظرًا لوجودها في طبقة الأعمال، فإن الطريقة M تستبعد تخزين البيانات الأساسي. فهي لا تحتاج، على سبيل المثال، إلى افتراض أن البيانات موجودة في نظام إدارة قواعد البيانات (DBMS) وأنها تحتاج إلى وضع الاستدعاءين للطريقتين M1 و M2 ضمن معاملة نظام إدارة قواعد البيانات. الأمر متروك لطبقة DAO للتعامل مع هذه التفاصيل. أحد الحلول للمشكلة السابقة هو إنشاء طريقة في طبقة DAO تقوم هي نفسها باستدعاء الطريقتين M1 و M2، مع تغليف هذه الاستدعاءات ضمن معاملة نظام إدارة قواعد البيانات (DBMS).

يعد حل التصفية AOP أكثر مرونة. فهو يتيح لك تعريف مرشح يقوم، قبل استدعاء M، ببدء معاملة، وبعد الاستدعاء، يقوم بإجراء التثبيت أو التراجع حسب الاقتضاء.

هناك عدة مزايا لهذا النهج:

  • بمجرد تعريف المرشح، يمكن تطبيقه على طرق متعددة، على سبيل المثال، جميع الطرق التي تتطلب معاملة
  • لا تحتاج الطرق التي تمت تصفيتها إلى إعادة كتابة
  • نظرًا لأن المرشحات التي سيتم استخدامها يتم تحديدها من خلال التكوين، فيمكن تغييرها

لمزيد من المعلومات: http://www.springframework.net.