moji5
25th November 2009, 07:56 PM
دو اصطلاح Tangling و Scattering
● پراكندگي: (Scattering) پيادهسازي يك Concern، پراكنده شده است هر گاه كد آن بين چند ماجول پخش شده باشد.
● پيچش: (Tangling) پيادهسازي يك Concern، پيچيده شدهاست هر گاه كد آن با كد يك Concern ديگر مخلوط شده باشد.
پراكندگي و پيچش در Aspect Oriented بهعنوان علائم يك Cross-Cutting Concern در نظر گرفته ميشوند. در حقيقت عموماً Concernهايي كه چنين خصوصياتي را در پيادهسازيهاي غير AOP داشته باشند، مورد بحث قرار ميگيرند.
AOP در عمل
بايد اعتراف كرد كه با وجود ارزشمند بودن توضيحات و تئوريهاي برنامهنويسي، تا وقتي كه آنها را در عمل، به وسيله برنامهنويسي مورد آزمايش قرار ندهيم، در حقيقت كار خاصي انجام ندادهايم. در ادامه سعي ميكنيم مفاهيم مطرح شده را به همراه كد آن مورد بررسي قرار دهيم تا علاوه بر معرفي و تعريف مفاهيم موجود در AOP، با نحوه به كارگيري آنان نيز آشنا شويم، اما قبل از هر چيز بايد به اين سؤال پاسخ دهيم كه كجا ميتوانيم AOPبنويسيم؟
براي پاسخ دادن به اين پرسش، ابتدا بايد ببينيد AOP چگونه اجرا ميشود. براي نوشتن يك پروژه به صورت AOPشما ابتدا هر دو بخش كد خود را (اعم از روند كاري اصلي برنامه و كدهاي مربوط به Aspectها) به صورت جداگانه در يك زبان شيءگرا مينويسيد و سپس موجودي به نام Aspect Weaver آن دو بخش را با يكديگر تركيب كرده و كد نهايي را ميسازد.
جداسازي كد اصلي و كد Concernها (كد اصلي نيازي به اطلاع از بخش Concernها ندارد) به افزايش قابليت استفاده دوباره (Reusability) و قابليت نگهداري (Maintainability) پروژه كمك شاياني ميكند. شكل 1 نحوه كار Weaverها را نشان ميدهد.
دو نمونه بارز از ابزارهايي كه ميتوان با استفاده از آنها برنامههاي Aspect-Oriented نوشت AspectJ و AspectWerkz هستند.
AspectJ يك Extension است كه براي زبان جاوا در زيراكس پارك و توسط گروه بهوجود آورنده AOP نوشته شدهاست. اين Extension به دو صورت تنها و تعبيه شده در Eclipse IDE عرضه ميشود. AspectJ پتانسيل پشتيباني از مدلRT Weaver را نيزدارد، اما براي بهره بردن از تواناييهاي آن بايد از آن بهصورت يك Compile-time Weaver استفاده كرد (هماكنون به صورت Compile-time و loadtime عرضه ميشود).
Aspect Weaver !
در حقيقت Aspect Weaver كد اصلي و كد Aspectها را بهعنوان ورودي ميگيرد و محصول نهايي را توليد ميكند. توجه به اين موضوع ضروري است كه نگاه كلي به Weaverها مانند يك كامپايلر نيست، زيرا قرار نيست كه تمام كارهاي پيچيدهاي را كه يك كامپايلر انجام ميدهد.
Weaver نيز در مورد تركيب دو بخش كد انجام دهد. در حقيقت، همانطور كه خود كيزالس هم اشاره مي كند، وظيفه Weaverها فقط Integration (مجتمعسازي) است.
تكنيكهاي اوليه Weaving به دو دسته عمده تقسيم ميشوند: Compile Time) CT) و Run-Time) RT Weaver)هايCT. همانطور كه از نامشان پيدا است، تمام كارهاي مربوط به تركيب كد را در زمان كامپايل انجام ميدهند و در حقيقت كد نهايي كه اجرا ميشود، محصول كامل است.
در مقابل RTها اينكار را در زمان اجرا انجام ميدهند و ايجاد ارتباط را تا آن وقت به تأخير مياندازند. Weaverهاي CT با توجه به اين كه تمام فرآيند ايجاد ارتباط را در ابتداي كار و هنگام كامپايل انجام ميدهند بسيار سريعتر از RTها عمل ميكنند، اما در مقابل RTها هم اين مزيت را دارند كه در صورت تغيير كد Aspectها نيازي به انجام دوباره عمليات Weaving نيست و برخلاف CTها در Run-Time Weaver تغييرات در كد بدون نياز به هيچ كاري سريع منعكس ميشوند.
همانطور كه در بالا ذكر كرديم، تكنيكهاي Weaving ديگري نيز وجود دارند كه در واقع فضاي بين Compile-time و Run-time قرار ميگيرند. اين دو تكنيك Weaving را (post-compile time (binary و Load-time مينامند.
Binary Weaving در حقيقت عمليات Weaving را روي byte code انجام ميدهد (پس از كامپايل). Load time Weaving نيز يك نوع binary Weaving است. با اين تفاوت كه عمليات Weaving را تا زماني كه يك كلاس به Class loader معرفي نشده است، به تأخير مياندازد.
(اين توانايي ميتواند برخي از نقصهاي مدل Compile-time را برطرف كند، زيرا شما ميتوانيد بدون كامپايل كردن دوباره كد اصلي خود (Aspect ،(Business Logicهايي به برنامه اضافه كرده و سپس آنها را به پروژه اصلي لينك دهيد). در حقيقت، در اين مدل تا جايي كه ممكن است عمليات Weaving به تأخير ميافتد و تا مرحله Load شدن كلاسهاي موردنياز هيچ تركيبي انجام نميشود.
AspectJ محيطي ساده و بسيار كارا دارد و هماكنون محبوبترين ابزار برنامهنويسي Aspect-Oriented است. نسخههاي جديدتر AspectJ بهطور كامل با محيط توسعه Eclipse هماهنگي دارند و ميتوان از تمام امكانات Eclipse در مورد Aspectها نيز سود برد.
توجه به اين نكته ضروري است كه AspectJ تغييراتي در Syntax زبان به وجود ميآورد كه اين موضوع ميتواند باعث بروز مشكلاتي شود (Eclipse با توجه به اضافه كردن Keywordهاي مربوط به برنامهنويسي AOP اين مشكل را ندارد).
اين مشكلات باعث شدند تا ابزارهاي ديگري به وجود آيند كه به اين تغييرات گرامري در زبان برنامهنويسي نيازي نداشته باشند. يك نمونه مشهور از اين زبانها AspectWerkz است. AspectWerkz در حال حاضر از هر سه مدل Compile-time ،Load-time و Run time استفاده ميكند.
خصوصيت بارز AspectWerkz اين است كه Syntax زبان را تغيير نميدهد و در حقيقت تغييرات را با استفاده از Annotation انجام ميدهد كه به يك ساختار زباني جديد نيازي ندارد.
در حال حاضر، دو پروژه AspectJ و AspectWerkz با يكديگر تركيب شدهاند تا بتوانيم از قابليتهاي هر دو به صورت همزمان استفاده كنيم.
تمام اين مقدمهها براي اين ذكر شد كه شما كمي بيشتر با نحوه عملكرد داخلي ابزارهاي توسعه برنامههاي Aspect-Oriented آشنا شويد. در قسمت بعد وارد بخش كدنويسي ميشويم.
كدنويسي در AspectJ
در اين قسمت به بررسي پيادهسازي مفاهيم مختلف AOP توسط AspectJ ميپردازيم. هر چند بررسي تمام خصوصيات AspectJ خود يك مقاله جداگانه ميطلبد، اما سعي ميكنيم تا مفاهيم اصلي پركاربرد را در نوشتن يك برنامه Aspect-Oriented مطرح كنيم. در شمارههاي آينده، بهطور عميقتري به بررسي جنبههاي عملي AOPميپردازيم.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/c140ac6e28bcd09580ec5129656d8f0b.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/c140ac6e28bcd09580ec5129656d8f0b.jpg)
Annotation
شايد تعريف كردن Annotation كمي دشوار باشد. در حقيقت آنها برچسبها و شايد پايگاهدادههايي هستند كه اطلاعاتي درباره برنامه شما ميدهند.
در حقيقت، Annotationها تغييري در Semantic زبان ايجاد نميكنند، اما ميتوانند براي ابزارهاي مختلفي كه در روند اجراي برنامه دخيل هستند، پيغامهايي در برداشته باشند.
در حقيقت، ميتوان Annotationها را علامتهايي تلقي كرد كه كامپايلر آنها را بهعنوان متاديتا در جايي ذخيره ميكند. سپس VM (سرنام Virtual Machine) با ديدن آنها تعيين ميكند كه چگونه رفتار بعضي از Elementهاي برنامه را تغيير دهد.
توضيح كامل Annotationها در اين مقاله نميگنجد. پس به ذكر همين مقدمات بسنده ميكنيم.
Join Point
Join Point، در حقيقت مفهوم جديدي نيست. ما از اين مفهوم قبلاً بارها استفاده كردهايم ، فقط شايد اسمي براي آن نداشتهايمJoint Point .ها نقاط خوش تعريف خاصي در برنامه هستند. بهعنوان مثال، نقطه فراخواني يا بازگشت متد.
Aspect
شروع كاري ما براي آوردن AOP به صحنه عمل كليدواژه Aspect است. Aspectها تقريباً مشابه كليدواژه class در نوشتن كدهاي معمولي شيءگرا هستند. هر Aspect بايد در يك فايل جداگانه كه نام آن با نام Aspect يكي است، تعريف شود:
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/a082553765be409c8e014a0080b6f276.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/a082553765be409c8e014a0080b6f276.jpg)
يك Aspect مجموعهاي از point-cutها و Adviceها است.
Point-cut
در AOP شما نياز داريد نقاط خاصي را بهعنوان نقاطي كه موجب فراخواني يك Aspect ميشوند، تعريف كنيد. اين كار توسط Point-cutها انجام ميگيرد. در حقيقت، Point-cutها يك مجموعه از Join Pointها را تعريف ميكنند و ميتوانيم آنها را بهعنوان يك Query براي انتخاب joint pointها در نظر بگيريم.
در حقيقت، تعريف يك point-cut بيانكننده اين موضوع است كه join Pointهاي كد اصلي (Business Logic) در چه جاهايي قرار دارند. به زبان سادهتر point-cut شرايط نقطههايي را تعريف ميكند كه بايد با رسيدن به آنها در كد اصلي اين Aspect اجرا شود.
اين مكانيسم، به مكانيسم فراخواني روش بسيار شبيه است. در حقيقت، در اينجا point-cutها حكم يك روش و join point حكم نقطهاي است كه دستور فراخواني متد در آن قرار دارد.
به عنوان مثال:
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/7017532d59c4e6aabc79ef4466c1b349.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/7017532d59c4e6aabc79ef4466c1b349.jpg)
اين Pointcut مشخص ميكند كه پس از اجراي متد از كلاس Data كه با Write شروع ميشوند و هيچ ورودياي ندارند، اين Aspect فراخواني ميشود.
ميتوانيم در يك Aspect چند pointcut داشته باشيم. كد پايين سه pointcut تعريف ميكند.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/9091760640750b6244d15321a99d2754.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/9091760640750b6244d15321a99d2754.jpg)
در كد بالا سه pointcut تعريف شده است.
PC1: وقتي متدي از كلاس Data كه با Write شروع ميشود و آرگومان String ميگيرد، فراخواني ميشود، pointcut PC1 رخ ميدهد.
PC2: با فراخواني هر متدي (هر نامي با هر ورودياي) كه در داخل كلاس Data است pointcut PC2 رخ ميدهد.
PC3: با اجراي بدنه اصلي متد SecurityChecking ،pointcut PC3 فراخواني ميشود.
Advice
كد تكميلي كه به سيستم اضافه ميشود تا كارهاي مربوط به يك Concern را انجام دهد، Advice نام دارد. Adviceها در حقيقت همان قطعهكدهاي معمولي هستند كه حكم انجام عملياتي يك Concern را دارند.
هر Advice كاري را كه يك Aspect بايد در ازاي وقوع يك اتفاق خاص (pointcut) انجام دهد، مشخص مي كند. باز هم با تعبير متدگونه advice در واقع حكم بدنه متد را دارد (point-cut حكم اعلان متد را دارد).
كاري كه كد زير انجام ميدهد اين است كه قبل از اجراي هر متدي از كلاس Data كه با Write شروع ميشود و هيچ ورودي ندارد، هويت كاربر حال حاضر بررسي ميشود.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/7f01912b94a3241c4634b7a5f5909c36.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/7f01912b94a3241c4634b7a5f5909c36.jpg)
Adviceها به سه نوع تقسيم ميشوند:
1-Before: اين Adviceها بلافاصله بعد از اينكه يك join point اتفاق افتاد و پيش از اين كه برنامه ادامه يابد اجرا ميشوند. بهعنوان مثال، اگر join point فراخواني يك ljn باشد، بلافاصله پس از فراخواني ljn (و پيش از اجراي بدنه آن و برگرداندن يك مقدار)، قطعه كد Advice اجرا ميشود.
2- After: اين نوع Advice نيز همانطور كه از نامش پيدا است، درست برخلاف Before عمل ميكند. Adviceهاي نوع After پس از پايان اجراي join point، اجرا ميشوند. در حقيقت، در مثال بالا (فراخواني متد) يك Advice از اين دسته پس از پايان روند كاري متد (برگرداندن مقدار- وقوع exception) اجرا ميشود.
3- Around: اين نوع بسيار جالب از Adviceها ميتوانند كلاً اجراي join point را ناديده بگيرند. در حقيقت Adviceهاي نوع Around دقيقاً به جاي join point اجرا ميشوند (اجراي join point ناديده گرفته ميشود و به جاي آن اين Advice اجرا ميشود). معني اين امر درباره متدها اين است كه بدنه اصلي متد اجرا نميشود و به جاي آن كد موجود در Advice اجرا ميشود.
يك كد تركيبي
كد زير تركيبي از سه pointcut و سه Advice را نشان ميدهد.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/d759a0c464f4bc512d5d5eed904d7bfb.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/d759a0c464f4bc512d5d5eed904d7bfb.jpg)
كد زير نيز كد كلاس Data است كه شامل روش main نيز است.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/3d2ecdc4c7b118eec19b672350a8a330.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/3d2ecdc4c7b118eec19b672350a8a330.jpg)
پس از كامپايل و اجراي Data خروجي زير را دريافت ميكنيم:
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/c825970dbc1192983f4c6fa799c4bc68.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/c825970dbc1192983f4c6fa799c4bc68.jpg)
به ترتيب خروجيها دقت كنيد. در PC1 نحوه مشخص كردن آرگومان ورودي متد و استفاده از آن را مشاهده ميكنيد. در PC2 مطابق تعريف تمام پيغامهاي Logging همانطور كه در كد pcAspect2 تعريف شده بود، بعد از اجراي تمام متدها چاپ شده است.
در PC3 نحوه استفاده از شيء كلاس مبدأ را مشاهده ميكنيد. در ضمن كد متد SecurityChecking در كلاس Dataاصلاً اجرا نشده است، زيرا ما Adviceاي را كه بايد درباره اين متد اجرا ميشد، around در نظر گرفتهايم و همانطور كه در بالا ذكر شد Adviceهاي نوع around كلاً اجراي عادي join point را متوقف ميكنند.
احتمالاً با بررسي اين كد، متد كار Adviceها و point-cutها تا حدود زيادي براي شما روشن شدهاست. البته پيادهسازيهاي اين مفهوم ريزهكاريهاي بسيار پيچيدهتري نيز دارند كه در اين مقاله نميگنجد.
Inter-type Declaration
Inter-type Declaration امكان جالبي است كه به برنامهنويس اجازه ميدهد ساختار elementهاي برنامه را در صورت اجرا شدن يك Aspect تغيير دهد، اما تغيير ساختار به چه معنا است؟
- اضافه كردن متد، Constructor يا Field به ديگر Typeهاي برنامه (كلاس يا Aspectهاي ديگر)
- اضافه كردن يا تغيير اينترفيسها و كلاسهاي والد تعيين شده براي Typeهاي ديگر
- تغيير حالت exceptionها از checked به unchecked
- اضافه كردن warningهاي دلخواه و... .
در حقيقت، با استفاده از اين توانايي يك Aspect ميتواند بهطور كامل وظيفه تعريف، نگهداري و اجراي elementهاي مورد استفاده خود را بر عهده بگيرد و اين موضوع باعث ميشود تا تمام موضوعات مربوط به Aspectنويس در همان فايل aspect خلاصه شود.
نوشتن كدي كه از Inter-type Declaration عموماً چيز جديدي در بر ندارد و در بيشتر موارد مانند روش كلاسيك كد نوشتن ما است. بهعنوان مثال، كد زير به ترتيب يك Field و يك متد براي كلاس AirPlane تعريف ميكند.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/d81e3689e73d8462b456a0e83ce48823.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/d81e3689e73d8462b456a0e83ce48823.jpg)
قطعه كد زير نيز يك Constructor دو Argumentي براي كلاس AirPlane تعريف ميكند.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/9cf6f5b4d49d93d9d84e6a7ce0dbe45a.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/9cf6f5b4d49d93d9d84e6a7ce0dbe45a.jpg)
كد زير نيز كلاس والد كلاس AirPlane را كلاس SomethingFly تعيين ميكند.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/e93273276ce59913c6e89c12e1fa4ee5.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/e93273276ce59913c6e89c12e1fa4ee5.jpg)
چگونه اينها را اجرا كنيم؟
اجراي يك برنامه نوشته شده تحت AOP در AspectJ با اجراي يك برنامه معمولي جاوا چندان تفاوتي ندارد. ابتدا نحوه اجراي يك برنامه را كه براي Weaving از تكنيك Compile-time استفاده ميكند، نشان ميدهيم.
بهطور كلي براي كامپايل كردن اين برنامهها، AspectJ دستور ajc را در نظر گرفته است. سادهترين نوع كامپايل به صورت زير است.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/826bf9d36688b4e8d685d1984fd8c29b.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/826bf9d36688b4e8d685d1984fd8c29b.jpg)
براي اجراي برنامه نيز نبايد مشكلي داشته باشيم. زيرا تركيب (Weaving) بين Aspect و كد اصلي انجام شده است و ما هماكنون يك فايل class. كامل در اختيار داريم. پس ميتوانيم به سادگي آن را اجرا كنيم.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/bed2dc7cb0e5717f105bd63c0a8204c8.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/bed2dc7cb0e5717f105bd63c0a8204c8.jpg)
مدل Load-time
اكنون سعي مي كنيم با ذكر يك مثال، نحوه اجراي يك برنامه به صورت Load-time و اثرات آن را مشاهده كنيم.
كد ارائهشده در بخش <يك كد تركيبي> در قسمت قبل را در نظر بگيريد. فرض كنيد ميخواهيم يك Aspect ديگر به نام «pcAspect3» داشتهباشيم. «pcAspect3.java» به صورت زير در نظر گرفته ميشود:
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/3c07a85956422ac218a3d17fc311d435.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/3c07a85956422ac218a3d17fc311d435.jpg)
براي اضافه كردن aspect2 به پروژه ميتوان دو كار انجام داد:
1- مانند قبل عمل ميكنيم و سه فايل data و pcAspect2 و pcAspect3 را با يكديگر كامپايل ميكنيم. عمليات Weaving به صورت Compile-time انجام ميشود و با اجراي كلاس Data نتيجه موردنظر را دريافت ميكنيم.
2- از load-time Weaving استفاده كنيم (براي تعريف load-time Weaving به كادر Aspect Weaver مراجعه كنيد).
اصولاً در بعضي موارد ممكن است كامپايل كردن دوباره تمام پروژه بسيار هزينهبر باشد. با استفاده از load-time weaving نيازي به كامپايل كردن دوباره هيچكدام از فايلهاي قبلي نيست. بلكه ما فقط كلاس فايل جديدمان را به بقيه فايلها اضافه ميكنيم، سپس با اجراي دوباره پروژه تمام تغييرات لازم خود به خود، اعمال ميشود.
اما انجام اين كار به چه شكل است؟
همانطور كه ذكر شد ما بايد به نحوي بعد از كامپايل فايل اصلي يك Aspect را به آن معرفي كنيم.
اين اعلام به وسيله META-INF/aop.xml (فايل aop.xml درون دايركتوري META-INF) انجام ميگيرد. فايل aop.xml ميتواند به روشهاي مختلفي ايجاد شود. استفاده از دستور زير به هنگام كامپايل فايل اوليه يك زير دايركتوري در دايركتوري فعلي، META-INF/aop.xml را ايجاد ميكند.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/29ed0dc679088e6323d7d8597e527ca2.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/29ed0dc679088e6323d7d8597e527ca2.jpg)
اين كار (ساخت فايل aop.xml) به صورت دستي نيز ميتواند انجام شود. اكنون بايد كد جديد خود را به قسمت اصلي برنامه اضافه كنيم. براي اين كار ابتدا كد جديد را به همان روش هميشگي كامپايل ميكنيم.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/7015389b7f9167a07bfa6c2be6989253.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/7015389b7f9167a07bfa6c2be6989253.jpg)
اعلان Aspectها در فايل aop.xml بسيار ساده است. فقط كافي است aop.xml را به صورت زير اصلاح كنيم.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/2c4817368c75a7578f084dca72749b2d.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/2c4817368c75a7578f084dca72749b2d.jpg)
اكنون با اجراي Data هر دو Aspect در پروژه اثر ميكنند. توجه كنيد كه در اينجا ديگر نميتوانيد از جاوا براي اجراي Data استفاده كنيد، زيرا در اينجا عمليات Weaving در هنگام Load شدن كلاسها و بعد از اجرا انجام ميشود. پس بايد از دستور مخصوص اجرا بهصورت Load-time Weaving استفاده كنيد.
اين دستور aj5) aj براي Java5) است.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/e45f3ae2a9bbb80ae71063e8cee2b257.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/e45f3ae2a9bbb80ae71063e8cee2b257.jpg)
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/631dd786dea83044dd4f8a615cf68fd0.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/631dd786dea83044dd4f8a615cf68fd0.jpg)
و اما بعد...
اما سرنوشت AOP به كجا ميانجامد؟ در طول ساليان اخير ايدههاي مختلفي براي ايجاد يك پارادايم برتر ارائه شدهاست، اما همچنان مدل شيءگرا، بهعنوان يك 4Silver Bullet براي عرصه پارادايمها باقي مانده و سالها است كه محبوبترين شيوه توليد نرمافزار است.
AOP در تقابل با برنامهنويسي شيءگرا قرار نميگيرد، اما همچنان عدهاي با ترديد به توليد كيزالس و گروهش مينگرند و استفاده از آن در مقياس وسيع را نيازمند تحقيق و بررسي بيشتري ميدانند. در اين ميان، عدهاي پا را از اين نيز فراتر ميگذارند و AOP را ايدهاي ناكارآمد در عمل و غيرقابل استفاده براي پروژههاي بزرگ مينامند. البته در مقابل اين دسته، افرادي نيز وجود دارند كه AOP را انقلابي در زمينه پارادايمهاي برنامهنويسي معرفي و استفاده از آن را به شدت پيشنهاد ميكنند.5
اما از اين افراطيگريها چيزي نصيب مهندسان نرمافزار نميشود. هر نظري كه درباره AOP داشته باشيد، خواه طرفدار پر و پا قرص، خواه مخالف شديد يا يك آدم بيطرف، در تمام موارد چيزي كه وجود دارد اين است: AOP فقط يك پارادايم برنامهنويسي است.
قرار نيست با AOP مشكلاتي را حل كنيد كه قبلاً نميتوانستيد آن را حل كنيد يا به بيان ديگر قرار نيست اين پارادايم براي شما نرمافزارهايي بسازد كه قبلاً نميتوانستيد آنها را بسازيد.
رايان گالبِك درباره اين موضوع ميگويد: «مطرح كردن اين واقعيت كه شما ميتوانيد همچنان با OOP سر كنيد و Cross-Cutting Concernها را با آن پيادهسازي كنيد، چيزي را عوض نميكند. زيرا بر اساس تز چرچ/تورينگ6 شما ميتوانيد تمام مباحث موجود در مدل شيءگرا را در قالب زبانهاي رويهاي يا حتي كد اسمبلي پيادهسازي كنيد. توانايي اصلياي كه AOP براي شما به ارمغان ميآورد Modularization بهتر است نه قدرت بيشتر!»
صحبتهاي گالبِك را ميتوان به تمام پارادايمهاي برنامهنويسي تعميم داد. پارادايمهاي برنامهنويسي براي ما ناممكن را ممكن نميكنند (يا برعكس). بلكه فقط كمك ميكنند تا هزينه توليد و نگهداري يك پروژه براي شما كاهش يابد.
جدا از تمام اينها، بحثهاي انتقادي اصولي نيز درباره AOP و نحوه Modularization آن وجود دارد كه قابل تعمق است. اصولا ًدر يك سيستم AOP-based به يك برنامهنويس قدرت بسياري داده ميشود كه اين موضوع ميتواند علاوه بر اثرات مفيد، مشكلاتي را نيز براي سيستم ايجاد كند.
توانايي ايجاد تغييرات كلي در سيستم توسط برنامهنويسي كه مسئوليت نوشتن كدهاي مربوط به يك Aspect را برعهده دارد ميتواند اثرات زيانباري روي كارايي كل سيستم شما بگذارد. Andres Hejlsberg معمار زبان #C، با وجود اين كه ايده AOP را ايدهاي قابل تقدير و مستعد بررسي و تحقيق بيشتر ميداند، اما نگرانياش را از بابت قدرت زياد برنامهنويس، پنهان نميكند:
«من هنوز نگرانيهاي اساسي درباره Aspectهايي كه قدرت استدلال شما درباره كد خودتان را دچار مشكل ميكند، دارم. دليل بروز اين نگراني به اين موضوع برميگردد كه افراد ميتوانند به هر بخش كد شما كه ميخواهند، چيزهايي بيافزايند. بله به هر بخش كد و اين ميتواند باعث ايجاد يك 7Side effect شود. اين كار شما استدلال در مورد كدتان را بسيار مشكل ميكند».
در حقيقت، بيشتر نگرانيها درباره AOP به دو موضوع پيچيده بودن استدلال و تأثيرات ناخواسته زيانبار نويسنده Aspect برميگردد. هرچند كه ابداعكنندگان AOP ادعا ميكنند كه با استفاده از AOP و به دليل تجمع كل جريان كاري Cross-Cutting Concernها در يك فايل، توانايي استدلال در مورد كد بالا ميرود، اما بايد پذيرفت كه اشكالات مطرح شده كاملاً قابل وقوع است و بايد درباره آن تصميماتي اتخاذ شود.
علاوه بر اشكالاتي كه ممكن است از طرف برنامهنويسي كه Aspectها را مينويسد پيش آيد، برنامهنويس بخش اصلي نيز ممكن است با تغيير join pointهاي پيشبيني شده (مثلاً با تغيير نام آنها) باعث ايجاد مشكلاتي شود كه برنامهنويس Aspect از آن بيخبر است.
اين دسته از نگرانيها درباره عدم هماهنگي بين برنامهنويسان بخشهاي مختلف و توانايي اثرگذاري بالا در يك سيستم، دغدغه اصلي مهندسان نرمافزاري است كه به AOP ميانديشند. تمام اين موارد، اين واقعيت را بيشتر نمايان ميسازد؛ برنامهنويساني كه از AOP استفاده ميكنند بايد از تمام پتانسيلهاي آن اطلاع كامل داشته باشند تا بتوانند برنامههايي به دور از مشكلاتي كه در بالا ذكر شد، ايجاد كنند.
منبع : www.shabakeh-mag.com (http://www.shabakeh-mag.com/Articles/Show.aspx?n=1003759&p=2)
● پراكندگي: (Scattering) پيادهسازي يك Concern، پراكنده شده است هر گاه كد آن بين چند ماجول پخش شده باشد.
● پيچش: (Tangling) پيادهسازي يك Concern، پيچيده شدهاست هر گاه كد آن با كد يك Concern ديگر مخلوط شده باشد.
پراكندگي و پيچش در Aspect Oriented بهعنوان علائم يك Cross-Cutting Concern در نظر گرفته ميشوند. در حقيقت عموماً Concernهايي كه چنين خصوصياتي را در پيادهسازيهاي غير AOP داشته باشند، مورد بحث قرار ميگيرند.
AOP در عمل
بايد اعتراف كرد كه با وجود ارزشمند بودن توضيحات و تئوريهاي برنامهنويسي، تا وقتي كه آنها را در عمل، به وسيله برنامهنويسي مورد آزمايش قرار ندهيم، در حقيقت كار خاصي انجام ندادهايم. در ادامه سعي ميكنيم مفاهيم مطرح شده را به همراه كد آن مورد بررسي قرار دهيم تا علاوه بر معرفي و تعريف مفاهيم موجود در AOP، با نحوه به كارگيري آنان نيز آشنا شويم، اما قبل از هر چيز بايد به اين سؤال پاسخ دهيم كه كجا ميتوانيم AOPبنويسيم؟
براي پاسخ دادن به اين پرسش، ابتدا بايد ببينيد AOP چگونه اجرا ميشود. براي نوشتن يك پروژه به صورت AOPشما ابتدا هر دو بخش كد خود را (اعم از روند كاري اصلي برنامه و كدهاي مربوط به Aspectها) به صورت جداگانه در يك زبان شيءگرا مينويسيد و سپس موجودي به نام Aspect Weaver آن دو بخش را با يكديگر تركيب كرده و كد نهايي را ميسازد.
جداسازي كد اصلي و كد Concernها (كد اصلي نيازي به اطلاع از بخش Concernها ندارد) به افزايش قابليت استفاده دوباره (Reusability) و قابليت نگهداري (Maintainability) پروژه كمك شاياني ميكند. شكل 1 نحوه كار Weaverها را نشان ميدهد.
دو نمونه بارز از ابزارهايي كه ميتوان با استفاده از آنها برنامههاي Aspect-Oriented نوشت AspectJ و AspectWerkz هستند.
AspectJ يك Extension است كه براي زبان جاوا در زيراكس پارك و توسط گروه بهوجود آورنده AOP نوشته شدهاست. اين Extension به دو صورت تنها و تعبيه شده در Eclipse IDE عرضه ميشود. AspectJ پتانسيل پشتيباني از مدلRT Weaver را نيزدارد، اما براي بهره بردن از تواناييهاي آن بايد از آن بهصورت يك Compile-time Weaver استفاده كرد (هماكنون به صورت Compile-time و loadtime عرضه ميشود).
Aspect Weaver !
در حقيقت Aspect Weaver كد اصلي و كد Aspectها را بهعنوان ورودي ميگيرد و محصول نهايي را توليد ميكند. توجه به اين موضوع ضروري است كه نگاه كلي به Weaverها مانند يك كامپايلر نيست، زيرا قرار نيست كه تمام كارهاي پيچيدهاي را كه يك كامپايلر انجام ميدهد.
Weaver نيز در مورد تركيب دو بخش كد انجام دهد. در حقيقت، همانطور كه خود كيزالس هم اشاره مي كند، وظيفه Weaverها فقط Integration (مجتمعسازي) است.
تكنيكهاي اوليه Weaving به دو دسته عمده تقسيم ميشوند: Compile Time) CT) و Run-Time) RT Weaver)هايCT. همانطور كه از نامشان پيدا است، تمام كارهاي مربوط به تركيب كد را در زمان كامپايل انجام ميدهند و در حقيقت كد نهايي كه اجرا ميشود، محصول كامل است.
در مقابل RTها اينكار را در زمان اجرا انجام ميدهند و ايجاد ارتباط را تا آن وقت به تأخير مياندازند. Weaverهاي CT با توجه به اين كه تمام فرآيند ايجاد ارتباط را در ابتداي كار و هنگام كامپايل انجام ميدهند بسيار سريعتر از RTها عمل ميكنند، اما در مقابل RTها هم اين مزيت را دارند كه در صورت تغيير كد Aspectها نيازي به انجام دوباره عمليات Weaving نيست و برخلاف CTها در Run-Time Weaver تغييرات در كد بدون نياز به هيچ كاري سريع منعكس ميشوند.
همانطور كه در بالا ذكر كرديم، تكنيكهاي Weaving ديگري نيز وجود دارند كه در واقع فضاي بين Compile-time و Run-time قرار ميگيرند. اين دو تكنيك Weaving را (post-compile time (binary و Load-time مينامند.
Binary Weaving در حقيقت عمليات Weaving را روي byte code انجام ميدهد (پس از كامپايل). Load time Weaving نيز يك نوع binary Weaving است. با اين تفاوت كه عمليات Weaving را تا زماني كه يك كلاس به Class loader معرفي نشده است، به تأخير مياندازد.
(اين توانايي ميتواند برخي از نقصهاي مدل Compile-time را برطرف كند، زيرا شما ميتوانيد بدون كامپايل كردن دوباره كد اصلي خود (Aspect ،(Business Logicهايي به برنامه اضافه كرده و سپس آنها را به پروژه اصلي لينك دهيد). در حقيقت، در اين مدل تا جايي كه ممكن است عمليات Weaving به تأخير ميافتد و تا مرحله Load شدن كلاسهاي موردنياز هيچ تركيبي انجام نميشود.
AspectJ محيطي ساده و بسيار كارا دارد و هماكنون محبوبترين ابزار برنامهنويسي Aspect-Oriented است. نسخههاي جديدتر AspectJ بهطور كامل با محيط توسعه Eclipse هماهنگي دارند و ميتوان از تمام امكانات Eclipse در مورد Aspectها نيز سود برد.
توجه به اين نكته ضروري است كه AspectJ تغييراتي در Syntax زبان به وجود ميآورد كه اين موضوع ميتواند باعث بروز مشكلاتي شود (Eclipse با توجه به اضافه كردن Keywordهاي مربوط به برنامهنويسي AOP اين مشكل را ندارد).
اين مشكلات باعث شدند تا ابزارهاي ديگري به وجود آيند كه به اين تغييرات گرامري در زبان برنامهنويسي نيازي نداشته باشند. يك نمونه مشهور از اين زبانها AspectWerkz است. AspectWerkz در حال حاضر از هر سه مدل Compile-time ،Load-time و Run time استفاده ميكند.
خصوصيت بارز AspectWerkz اين است كه Syntax زبان را تغيير نميدهد و در حقيقت تغييرات را با استفاده از Annotation انجام ميدهد كه به يك ساختار زباني جديد نيازي ندارد.
در حال حاضر، دو پروژه AspectJ و AspectWerkz با يكديگر تركيب شدهاند تا بتوانيم از قابليتهاي هر دو به صورت همزمان استفاده كنيم.
تمام اين مقدمهها براي اين ذكر شد كه شما كمي بيشتر با نحوه عملكرد داخلي ابزارهاي توسعه برنامههاي Aspect-Oriented آشنا شويد. در قسمت بعد وارد بخش كدنويسي ميشويم.
كدنويسي در AspectJ
در اين قسمت به بررسي پيادهسازي مفاهيم مختلف AOP توسط AspectJ ميپردازيم. هر چند بررسي تمام خصوصيات AspectJ خود يك مقاله جداگانه ميطلبد، اما سعي ميكنيم تا مفاهيم اصلي پركاربرد را در نوشتن يك برنامه Aspect-Oriented مطرح كنيم. در شمارههاي آينده، بهطور عميقتري به بررسي جنبههاي عملي AOPميپردازيم.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/c140ac6e28bcd09580ec5129656d8f0b.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/c140ac6e28bcd09580ec5129656d8f0b.jpg)
Annotation
شايد تعريف كردن Annotation كمي دشوار باشد. در حقيقت آنها برچسبها و شايد پايگاهدادههايي هستند كه اطلاعاتي درباره برنامه شما ميدهند.
در حقيقت، Annotationها تغييري در Semantic زبان ايجاد نميكنند، اما ميتوانند براي ابزارهاي مختلفي كه در روند اجراي برنامه دخيل هستند، پيغامهايي در برداشته باشند.
در حقيقت، ميتوان Annotationها را علامتهايي تلقي كرد كه كامپايلر آنها را بهعنوان متاديتا در جايي ذخيره ميكند. سپس VM (سرنام Virtual Machine) با ديدن آنها تعيين ميكند كه چگونه رفتار بعضي از Elementهاي برنامه را تغيير دهد.
توضيح كامل Annotationها در اين مقاله نميگنجد. پس به ذكر همين مقدمات بسنده ميكنيم.
Join Point
Join Point، در حقيقت مفهوم جديدي نيست. ما از اين مفهوم قبلاً بارها استفاده كردهايم ، فقط شايد اسمي براي آن نداشتهايمJoint Point .ها نقاط خوش تعريف خاصي در برنامه هستند. بهعنوان مثال، نقطه فراخواني يا بازگشت متد.
Aspect
شروع كاري ما براي آوردن AOP به صحنه عمل كليدواژه Aspect است. Aspectها تقريباً مشابه كليدواژه class در نوشتن كدهاي معمولي شيءگرا هستند. هر Aspect بايد در يك فايل جداگانه كه نام آن با نام Aspect يكي است، تعريف شود:
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/a082553765be409c8e014a0080b6f276.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/a082553765be409c8e014a0080b6f276.jpg)
يك Aspect مجموعهاي از point-cutها و Adviceها است.
Point-cut
در AOP شما نياز داريد نقاط خاصي را بهعنوان نقاطي كه موجب فراخواني يك Aspect ميشوند، تعريف كنيد. اين كار توسط Point-cutها انجام ميگيرد. در حقيقت، Point-cutها يك مجموعه از Join Pointها را تعريف ميكنند و ميتوانيم آنها را بهعنوان يك Query براي انتخاب joint pointها در نظر بگيريم.
در حقيقت، تعريف يك point-cut بيانكننده اين موضوع است كه join Pointهاي كد اصلي (Business Logic) در چه جاهايي قرار دارند. به زبان سادهتر point-cut شرايط نقطههايي را تعريف ميكند كه بايد با رسيدن به آنها در كد اصلي اين Aspect اجرا شود.
اين مكانيسم، به مكانيسم فراخواني روش بسيار شبيه است. در حقيقت، در اينجا point-cutها حكم يك روش و join point حكم نقطهاي است كه دستور فراخواني متد در آن قرار دارد.
به عنوان مثال:
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/7017532d59c4e6aabc79ef4466c1b349.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/7017532d59c4e6aabc79ef4466c1b349.jpg)
اين Pointcut مشخص ميكند كه پس از اجراي متد از كلاس Data كه با Write شروع ميشوند و هيچ ورودياي ندارند، اين Aspect فراخواني ميشود.
ميتوانيم در يك Aspect چند pointcut داشته باشيم. كد پايين سه pointcut تعريف ميكند.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/9091760640750b6244d15321a99d2754.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/9091760640750b6244d15321a99d2754.jpg)
در كد بالا سه pointcut تعريف شده است.
PC1: وقتي متدي از كلاس Data كه با Write شروع ميشود و آرگومان String ميگيرد، فراخواني ميشود، pointcut PC1 رخ ميدهد.
PC2: با فراخواني هر متدي (هر نامي با هر ورودياي) كه در داخل كلاس Data است pointcut PC2 رخ ميدهد.
PC3: با اجراي بدنه اصلي متد SecurityChecking ،pointcut PC3 فراخواني ميشود.
Advice
كد تكميلي كه به سيستم اضافه ميشود تا كارهاي مربوط به يك Concern را انجام دهد، Advice نام دارد. Adviceها در حقيقت همان قطعهكدهاي معمولي هستند كه حكم انجام عملياتي يك Concern را دارند.
هر Advice كاري را كه يك Aspect بايد در ازاي وقوع يك اتفاق خاص (pointcut) انجام دهد، مشخص مي كند. باز هم با تعبير متدگونه advice در واقع حكم بدنه متد را دارد (point-cut حكم اعلان متد را دارد).
كاري كه كد زير انجام ميدهد اين است كه قبل از اجراي هر متدي از كلاس Data كه با Write شروع ميشود و هيچ ورودي ندارد، هويت كاربر حال حاضر بررسي ميشود.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/7f01912b94a3241c4634b7a5f5909c36.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/7f01912b94a3241c4634b7a5f5909c36.jpg)
Adviceها به سه نوع تقسيم ميشوند:
1-Before: اين Adviceها بلافاصله بعد از اينكه يك join point اتفاق افتاد و پيش از اين كه برنامه ادامه يابد اجرا ميشوند. بهعنوان مثال، اگر join point فراخواني يك ljn باشد، بلافاصله پس از فراخواني ljn (و پيش از اجراي بدنه آن و برگرداندن يك مقدار)، قطعه كد Advice اجرا ميشود.
2- After: اين نوع Advice نيز همانطور كه از نامش پيدا است، درست برخلاف Before عمل ميكند. Adviceهاي نوع After پس از پايان اجراي join point، اجرا ميشوند. در حقيقت، در مثال بالا (فراخواني متد) يك Advice از اين دسته پس از پايان روند كاري متد (برگرداندن مقدار- وقوع exception) اجرا ميشود.
3- Around: اين نوع بسيار جالب از Adviceها ميتوانند كلاً اجراي join point را ناديده بگيرند. در حقيقت Adviceهاي نوع Around دقيقاً به جاي join point اجرا ميشوند (اجراي join point ناديده گرفته ميشود و به جاي آن اين Advice اجرا ميشود). معني اين امر درباره متدها اين است كه بدنه اصلي متد اجرا نميشود و به جاي آن كد موجود در Advice اجرا ميشود.
يك كد تركيبي
كد زير تركيبي از سه pointcut و سه Advice را نشان ميدهد.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/d759a0c464f4bc512d5d5eed904d7bfb.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/d759a0c464f4bc512d5d5eed904d7bfb.jpg)
كد زير نيز كد كلاس Data است كه شامل روش main نيز است.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/3d2ecdc4c7b118eec19b672350a8a330.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/3d2ecdc4c7b118eec19b672350a8a330.jpg)
پس از كامپايل و اجراي Data خروجي زير را دريافت ميكنيم:
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/c825970dbc1192983f4c6fa799c4bc68.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/c825970dbc1192983f4c6fa799c4bc68.jpg)
به ترتيب خروجيها دقت كنيد. در PC1 نحوه مشخص كردن آرگومان ورودي متد و استفاده از آن را مشاهده ميكنيد. در PC2 مطابق تعريف تمام پيغامهاي Logging همانطور كه در كد pcAspect2 تعريف شده بود، بعد از اجراي تمام متدها چاپ شده است.
در PC3 نحوه استفاده از شيء كلاس مبدأ را مشاهده ميكنيد. در ضمن كد متد SecurityChecking در كلاس Dataاصلاً اجرا نشده است، زيرا ما Adviceاي را كه بايد درباره اين متد اجرا ميشد، around در نظر گرفتهايم و همانطور كه در بالا ذكر شد Adviceهاي نوع around كلاً اجراي عادي join point را متوقف ميكنند.
احتمالاً با بررسي اين كد، متد كار Adviceها و point-cutها تا حدود زيادي براي شما روشن شدهاست. البته پيادهسازيهاي اين مفهوم ريزهكاريهاي بسيار پيچيدهتري نيز دارند كه در اين مقاله نميگنجد.
Inter-type Declaration
Inter-type Declaration امكان جالبي است كه به برنامهنويس اجازه ميدهد ساختار elementهاي برنامه را در صورت اجرا شدن يك Aspect تغيير دهد، اما تغيير ساختار به چه معنا است؟
- اضافه كردن متد، Constructor يا Field به ديگر Typeهاي برنامه (كلاس يا Aspectهاي ديگر)
- اضافه كردن يا تغيير اينترفيسها و كلاسهاي والد تعيين شده براي Typeهاي ديگر
- تغيير حالت exceptionها از checked به unchecked
- اضافه كردن warningهاي دلخواه و... .
در حقيقت، با استفاده از اين توانايي يك Aspect ميتواند بهطور كامل وظيفه تعريف، نگهداري و اجراي elementهاي مورد استفاده خود را بر عهده بگيرد و اين موضوع باعث ميشود تا تمام موضوعات مربوط به Aspectنويس در همان فايل aspect خلاصه شود.
نوشتن كدي كه از Inter-type Declaration عموماً چيز جديدي در بر ندارد و در بيشتر موارد مانند روش كلاسيك كد نوشتن ما است. بهعنوان مثال، كد زير به ترتيب يك Field و يك متد براي كلاس AirPlane تعريف ميكند.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/d81e3689e73d8462b456a0e83ce48823.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/d81e3689e73d8462b456a0e83ce48823.jpg)
قطعه كد زير نيز يك Constructor دو Argumentي براي كلاس AirPlane تعريف ميكند.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/9cf6f5b4d49d93d9d84e6a7ce0dbe45a.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/9cf6f5b4d49d93d9d84e6a7ce0dbe45a.jpg)
كد زير نيز كلاس والد كلاس AirPlane را كلاس SomethingFly تعيين ميكند.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/e93273276ce59913c6e89c12e1fa4ee5.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/e93273276ce59913c6e89c12e1fa4ee5.jpg)
چگونه اينها را اجرا كنيم؟
اجراي يك برنامه نوشته شده تحت AOP در AspectJ با اجراي يك برنامه معمولي جاوا چندان تفاوتي ندارد. ابتدا نحوه اجراي يك برنامه را كه براي Weaving از تكنيك Compile-time استفاده ميكند، نشان ميدهيم.
بهطور كلي براي كامپايل كردن اين برنامهها، AspectJ دستور ajc را در نظر گرفته است. سادهترين نوع كامپايل به صورت زير است.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/826bf9d36688b4e8d685d1984fd8c29b.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/826bf9d36688b4e8d685d1984fd8c29b.jpg)
براي اجراي برنامه نيز نبايد مشكلي داشته باشيم. زيرا تركيب (Weaving) بين Aspect و كد اصلي انجام شده است و ما هماكنون يك فايل class. كامل در اختيار داريم. پس ميتوانيم به سادگي آن را اجرا كنيم.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/bed2dc7cb0e5717f105bd63c0a8204c8.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/bed2dc7cb0e5717f105bd63c0a8204c8.jpg)
مدل Load-time
اكنون سعي مي كنيم با ذكر يك مثال، نحوه اجراي يك برنامه به صورت Load-time و اثرات آن را مشاهده كنيم.
كد ارائهشده در بخش <يك كد تركيبي> در قسمت قبل را در نظر بگيريد. فرض كنيد ميخواهيم يك Aspect ديگر به نام «pcAspect3» داشتهباشيم. «pcAspect3.java» به صورت زير در نظر گرفته ميشود:
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/3c07a85956422ac218a3d17fc311d435.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/3c07a85956422ac218a3d17fc311d435.jpg)
براي اضافه كردن aspect2 به پروژه ميتوان دو كار انجام داد:
1- مانند قبل عمل ميكنيم و سه فايل data و pcAspect2 و pcAspect3 را با يكديگر كامپايل ميكنيم. عمليات Weaving به صورت Compile-time انجام ميشود و با اجراي كلاس Data نتيجه موردنظر را دريافت ميكنيم.
2- از load-time Weaving استفاده كنيم (براي تعريف load-time Weaving به كادر Aspect Weaver مراجعه كنيد).
اصولاً در بعضي موارد ممكن است كامپايل كردن دوباره تمام پروژه بسيار هزينهبر باشد. با استفاده از load-time weaving نيازي به كامپايل كردن دوباره هيچكدام از فايلهاي قبلي نيست. بلكه ما فقط كلاس فايل جديدمان را به بقيه فايلها اضافه ميكنيم، سپس با اجراي دوباره پروژه تمام تغييرات لازم خود به خود، اعمال ميشود.
اما انجام اين كار به چه شكل است؟
همانطور كه ذكر شد ما بايد به نحوي بعد از كامپايل فايل اصلي يك Aspect را به آن معرفي كنيم.
اين اعلام به وسيله META-INF/aop.xml (فايل aop.xml درون دايركتوري META-INF) انجام ميگيرد. فايل aop.xml ميتواند به روشهاي مختلفي ايجاد شود. استفاده از دستور زير به هنگام كامپايل فايل اوليه يك زير دايركتوري در دايركتوري فعلي، META-INF/aop.xml را ايجاد ميكند.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/29ed0dc679088e6323d7d8597e527ca2.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/29ed0dc679088e6323d7d8597e527ca2.jpg)
اين كار (ساخت فايل aop.xml) به صورت دستي نيز ميتواند انجام شود. اكنون بايد كد جديد خود را به قسمت اصلي برنامه اضافه كنيم. براي اين كار ابتدا كد جديد را به همان روش هميشگي كامپايل ميكنيم.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/7015389b7f9167a07bfa6c2be6989253.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/7015389b7f9167a07bfa6c2be6989253.jpg)
اعلان Aspectها در فايل aop.xml بسيار ساده است. فقط كافي است aop.xml را به صورت زير اصلاح كنيم.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/2c4817368c75a7578f084dca72749b2d.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/2c4817368c75a7578f084dca72749b2d.jpg)
اكنون با اجراي Data هر دو Aspect در پروژه اثر ميكنند. توجه كنيد كه در اينجا ديگر نميتوانيد از جاوا براي اجراي Data استفاده كنيد، زيرا در اينجا عمليات Weaving در هنگام Load شدن كلاسها و بعد از اجرا انجام ميشود. پس بايد از دستور مخصوص اجرا بهصورت Load-time Weaving استفاده كنيد.
اين دستور aj5) aj براي Java5) است.
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/e45f3ae2a9bbb80ae71063e8cee2b257.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/e45f3ae2a9bbb80ae71063e8cee2b257.jpg)
http://www.sarirservice.com/Attach/SMMArticles/Sections/700/631dd786dea83044dd4f8a615cf68fd0.jpg (http://www.sarirservice.com/index.php?Module=SMM&SMMOp=showImgs&SMM_CMD=&Path=./Attach/SMMArticles/Sections/700/631dd786dea83044dd4f8a615cf68fd0.jpg)
و اما بعد...
اما سرنوشت AOP به كجا ميانجامد؟ در طول ساليان اخير ايدههاي مختلفي براي ايجاد يك پارادايم برتر ارائه شدهاست، اما همچنان مدل شيءگرا، بهعنوان يك 4Silver Bullet براي عرصه پارادايمها باقي مانده و سالها است كه محبوبترين شيوه توليد نرمافزار است.
AOP در تقابل با برنامهنويسي شيءگرا قرار نميگيرد، اما همچنان عدهاي با ترديد به توليد كيزالس و گروهش مينگرند و استفاده از آن در مقياس وسيع را نيازمند تحقيق و بررسي بيشتري ميدانند. در اين ميان، عدهاي پا را از اين نيز فراتر ميگذارند و AOP را ايدهاي ناكارآمد در عمل و غيرقابل استفاده براي پروژههاي بزرگ مينامند. البته در مقابل اين دسته، افرادي نيز وجود دارند كه AOP را انقلابي در زمينه پارادايمهاي برنامهنويسي معرفي و استفاده از آن را به شدت پيشنهاد ميكنند.5
اما از اين افراطيگريها چيزي نصيب مهندسان نرمافزار نميشود. هر نظري كه درباره AOP داشته باشيد، خواه طرفدار پر و پا قرص، خواه مخالف شديد يا يك آدم بيطرف، در تمام موارد چيزي كه وجود دارد اين است: AOP فقط يك پارادايم برنامهنويسي است.
قرار نيست با AOP مشكلاتي را حل كنيد كه قبلاً نميتوانستيد آن را حل كنيد يا به بيان ديگر قرار نيست اين پارادايم براي شما نرمافزارهايي بسازد كه قبلاً نميتوانستيد آنها را بسازيد.
رايان گالبِك درباره اين موضوع ميگويد: «مطرح كردن اين واقعيت كه شما ميتوانيد همچنان با OOP سر كنيد و Cross-Cutting Concernها را با آن پيادهسازي كنيد، چيزي را عوض نميكند. زيرا بر اساس تز چرچ/تورينگ6 شما ميتوانيد تمام مباحث موجود در مدل شيءگرا را در قالب زبانهاي رويهاي يا حتي كد اسمبلي پيادهسازي كنيد. توانايي اصلياي كه AOP براي شما به ارمغان ميآورد Modularization بهتر است نه قدرت بيشتر!»
صحبتهاي گالبِك را ميتوان به تمام پارادايمهاي برنامهنويسي تعميم داد. پارادايمهاي برنامهنويسي براي ما ناممكن را ممكن نميكنند (يا برعكس). بلكه فقط كمك ميكنند تا هزينه توليد و نگهداري يك پروژه براي شما كاهش يابد.
جدا از تمام اينها، بحثهاي انتقادي اصولي نيز درباره AOP و نحوه Modularization آن وجود دارد كه قابل تعمق است. اصولا ًدر يك سيستم AOP-based به يك برنامهنويس قدرت بسياري داده ميشود كه اين موضوع ميتواند علاوه بر اثرات مفيد، مشكلاتي را نيز براي سيستم ايجاد كند.
توانايي ايجاد تغييرات كلي در سيستم توسط برنامهنويسي كه مسئوليت نوشتن كدهاي مربوط به يك Aspect را برعهده دارد ميتواند اثرات زيانباري روي كارايي كل سيستم شما بگذارد. Andres Hejlsberg معمار زبان #C، با وجود اين كه ايده AOP را ايدهاي قابل تقدير و مستعد بررسي و تحقيق بيشتر ميداند، اما نگرانياش را از بابت قدرت زياد برنامهنويس، پنهان نميكند:
«من هنوز نگرانيهاي اساسي درباره Aspectهايي كه قدرت استدلال شما درباره كد خودتان را دچار مشكل ميكند، دارم. دليل بروز اين نگراني به اين موضوع برميگردد كه افراد ميتوانند به هر بخش كد شما كه ميخواهند، چيزهايي بيافزايند. بله به هر بخش كد و اين ميتواند باعث ايجاد يك 7Side effect شود. اين كار شما استدلال در مورد كدتان را بسيار مشكل ميكند».
در حقيقت، بيشتر نگرانيها درباره AOP به دو موضوع پيچيده بودن استدلال و تأثيرات ناخواسته زيانبار نويسنده Aspect برميگردد. هرچند كه ابداعكنندگان AOP ادعا ميكنند كه با استفاده از AOP و به دليل تجمع كل جريان كاري Cross-Cutting Concernها در يك فايل، توانايي استدلال در مورد كد بالا ميرود، اما بايد پذيرفت كه اشكالات مطرح شده كاملاً قابل وقوع است و بايد درباره آن تصميماتي اتخاذ شود.
علاوه بر اشكالاتي كه ممكن است از طرف برنامهنويسي كه Aspectها را مينويسد پيش آيد، برنامهنويس بخش اصلي نيز ممكن است با تغيير join pointهاي پيشبيني شده (مثلاً با تغيير نام آنها) باعث ايجاد مشكلاتي شود كه برنامهنويس Aspect از آن بيخبر است.
اين دسته از نگرانيها درباره عدم هماهنگي بين برنامهنويسان بخشهاي مختلف و توانايي اثرگذاري بالا در يك سيستم، دغدغه اصلي مهندسان نرمافزاري است كه به AOP ميانديشند. تمام اين موارد، اين واقعيت را بيشتر نمايان ميسازد؛ برنامهنويساني كه از AOP استفاده ميكنند بايد از تمام پتانسيلهاي آن اطلاع كامل داشته باشند تا بتوانند برنامههايي به دور از مشكلاتي كه در بالا ذكر شد، ايجاد كنند.
منبع : www.shabakeh-mag.com (http://www.shabakeh-mag.com/Articles/Show.aspx?n=1003759&p=2)