Skip to content

5. Spring IoC في الممارسة العملية

سنستخدم الآن أمثلة لتطبيق ما رأيناه سابقًا عمليًا.

5.1. المثال 1

فئة [Person.vb] هي كما يلي:

Namespace istia.st.springioc.demos
    Public Class Personne

        ' private fields
        Private _nom As String
        Private _age As Integer

        ' default builder
        Public Sub New()
        End Sub

        ' properties associated with private fields
        Public Property nom() As String
            Get
                Return _nom
            End Get
            Set(ByVal Value As String)
                _nom = Value
            End Set
        End Property

        Public Property age() As Integer
            Get
                Return _age
            End Get
            Set(ByVal Value As Integer)
                _age = Value
            End Set
        End Property

        ' identity chain
        Public Overrides Function tostring() As String
            Return String.Format("[{0},{1}]", nom, age)
        End Function

        ' init method
        Public Sub init()
            Console.WriteLine("init personne {0}", Me.ToString)
        End Sub

        ' close method
        Public Sub close()
            Console.WriteLine("destroy personne {0}", Me.ToString)
        End Sub

    End Class
End Namespace

تحتوي الفئة على:

  • حقلين خاصين، الاسم والعمر، يمكن الوصول إليهما عبر الخصائص
  • طريقة toString لاسترداد قيمة كائن [Person] كسلسلة
  • طريقة init التي سيتم استدعاؤها بواسطة Spring عند إنشاء الكائن، وطريقة close التي سيتم استدعاؤها عند تدمير الكائن

لإنشاء كائنات من النوع [Person]، سنستخدم ملف Spring [spring-config-1.xml] التالي:

<?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="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
        destroy-method="close">
        <property name="nom">
            <value>Simon</value>
        </property>
        <property name="age">
            <value>40</value>
        </property>
    </object>
    <object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
        destroy-method="close">
        <property name="nom">
            <value>Brigitte</value>
        </property>
        <property name="age">
            <value>20</value>
        </property>
    </object>
</objects>

يحدد هذا الملف

  • يحدد كائنين بمفاتيح "person1" و "person2" على التوالي من النوع [Person]
  • يقوم بتهيئة الخصائص [name, age] لكل شخص
  • ويحدد الطرق التي سيتم استدعاؤها أثناء الإنشاء الأولي للكائن [init-method] وأثناء إتلاف الكائن [destroy-method]

بالنسبة لاختباراتنا، سنستخدم فئات اختبار NUnit. ستكون الأولى على النحو التالي:

Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos

Namespace istia.st.springioc.tests

    <TestFixture()> _
    Public Class NunitTestSpringIocDemo1
        ' the test object
        Private factory As XmlObjectFactory

        <SetUp()> _
        Public Sub init()
            ' create a factory instance
            factory = New XmlObjectFactory(New FileStream("spring-config-1.xml", FileMode.Open))
            ' log
            Console.WriteLine("setup test")
        End Sub

        <Test()> _
        Public Sub demo()
            ' retrieve [Person] objects from the Spring file using their keys
            Dim personne1 As Personne = CType(factory.GetObject("personne1"), Personne)
            Console.WriteLine("personne1=" + personne1.ToString())
            Dim personne2 As Personne = CType(factory.GetObject("personne2"), Personne)
            Console.WriteLine("personne2=" + personne2.ToString())
            personne2 = CType(factory.GetObject("personne2"), Personne)
            Console.WriteLine("personne2=" + personne2.ToString())
        End Sub

        <TearDown()> _
        Public Sub destroy()
            ' we destroy the singletons
            factory.Dispose()
            ' release the factory resource
            factory = Nothing
            ' follow-up
            Console.WriteLine("teardown test")
        End Sub

    End Class
End Namespace

تعليقات:

  • لاسترداد الكائنات المحددة في ملف [spring-config-1.xml]، نستخدم كائنًا من النوع [XmlObjectFactory]. هناك أنواع أخرى من "المصانع" التي تسمح بالوصول إلى الكائنات الفردية في ملف التكوين. يتم الحصول على كائن [XmlObjectFactory] في طريقة السمة [SetUp] لفئة الاختبار وتخزينه في متغير خاص. لاحظ أن الطريقة التي تحتوي على السمة <Setup()> يتم تنفيذها قبل كل اختبار.
  • سيتم وضع ملف [spring-config-1.xml] في مجلد [bin] الخاص بالتطبيق
  • يمكن لـ Spring استخدام ملفات التكوين بتنسيقات مختلفة. يُستخدم الكائن [XmlObjectFactory] لتحليل ملف تكوين بتنسيق XML.
  • تؤدي قراءة ملف Spring إلى إرجاع كائن من النوع [XmlObjectFactory]، وهو في هذه الحالة كائن factory. باستخدام هذا الكائن، يمكن استرداد كائن فريد محدد بالمفتاح **C** باستخدام **factory.getObject(C)**.
  • تسترد الطريقة [demo] قيم الكائنات الفردية ذات المفاتيح "person1" و "person2" وتعرضها.

هيكل مشروع Visual Studio هو كما يلي:

Image

تعليقات:

  • يُسمى مشروع VS [demo1]
  • يحتوي المجلد [istia] على شفرة المصدر. ستنتقل الشفرة المُجمَّعة إلى المجلد [bin] في ملف DLL [demo1.dll]:

Image

  • يوجد ملف [spring-config-1.xml] في مجلد [bin] الخاص بالمشروع.
  • يحتوي المجلد [bin] على الملفات المطلوبة للتطبيق:
    • Spring.Core.dll، Spring.Core.xml لفئات Spring
    • log4net.dll لسجلات Spring
    • demo1.dll، وهو ملف DLL الذي تم إنشاؤه بواسطة المشروع

يتم تحميل ملف DLL [demo1.dll] الذي تم إنشاؤه بواسطة المشروع في أداة الاختبار الرسومية [Nunit-Gui 2.2]. تعرض هذه الأداة تلقائيًا فئات Nunit الموجودة في ملف DLL:

Image

يؤدي تنفيذ طريقة [demo] لاختبار NUnit إلى النتائج الموضحة أعلاه والمدرجة أدناه:

***** istia.st.springioc.tests.NunitTestSpringIocDemo1.demo
setup test
init personne [Simon,40]
personne1=[Simon,40]
init personne [Brigitte,20]
personne2=[Brigitte,20]
personne2=[Brigitte,20]
destroy personne [Simon,40]
destroy personne [Brigitte,20]
teardown test

تعليقات:

  • السطر 2: يبدأ الاختبار بتنفيذ طريقة السمة <Setup()>
  • السطر 3: العملية
            Dim personne1 As Personne = CType(factory.GetObject("personne1"), Personne)

أجبرت على إنشاء الفول [person1]. ولأننا كتبنا [init-method="init"] في تعريف الفول [person1]، تم تنفيذ طريقة [init] للكائن [Person] الذي تم إنشاؤه. يتم عرض الرسالة المقابلة.

init personne [Simon,40]
  • السطر 4: العملية
            Console.WriteLine("personne1=" + personne1.ToString())

عرضت قيمة الكائن [Person] الذي تم إنشاؤه.

personne1=[Simon,40]
  • السطران 5-6: يحدث الأمر نفسه بالنسبة لـ bean المفتاح [person2].
init personne [Brigitte,20]
personne2=[Brigitte,20]
  • السطر 7: العملية الأخيرة
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())

أدى ذلك إلى ظهور سطر واحد فقط من النتائج:

personne2=[Brigitte,20]

وبالتالي، لم يؤد ذلك إلى إنشاء كائن جديد من النوع [Person]. لو كان الأمر كذلك، لظهرت طريقة [init]، وهو ما لم يحدث هنا. هذا هو مبدأ singleton. بشكل افتراضي، يقوم Spring بإنشاء مثيل واحد فقط من beans في ملف التكوين الخاص به. إنها خدمة مرجعية للكائنات. إذا طُلبت مرجعية لكائن لم يتم إنشاؤه بعد، فإنه يقوم بإنشائه وإرجاع مرجعية إليه. إذا كان الكائن قد تم إنشاؤه بالفعل، فإن Spring يقوم ببساطة بإرجاع مرجعية إليه.

  • الأسطر 8–10: يتم تنفيذ طريقة السمة <TearDown()> (السطر 10). داخلها، يتم تنفيذ العملية
            the singletons are destroyed
            ' on détruit les singletons
            factory.Dispose()

يؤدي إلى تدمير العناصر الفردية التي أنشأتها Spring. وهذا يؤدي إلى قيام كل منها بتنفيذ طريقة [close] الخاصة بها لأننا كتبنا، في ملف التكوين، لكل منها: "destroy-method=close". وهذا ما تشير إليه الشاشات:

destroy personne [Simon,40]
destroy personne [Brigitte,20]

الآن بعد أن غطينا أساسيات تكوين Spring، سنتمكن من المضي قدماً في شرحنا بوتيرة أسرع قليلاً.

5.2. المثال 2

لننظر إلى فئة [Car] الجديدة التالية:

Imports istia.st.springioc.demos

Namespace istia.st.springioc.demos

    Public Class Voiture
        ' private fields
        Private _marque As String
        Private _type As String
        Private _propriétaire As Personne

        ' public property
        Public Property marque() As String
            Get
                Return _marque
            End Get
            Set(ByVal Value As String)
                _marque = Value
            End Set
        End Property

        Public Property type() As String
            Get
                Return _type
            End Get
            Set(ByVal Value As String)
                _type = Value
            End Set
        End Property

        Public Property propriétaire() As Personne
            Get
                Return _propriétaire
            End Get
            Set(ByVal Value As Personne)
                _propriétaire = Value
            End Set
        End Property

        ' default builder
        Public Sub New()

        End Sub

        ' three-parameter constructor
        Public Sub New(ByVal marque As String, ByVal type As String, ByVal propriétaire As Personne)
            ' initialize private fields via their associated properties
            With Me
                .marque = marque
                .type = type
                .propriétaire = propriétaire
            End With
        End Sub

        ' car object identity string
        Public Overrides Function tostring() As String
            Return String.Format("[{0},{1},{2}]", marque, type, propriétaire.ToString)
        End Function

        ' init-destroy methods
        Public Sub init()
            Console.WriteLine("init voiture {0}", Me.ToString)
        End Sub

        Public Sub destroy()
            Console.WriteLine("destroy voiture {0}", Me.ToString)
        End Sub

    End Class
End Namespace

تحتوي الفئة على:

  • ثلاثة حقول خاصة: _type و_make و_owner. يمكن تهيئة هذه الحقول وقراءتها عبر الخصائص العامة. كما يمكن تهيئتها باستخدام منشئ Car(String, String, Person). تحتوي الفئة أيضًا على منشئ بدون حجج يسمح لك أولاً بإنشاء كائن [Car] غير مهيأ ثم تهيئته عبر خصائصه العامة.
  • طريقة toString لاسترداد قيمة كائن [Car] كسلسلة
  • طريقة init التي سيستدعيها Spring فور إنشاء الكائن، وطريقة destroy التي سيتم استدعاؤها عند إزالة الكائن

لإنشاء كائنات من النوع [Car]، سنستخدم ملف Spring [spring-config-2.xml] التالي:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
     <!-- people -->
    <object id="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
        destroy-method="close">
        <property name="nom">
            <value>Simon</value>
        </property>
        <property name="age">
            <value>40</value>
        </property>
    </object>
    <object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
        destroy-method="close">
        <property name="nom">
            <value>Brigitte</value>
        </property>
        <property name="age">
            <value>20</value>
        </property>
    </object>
     <!-- a car -->
    <object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
        destroy-method="destroy">
        <constructor-arg index="0">
            <value>Peugeot</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>307</value>
        </constructor-arg>
        <constructor-arg index="2">
            <ref object="personne2"/>
        </constructor-arg>
    </object>
</objects>

يضيف هذا الملف كائنًا بالمفتاح "car1" من النوع [Car] إلى التعريفات السابقة. لتهيئة هذا الكائن، كان بإمكاننا كتابة:

    <object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
        destroy-method="destroy">
        <constructor-arg index="0">
            <value>Peugeot</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>307</value>
        </constructor-arg>
        <constructor-arg index="2">
            <ref object="personne2"/>
        </constructor-arg>
    </object>

بدلاً من اختيار الطريقة التي تم عرضها سابقًا، اخترنا هنا استخدام منشئ الفئة Car(String, String, Person). بالإضافة إلى ذلك، يحدد العنصر الفردي [car1] الطريقة التي سيتم استدعاؤها أثناء الإنشاء الأولي للكائن [init-method] والطريقة التي سيتم استدعاؤها أثناء تدمير الكائن [destroy-method].

لإجراء اختباراتنا، سنستخدم فئة اختبار NUnit التالية [NunitTestSpringIocDemo2]:

Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos

Namespace istia.st.springioc.tests

    <TestFixture()> _
    Public Class NunitTestSpringIocDemo2
        ' the singletons factory
        Private factory As XmlObjectFactory

        <SetUp()> _
        Public Sub init()
            ' create a factory instance
            factory = New XmlObjectFactory(New FileStream("spring-config-2.xml", FileMode.Open))
            ' log
            Console.WriteLine("setup test")
        End Sub

        <TearDown()> _
    Public Sub destroy()
            ' we destroy the singletons
            factory.Dispose()
            ' we free the factory
            factory = Nothing
            ' follow-up
            Console.WriteLine("teardown test")
        End Sub

        <Test()> _
        Public Sub demo2()
            ' singleton retrieval [car1]]
            Dim voiture1 As Voiture = CType(factory.GetObject("voiture1"), Voiture)
            Console.WriteLine("Voiture1=[{0}]", voiture1)
        End Sub

    End Class
End Namespace

تعليقات:

  • تظل طرق السمات <Setup()> و <TearDown()> دون تغيير.
  • تسترد الطريقة [demo2] مرجعًا إلى العنصر الفردي [car1] وتعرضه.

تظل بنية مشروع Visual Studio كما كانت من قبل. تمت إضافة فئتين جديدتين فقط، إلى جانب ملف تكوين Spring:

Image

يؤدي تشغيل اختبار NUnit إلى الحصول على النتائج التالية:

Image

دعونا نعلق على مخرجات الشاشة:

1
2
3
4
5
6
7
8
***** istia.st.springioc.tests.NunitTestSpringIocDemo2.demo2
setup test
init personne [Brigitte,20]
init voiture [Peugeot,307,[Brigitte,20]]
Voiture1=[[Peugeot,307,[Brigitte,20]]]
destroy voiture [Peugeot,307,[Brigitte,20]]
destroy personne [Brigitte,20]
teardown test

تعليقات:

  • السطر 2: يتم تنفيذ طريقة السمة [Setup] قبل كل اختبار، وهنا [demo]
  • السطر 3: يبدأ Spring في إنشاء الكائن الفردي [car1]. هذا الكائن الفردي يعتمد على الكائن الفردي [person2]، الذي لا يوجد بعد. لذلك يتم إنشاء هذا الأخير وتنفيذ طريقة [init] الخاصة به.
  • السطر 4: يمكن الآن إنشاء الكائن الفردي [car1]. ثم يتم تنفيذ طريقة [init] الخاصة به.
  • السطر 5: تعرض طريقة [demo2] قيمة الكائن الفردي [car1]
  • الأسطر 6-8: يتم تنفيذ طريقة [TearDown] الخاصة بالاختبار. يتم تدمير الكائنات الفردية، مما يؤدي إلى تنفيذ طرق [destroy] الخاصة بها

5.3. المثال 3

نقدم الفئة الجديدة التالية [PersonGroup]:

Imports istia.st.springioc.demos

Namespace istia.st.springioc.demos

    Public Class GroupePersonnes
        ' private fields
        Private _membres() As Personne
        Private _groupesDeTravail As Hashtable

        ' public property
        Public Property membres() As Personne()
            Get
                Return _membres
            End Get
            Set(ByVal Value() As Personne)
                _membres = Value
            End Set
        End Property

        Public Property groupesDeTravail() As Hashtable
            Get
                Return _groupesDeTravail
            End Get
            Set(ByVal Value As Hashtable)
                _groupesDeTravail = Value
            End Set
        End Property

        ' default builder
        Public Sub New()

        End Sub

        ' object identity string GroupeDePersonnes
        Public Overrides Function tostring() As String
            ' browse the list of group members
            Dim identité As String = "[membres=("
            Dim i As Integer
            For i = 0 To membres.Length - 2
                identité += membres(i).ToString + ","
            Next
            identité += membres(i).ToString + "), groupes de travail=("
            ' browse the workgroup dictionary
            Dim clés As IEnumerator = groupesDeTravail.Keys.GetEnumerator
            Dim clé As Object
            While clés.MoveNext
                clé = clés.Current
                identité += String.Format("[{0},{1}] ", clé, groupesDeTravail(clé))
            End While
            ' we return the result
            Return identité + "]"
        End Function

        ' init-destroy methods
        Public Sub init()
            Console.WriteLine("init GroupeDePersonnes {0}", Me.ToString)
        End Sub

        Public Sub destroy()
            Console.WriteLine("destroy GroupeDePersonnes {0}", Me.ToString)
        End Sub

    End Class
End Namespace

عضواها الخاصان هما:

_members: مصفوفة تضم الأشخاص الأعضاء في المجموعة

_workGroups: قاموس يربط كل شخص بمجموعة عمل

يتم إتاحة الوصول إلى هذه العناصر الخاصة عبر خصائص عامة. الهدف هنا هو توضيح كيف تتيح لك Spring تهيئة كائنات معقدة، مثل تلك التي تحتوي على حقول مصفوفة أو قاموس.

سيكون ملف تكوين Spring [spring-config-3.xml] كما يلي:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
     <!-- people -->
    <object id="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
        destroy-method="close">
        <property name="nom">
            <value>Simon</value>
        </property>
        <property name="age">
            <value>40</value>
        </property>
    </object>
    <object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
        destroy-method="close">
        <property name="nom">
            <value>Brigitte</value>
        </property>
        <property name="age">
            <value>20</value>
        </property>
    </object>
     <!-- a car -->
    <object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
        destroy-method="destroy">
        <constructor-arg index="0">
            <value>Peugeot</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>307</value>
        </constructor-arg>
        <constructor-arg index="2">
            <ref object="personne2" />
        </constructor-arg>
    </object>
     <!-- a group of people -->
    <object id="groupe1" type="istia.st.springioc.demos.GroupePersonnes" init-method="init"
        destroy-method="destroy">
        <property name="membres">
            <list>
                <ref object="personne1" />
                <ref object="personne2" />
            </list>
        </property>
        <property name="groupesDeTravail">
            <dictionary>
                <entry key="Brigitte">
                    <value>Marketing</value>
                </entry>
                <entry key="Simon">
                    <value>Ressources humaines</value>
                </entry>
            </dictionary>
        </property>
    </object>
</objects>
  1. تسمح لك علامة <list> بتهيئة حقل من نوع المصفوفة أو نوع IList بقيم مختلفة.
  1. تسمح لك العلامة <dictionary> بالقيام بنفس الشيء مع حقل ينفذ واجهة IDictionary

في اختباراتنا، سنستخدم فئة اختبار NUnit التالية [NunitTestSpringIocDemo3]:

Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos

Namespace istia.st.springioc.tests

    <TestFixture()> _
    Public Class NunitTestSpringIocDemo3
        ' the test object
        Private factory As XmlObjectFactory

        <SetUp()> _
        Public Sub init()
            ' create a factory instance
            factory = New XmlObjectFactory(New FileStream("spring-config-3.xml", FileMode.Open))
            ' log
            Console.WriteLine("setup test")
        End Sub

        <TearDown()> _
        Public Sub destroy()
            ' we destroy the singletons
            factory.Dispose()
            ' follow-up
            Console.WriteLine("teardown test")
        End Sub

        <Test()> _
    Public Sub demo3()
            ' retrieving the singleton [group1]
            Dim groupe1 As GroupePersonnes = CType(factory.GetObject("groupe1"), GroupePersonnes)
            Console.WriteLine("groupe1={0}", groupe1)
        End Sub

    End Class
End Namespace

تسترد الطريقة [demo3] العنصر الفردي [group1] وتعرضه.

تظل بنية مشروع Visual Studio كما هي، باستثناء أنه يحتوي الآن على فئتين إضافيتين وملف تكوين.

Image

يؤدي تنفيذ الطريقة [demo3] في اختبار NUnit إلى النتائج التالية:

Image

تعليقات:

***** istia.st.springioc.tests.NunitTestSpringIocDemo3.demo3
setup test
init personne [Simon,40]
init personne [Brigitte,20]
init GroupeDePersonnes [membres=([Simon,40],[Brigitte,20]), groupes de travail=([Brigitte,Marketing] [Simon,Ressources humaines] ]
groupe1=[membres=([Simon,40],[Brigitte,20]), groupes de travail=([Brigitte,Marketing] [Simon,Ressources humaines] ]
destroy GroupeDePersonnes [membres=([Simon,40],[Brigitte,20]), groupes de travail=([Brigitte,Marketing] [Simon,Ressources humaines] ]
destroy personne [Simon,40]
destroy personne [Brigitte,20]
teardown test
  • السطر 2: طريقة السمة [Setup] التي يتم تنفيذها قبل كل اختبار، وهنا [demo]
  • السطران 3-4: يبدأ Spring في إنشاء الكائن الفردي [group1]. يعتمد هذا الكائن الفردي على الكائنين الفرديين [person1، person2]، اللذين لا يزالان غير موجودين. يتم إنشاء هذين الكائنين الفرديين وتنفيذ طريقتيهما [init].
  • السطر 5: يمكن الآن إنشاء الكائن الفردي [group1]. ثم يتم تنفيذ طريقة [init] الخاصة به.
  • السطر 6: تعرض طريقة [demo3] قيمة الكائن الفريد [group1]
  • الأسطر 7-10: يتم تنفيذ طريقة [TearDown] الخاصة بالاختبار. يتم تدمير الكائنات الفردية، مما يؤدي إلى تنفيذ طرق [destroy] الخاصة بها