aspect oriented programing _3

بخش۶                    مبانی برنامه نویسی جنبه گرا

تا اینجا به این نتیجه رسیدیم که پیاده‌سازی دغدغه‌های متداخل به طور جداگانه کار مفیدی است. محققین روش‌های متعددی برای این کار پیشنهاد کرده‌اند که به طور کلی به آن «جداسازی دغدغه‌ها» می‌گویند. برنامه‌نویسی جنبه‌گرا یکی از این روش‌هاست. در واقع AOP می‌کوشد دغدغه‌های موجود در یک سیستم کاملاً از یکدیگر جدا شوند تا مشکلاتی که در بخش‌های قبلی مورد بررسی قرار گرفتند پیش نیاید.

 

برنامه‌نویسی جنبه‌گرا به شما این اجازه را می‌دهد تا دغدغه‌های مختلف را به صورت مستقل و با ارتباط و وابستگی پایین پیاده سازی کنید. سپس با ترکیب کردن این پیاده‌سازی‌ها سیستم نهایی را برای شما آماده می‌کند. در واقع در AOP سیستم‌ها با پیاده‌سازی مستقل، مؤلفه‌بندی شده، و با وابستگی پایین دغدغه‌های متداخل ساخته می‌شود. در صورتی‌که در OOP سیستم‌ها با پیاده‌سازی مستقل، مؤلفه‌بندی شده، و با وابستگی پایین دغدغه‌های مشترک ساخته می‌شود. ‌ واحد پیمانه‌بندی در AOP جنبه[1] است، در صورتی‌که در این واحد در OOP کلاس است.

 

توسعه‌ی برنامه در روش برنامه نویسی جنبه گرا به طور کلی از سه مرحله تشکیل شده‌است:

Ÿ        تجزیه‌ی جنبه‌ای[2]: تجزیه‌کردن نیازمندی‌ها، برای اینکه دغدغه‌های مشترک و متداخل مشخص شوند. در این قسمت دغدغه‌های در سطح پیمانه‌ها از دغدغه‌های سطح سیستمی جدا می‌شوند. مثلاً در مورد سیستم کارت اعتباری دغدغه‌ها عبارتند از: بخش پردازش اعتبار، ثبت وقایع، و هویت شناسی.

Ÿ        پیاده‌سازی دغدغه‌ها: پیاده‌سازی هر دغدغه به طور کاملاً جداگانه. مثلاً در سیستم کارت اعتباری باید بخش مرکزی پردازش کارت اعتباری، بخش ثبت وقایع، و بخش هویت شناسی پیاده‌سازی شوند.

Ÿ        ترکیب مجدد جنبه‌ای[3]: در این مرحله مجتمع‌ساز جنبه‌ها[4] قوانین مربوط به ترکیب مجدد را مشخص می‌کند و به این وسیله aspect ها را می‌سازد. فرآیند ترکیب مجدد، که به تلاقی[5] یا مجتمع سازی نیز معروف است، این اطلاعات را می‌گیرد و سیستم نهایی را آماده‌می‌کند. برای مثال پردازش کارت اعتباری: باید توسط یک یک زبان جنبه‌گرا مشخص کرد که اطلاعات در اول و آخر هر عملیات پردازش اعتبار ثبت شوند. همچنین باید مشخص کرد که قبل از ورود به هر کدام از عملیات تجاری عمل هویت شناسی انجام شود و در صورت درستی عملیات مورد نظر انجام گیرد.

 

برنامه نویسی جنبه‌گرا در برخورد با دغدغه‌های متداخل با برنامه نویسی شیء گرا متفاوت است. در برنامه نویسی جنبه‌گرا پیاده سازی هر دغدغه از اینکه توسط دغدغه‌های دیگر تحت نظر است بی‌خبر است. مثلاً قسمت پردازش کارت اعتباری نمی‌داند که دغدغه‌های دیگری آغاز و پایان عملیات آن‌را ثبت می‌کنند یا هویت کاربر را قبل از آن چک می‌کنند. این نشان‌دهنده یک تغییر تفکر از نگرش شیء گرا است.

 

یک زبان جنبه‌گرا ممکن است یک روش دیگر برنامه‌نویسی را در خدمت خود بگیرد تا بتواند از مزیت‌های آن استفاده کند. برای مثال یک زبان جنبه‌گرا ممکن است یک زبان شی‌ء گرا را به عنوان پایه برای خود در نظر بگیرد. به این وسیله می‌تواند در پیاده‌سازی هر یک از دغدغه‌ها، از امکانات شیء‌گرایی استفاده کند. این موضوع دقیقاً شبیه به زبان‌های شیء‌گرا است که زبان‌ها روالی را به عنوان پایه برای خود انتخاب کردند.

 

 

[2] Aspectual Decomposition

[3] Aspectual Recomposition

[4] Aspect Integrator

[5] Weaving

aspect oriented programing _2

 

بخش۴                    مسائل مربوط به دغدغه‌های متداخل

با وجود اینکه دغدغه‌های متداخل بین چندین پیمانه‌ی برنامه پخش می‌شوند، تکنیک‌‌های کنونی پیاده‌سازی این‌گونه نیازمندی‌ها را با روش‌های یک‌بعدی پیاده‌سازی می‌کنند. در این روش، این دغدغه‌ها در پیمانه‌ی مرکزی پیاده‌سازی می‌شوند. بقیه‌ی نیازمندی‌ها هم  به نوعی روی همین بعد پیاده‌سازی می‌شوند. به عبارت دیگر، فضای نیازمندی‌ها یک فضای چند بعدی است، در صورتی که فضای پیاده‌سازی یک بعدی است. چنین عدم تطابقی باعث یک نگاشت نامناسب بین پیاده‌سازی و نیازمندی‌ها می‌شود.

علائم این مشکل
در صورتی که از روش‌های متداول کنونی استفاده کنیم چند عارضه ممکن است در پیاده‌سازی چنین دغدغه‌های متداخلی پیش‌بیاید. می‌توان این مشکلات را به طور کلی به دو دسته تقسیم کرد.

1.      در هم پیچیدگی کد: پیمانه‌های مختلف برنامه ممکن است به طور هم‌زمان با چندین نیازمندی سروکار داشته‌باشند. مثلاً اغلب برنامه‌نویسان هم‌زمان درباره‌ی منطق کاری، کارایی، هم‌زمانی، ثبت وقایع، و امنیت فکر می‌کنند. تجمع همه‌ی این نیازمندی‌ها باعث می‌شود در هر بخش از برنامه قسمتی از هر کدام از این دغدغه‌ها پیاده‌سازی شوند که باعث در هم پیچیدگی کد می‌شود.

2.      پراکندگی کد: چون دغدغه‌های متداخل، طبق تعریف، بین پیمانه‌های مختلف پخش هستند، پیاده‌سازی مربوط به آنها نیز بین این پیمانه‌ها پراکنده‌است. مثلاً در یک سیستم که از یک پایگاه داده استفاده می‌کند، دغدغه‌ی کارایی ممکن است روی تمامی پیمانه‌هایی که به پایگاه‌داده دسترسی دارند تأثیر بگذارد.

نتایج این مشکل
روی هم رفته، در هم پیچیدگی و پراکندگی کد اثرات مختلفی روی طراحی و ساخت برنامه می‌گذارد:

Ÿ        ضعف در ردیابی: پیاده‌سازی هم‌زمان چندین دغدغه، رابطه‌ی بین یک دغدغه و پیاده‌سازیش را غیرشفاف می‌کند. این کار باعث می‌شود نگاشت بین این دو دقیق و شسته‌رفته نباشد.

Ÿ         بهره‌وری پایین: پیاده‌سازی هم زمان چندین دغدغه تمرکز برنامه‌نویس را از دغدغه‌ی اصلی به دغدغه‌های جانبی جلب می‌کند. به همین دلیل بهره‌وری پایین می‌آید.

Ÿ        کم شدن استفاده‌ی مجدد از کد: از آنجایی که در این شرایط هر پیمانه چندین دغدغه را پیاده‌سازی می‌کند، در سیستم‌های دیگر که همین کارکردها را دارند نمی‌توان از این پیمانه‌ها استفاده کرد. این مسأله بهره‌وری را باز هم کمتر می‌کند.

Ÿ        کیفیت پایین کد: در هم پیچیدگی کد باعث می‌شود مشکلات در کدها پنهان شود. علاوه بر آن وقتی در یک لحظه به چندین دغدغه بپردازیم ممکن است توجه لازم به برخی از آنها نکنیم.

Ÿ        سختی تحول: نگرش محدود و محدودیت منابع در زمان طراحی یک سیستم باعث می‌شود که در طراحی، فقط برخی از دغدغه‌ها مورد نظر قرار گیرند. برای در نظر گرفتن نیازمندی‌های بعدی معمولاً لازم است در پیاده‌سازی دوباره‌کاری کنیم.

 راه حل‌های کنونی
برای اینکه برنامه نویسان با دغدغه‌های متداخل به صورت پیمانه‌شده‌ای سروکار داشته‌باشند، راه حل‌هایی با دامنه‌ی محدود مانند frameworkها وApplication Serverها  وجود دارد. مثلاً معماری Enterprise JavaBeans دغدغه‌های متداخلی مانند امنیت، سرپرستی، کارایی و ماندگار کردن داده‌ها را برعهده می‌گیرد. برنامه نویسان روی منطق کاری تمرکز می‌کنند و مسئولان جای‌گذاری روی مسائل مربوط به جای‌گذاری، مانند نگاشت داده‌های اشیاء بر روی پایگاه‌داده، کار می‌کنند. در این صورت برنامه‌نویس دیگر لازم نیست به مشکلات مربوط به ماندگاری داده‌ها توجه کند. در این روش ذغدغه‌ی ذخیره‌ی داده‌ها در پایگاه‌داده، در یک واصف مبتنی بر XML پیاده‌سازی می‌شود.

 بخش۵                        معمای معماری

معماری مطلوب برای یک سیستم به گونه‌ای است که نیازمندی‌های کنونی و آینده را در نظر می‌گیرد و از اینکه پیاده‌سازی برنامه به صورت وصله‌پینه‌ای باشد جلوگیری می‌کند. در این میان یک مشکل پیش می‌آید: پیش‌بینی آیند کار سختی است! اگر یک نیازمندی متداخل را که بعداً ممکن است به‌وجود بیاید از قلم انداخته‌باشیم، مجبور می‌شویم برای در نظر گرفتن آن بسیاری از بخش‌های برنامه را عوض کنیم یا حتی دوباره پیاده‌سازی کنیم. از طرف دیگر، اگر روی تعداد زیادی از نیازمندی‌ها که احتمال نیاز به آنها کم است تمرکز کنیم، ممکن است در نهایت به یک سیستم بی‌دلیل بزرگ، سردرگم کننده و غیر قابل فهم برسیم. در نهایت این معمای غیر قابل حل همیشه وجود دارد که:‌ تا چه حد آینده‌نگری در طراحی لازم است؟ در حال حاضر باید بیشتر از این روی طراحی وقت گذاشت، یا همین حد هم زیاد است؟ و ... .

 مثلاً: آیا یک معمار نرم‌افزار باید مکانیزم ثبت وقایع را در یک سیستم که از اول نیازی به این کار ندارد، در نظر بگیرد؟ اگر باید در نظر بگیرد، در چه جاهایی از برنامه باید ثبت وقایع اتفاق بیفتد و چه اطلاعاتی باید ثبت شود؟ اینچنین سؤالی درباره‌ی نیازمندی‌های مربوط به بهینه‌سازی نیز مطرح است. ما معمولاً خیلی کم درباره‌ی محدود کننده‌های کارایی یک سیستم می‌دانیم. معمولاً اول برنامه را می‌نویسیم. سپس آن را تحلیل می‌کنیم و بخش‌های ضعیف‌تر آن را بهینه‌سازی می‌کنیم تا کارایی مورد نظرمان را به‌دست آوریم. در این طرز نگرش لازم است به طور بالقوه بتوانیم بخش‌های مختلفی از برنامه را تغییر دهیم. علاوه برآن، در طول زمان چون الگوی استفاده‌ی یک سیستم تغییر می‌کند، محدود کننده‌های آن نیز تغییر می‌کنند. مسئول معماری یک کتابخانه‌ی نرم‌افزاری با قابلیت استفاده‌ی مجدد حتی از این هم کار سخت‌تری در پیش دارد! زیرا نمی‌تواند تمام سناریوهای استفاده از کتابخانه‌اش را تجسم کند.

 به‌طور خلاصه، به ندرت پیش می‌آید که معماران نرم‌افزار تمام دغدغه‌هایی که سیستم‌شان باید مرتفع کند را بشناسند. حتی برای نیازمندی‌هایی که تا کنون شناخته‌شده‌اند نمی‌توان مشخصات دقیق پیاده‌سازی را مشخص کرد. به طور کلی معماران نرم‌افزار همواره با این معمای لاینحل مواجه هستند که چقدر به جزئیات طراحی بپردازند.

 

aspect oriented programing

 

سلام
بخشی که می خواهم بگذارم مربوط به یک تحول جدید در برنامه نویسی است که به عنوان سمینار درس مهندسی نرم افزار در دانشگاه ما ارایه شده است
از  تمامی کسانیکه در این پروژه زحمت کشیده اند کمال تشکر را دارم
 

مقدمه
در بیشتر سیستم‌های نرم افزاری کنونی دغدغه[1]‌های مختلفی وجود دارد که بین بخش‌های مختلف برنامه مشترک هستند و بعضاً رابطه‌های پیچیده‌ای بین بخش‌های مختلف برنامه ایجاد می‌کنند. دغدغه‌هایی از قبیل امنیت، امکان ثبت وقایع و ... بخش کوچکی از این دغدغه‌ها را تشکیل می‌دهند. اگر برای پیاده‌سازی این‌گونه دغدغه‌ها از تکنیک‌های برنامه‌نویسی شی‌ء‌گرا استفاده شود، یک سیستم پیچیده، غیر قابل فهم و غیر انعطاف پذیر به‌وجود می‌آید. روش جدید برنامه‌نویسی جنبه‌گرا[2](
AOP) باعث می‌شود که تقسیم‌بندی بخش‌های مختلف برنامه به نحوی ساده شود که برطرف کردن دغدغه‌ها باعث پیچیدگی زیادی نشود. این کار باعث راحت‌تر شدن طراحی، فهم، و نگهداری سیستم خواهد شد. علاوه بر این، AOP باعث تولید محصولاتی با بهره‌وری بالاتر، کیفیت بهتر، و امکان اضافه کردن قابلیت‌های بیشتری می‌شود.
دغدغه[3] یک هدف مشخص، مفهوم یا حوزه‌ی کاری است. یک سیستم نرم‌افزاری عادی شامل دغدغه‌های متعددی، از دغدغه‌های لایه‌های مرکزی گرفته تا دغدغه‌های لایه‌های سیستمی، می‌شود. برای مثال، دغدغه‌ی مرکزی یک سیستم پردازش کارت اعتباری پردازش پرداخت‌ها است. در صورتی که دغدغه‌های سیستمی آن سر و کار داشتن با ثبت وقایع، یک‌پارچه کردن تراکنش‌ها، شناسایی کاربر، امنیت، کارایی و ... است. خیلی از این دغدغه‌ها، که به دغدغه‌های تداخلی[4] معروفند، بر روی پیاده‌سازی پیمانه‌‌های مختلف برنامه اثر می‌گذارند. در صورت استفاده از روش‌های کنونی برنامه‌نویسی این دغدغه‌های تداخلی برروی تعداد زیادی از پیمانه‌‌های برنامه پخش می‌شوند و اثر می‌گذارند و به همین دلیل طراحی و درک سیستم مورد نظر سخت‌تر، و همچنین پیاده‌سازی آن پیچیده‌تر خواهد شد. تغییر هم در چنین سیستمی مطمئناً سخت‌تر خواهد بود. برنامه‌نویسی جنبه‌گرا بهتر از روش‌های متداول قبلی دغدغه‌ها را از پیمانه‌‌های سیستم جدا می‌کند. به همین دلیل بهتر می‌توان از تداخل دغدغه‌ها جلوگیری کرد.
در این مقاله به بررسی مشکلاتی که تداخل دغدغه‌ها در بخش‌های مختلف برنامه ایجاد می‌کند می‌پردازیم و سپس مفاهیم پایه‌ی برنامه‌نویسی جنبه‌گرا را مورد بررسی قرار می‌دهیم. در نهایت به بررسی چند نمونه از زبان‌هایی که از جنبه‌گرایی پشتیبانی می‌کنند می‌پردازیم.


بخش1                    سیر تکامل روش برنامه‌نویسی
در روزهای اولیه‌ی پیدایش علم کامپیوتر، برنامه‌نویسان برنامه‌های خود را مستقیماً با استفاده از کدهای سطح ماشین می‌نوشتند. به همین دلیل آنان فکر خود را بیشتر از اصل مسأله، متوجه مجموعه دستورات ماشین مورد نظر می‌کردند! به‌تدریج زبان‌های سطح بالاتری به‌وجود آمدند که برای ما سطحی از انتزاع بر روی کامپیوترهای مختلف مورد استفاده ایجاد می‌کردند.  وقتی به یک زبان برنامه می‌نوشتیم، دیگر لازم نبود به این مسأله فکر کنیم که برنامه روی چه ماشینی اجرا می‌شود. بعد از آن نوبت به زبان‌های ساخت‌یافته رسید. می‌توانستیم برای راحت‌تر کردن کار، مسأله‌ی خود را به تعدادی روال، که برای انجام کارها لازم هستند، تقسیم کنیم. برنامه نویسی شیء گرا به ما این اجازه را داد که یک سیستم را به صورت تعدادی شیء که با هم ارتباط و همکاری دارند ببینیم. کلاس‌ها به ما این اجازه را می‌دادند که جزئیات پیاده‌سازی را پشت واسط برنامه‌نویسی پنهان کنیم. چند شکلی بودن امکان رفتار و واسطه مشترک برای مفاهیم مشابه را به ما می‌داد. به این وسیله می‌توانستیم پیمانه‌های خاص و جدید را بدون نیاز به دست‌کاری در پیاده‌سازی مفاهیم پایه ایجاد کنیم.
روش‌های برنامه‌نویسی و زبان‌ها در واقع راه ارتباط با ماشین را تعریف می‌کنند. هر روش جدید راه‌های جدیدی برای تجزیه‌ی مسأله ارائه می‌دهند: کد ماشین، کد مستقل از ماشین، روال‌ها، کلاس‌ها، و ... . هر روش جدید یک طرز نگرش تازه برای تبدیل نیازهای سیستم به زیرساخت‌های برنامه‌نویسی ارائه می‌دهد. تکامل این روش‌های برنامه‌نویسی این امکان را به ما می‌دهد که سیستم‌های پیچیده‌تری ایجاد کنیم. عکس این مطلب نیز صادق است: سیستم‌های پیچیده می‌توانند پیاده‌سازی شوند، چون این روش‌ها به ما این اجازه را می‌دهند.
در حال حاضر، بیشتر برنامه‌نویسی شیء‌گرا به عنوان روش ایجاد پروژه‌های نرم‌افزاری استفاده می‌شود. این روش قدرت خود را روی مدل کردن رفتارهای معمولی نشان‌داده است. ولی، همان‌طور که به زودی خواهیم دید، و همانطور که ممکن است شما قبلاً تجربه کرده باشید، این روش به خوبی نمی‌تواند روی رفتارهایی که بین چندین پیمانه (مخصوصاً پیمانه‌های مستقل و احتمالاًبی‌ربط) مشترک است کار کند. بلعکس روش برنامه نویسی جنبه‌گرا تا حد خوبی این مشکل را برطرف می‌کند. به احتمال زیادی برنامه‌نویسی جنبه‌گرا ارائه‌دهنده‌ی گام بزرگ بعدی در تحول روش‌های برنامه نویسی خواهد بود.
 

بخش2                    تحلیل یک سیستم به عنوان مجموعه‌ای از دغدغه‌ها
یک سیستم پیچیده‌ی نرم‌افزاری را می‌توان به صورت پیاده‌سازی مخلوطی از چندین دغدغه ببینیم. یک سیستم خیلی معمولی ممکن است از تعداد زیادی دغدغه، شامل منطق کاری، کارایی، ماندگاری داده‌ها، ثبت وقایع و اشکال‌زدایی، هویت شناسی، امنیت، امنیت کار موازی[5]، بررسی اشکال‌ها و غیره تشکیل‌شده‌باشد. همچنین ممکن است با دغدغه‌های فرآیند تولید نرم‌افزار، مانند قابلیت درک، قابلیت نگهداری، قابلیت ردیابی، و سادگی تحول در سیستم مواجه باشیم.
 ما نیازمندی‌ها را از یک منشور مشخص کننده‌ی دغدغه‌ها می‌گذرانیم، که این منشور دغدغه‌های مختلف مربوط را در هر نیازمندی مشخص می‌کند. همین دیدگاه برای دغدغه‌های فرآیند ایجاد نرم‌افزار نیز قابل تعمیم است.
 

بخش3                    دغدغه‌های متداخل در یک سیستم
برنامه‌نویس، یک سیستم را به عنوان راه‌حلی برای چندین نیازمندی به وجود می‌آورد. به طور کلی می‌توان این نیازمندی‌ها را به نیازمندی‌های سطح مرکزی و نیازمندی‌های سطح سیستمی تقسیم کرد. بسیاری از نیازمندی‌های سطح سیستمی مستقل از یکدیگر و مستقل از نیازمندی‌های سطح مرکزی هستند. نیازمندی‌های سیستمی همچنین در بسیاری از پیمانه‌های مرکزی تداخل دارند. برای مثال، یک برنامه‌ی بنگاهی[6] از تعداد زیادی دغدغه‌ی تداخلی مانند هویت شناسی، ثبت وقایع، استفاده‌ی مشترک از منابع، سرپرستی، کارایی، و مدیریت ذخیره‌سازی تشکیل شده‌است. هر کدام از این دغدغه‌ها با بسیاری از زیرسیستم‌ها تداخل دارد. برای مثال دغدغه‌ی ذخیره‌سازی بر روی تمام اشیاء ماندگار در سیستم اثر می‌گذارد.
به این مثال ساده ولی واقعی توجه کنید. پیاده‌سازی محافظت شده[7] ی یک کلاس تجاری را در نظر بگیرید:
 

 

 

public class SomeBusinessClass extends OtherBusinessClass

}

   Core data members //     

 Other data members: Log stream, data-consistency flag //     

  Override methods in the base class //     

(public void performSomeOperation(OperationInformation info    

}    

 Ensure authentication //     

 Ensure info satisfies contracts //     

 Lock the object to ensure data-consistency in case other //     
 
threads access it //     

  Ensure the cache is up to date //     

 Log the start of operation //     

  ==== Perform the core operation ==== //     

  Log the completion of operation //     

 Unlock the object  //     

{     
More operations similar to above //    

(public void save(PersitanceStorage ps    
    {     
    }    

(public void load(PersitanceStorage ps    
    {     
    }     

{

 


در کد فوق، باید حداقل سه مسأله را مورد نظر قرار داد.

Ÿ        اول اینکه فیلدهای دیگر (Other Data Members) مربوط به دغدغه‌ی اصلی این کلاس نیستند.

Ÿ        دوم اینکه به نظر نمی‌رسد پیاده‌سازی متد performSomeOperation() فقط مربوط به کار اصلی این متد باشد. در واقع در پیاده‌سازی این متد، در کنار دغدغه‌ی اصلی، به کارهای جانبی‌ای از قبیل ثبت وقایع، چک کردن هویت، امنیت کار موازی، چک کردن درستی cache و ... پرداخته‌است. علاوه بر این، به نظر می‌رسد بسیاری از این دغدغه‌های جانبی به همین ترتیب در کلاس‌های دیگر نیز پیاده‌سازی شده‌اند.

Ÿ        سوم اینکه مشخص نیست که آیا متدهای save() و load() که کار مدیریت ماندگاری داده‌های را انجام می‌دهند باید بخشی از قسمت اصلی این کلاس باشند یا خیر.


[1] Concern

[2] Aspect Oriented Programming

[3] Concern

[4] Cross-cutting Concerns

[5] Multithread safety

[6] Enterprise Application

[7] Encapsulated