خانه / دانستنیها / Bijection فراتر از dependency injection (جشنواره عید تا عید)

Bijection فراتر از dependency injection (جشنواره عید تا عید)

(این مطلب توسط آقای امید پورهادی برای جشنواره عید تا عید جاواکاپ ارسال شده است و محتوای این مطلب لزوماً موردتأیید جاواکاپ نیست)

dependency injection یا inversion of control امروزه مفهومی آشنا برای اکثر جاوا دولوپر هاست .
dependency injection به یک کامپوننت اجازه میده تا reference از کامپوننتی دیگر رو با استفاده از کانتینر در خودش تزریق کنه .

در تقریبا تمام پیاده سازی های dependency injection مخصوصا پیاده سازی های سنتی ( اسپرینگ ) که شما تا حالا دیدید injection ( تزریق ) زمانیکه کامپوننت ساخته شده است ، رخ میدهد و reference در مدت طول عمر کامپوننت تغییری نمی کنه . فریم ورک جی باس سیم مفهوم جدیدی بنام bijection را معرفی می کند که بر خلاف injection :

  • کامپوننت ها با اسکوپ های کانتکست بزرگتر می توانند رفرنس به کامپوننت ها از کانتکست کوچکتر داشته باشند . این یعنی شما باید از خودتون بپرسید آیا در اسپرینگ می توان کامپوننتی با اسکوپ prototype را در کامپوننتی با اسکوپ singleton تزریق کرد ؟ اگر که مفهوم این دو را کامل بدانید جوابتان خیر است البته اسپرینگ کلی امکانات دیگه ای داره که شما می تونید دلتون رو به اون ها خوش کنید .
  • دوطرفه بودن تزریق : کامپوننت ها می توانند از کانتکس ، داخل متغیر ها تزریق شوند و پس از اعمال تغییر روی آن ها به بیرون پرتاب شوند .
  • پویایی : از اونجاییکه مقدار متغیرهای کانتکس در طول زمان تغییر می کند و کامپوننت های سیم stateful است bijection هر بار که یک کامپوننت اجرا می شه ، صدا زده می شود .

اجازه بدید جملات مبهم بالا رو با چند مثال توضیح بدم :

فرض کنید شما فرمی دارید که کاربر ، کد ملی خود را وارد می کند و فاکتورهایش را میبیند :

پس از کلیک بر روی دکمه مشاهده فاکنور .

برخلاف سیم ، تزریق در اسپرینگ زمانیکه متد اجرا می شود رخ نمی دهد فقط زمانی تزریق اتفاق میفتد که bean اسپرینگ instantiate میشود .

یک مثال دیگر ، فرض کنید در یک فرم ثبت نام ، کاربر باید دو معرف را نیز همراه با ثبت نام وارد کند .

حالا سعی کنید در اسپرینگ یک کامپوننت را در خودش تزریق کنید یا حتی ساده تر از اون.

 

اسپرینگ به شما می گوید انقدر باهوش نیست که اینکار را انجام دهد و خطای Spring Circular Dependency exception .

در اپلیکیشن های اسپرینگ رسم است که حتی لایه های سرویس که منطق برنامه ( business logic ) در آن وجود دارد را اینترفیس میگیرند درحالیکه در برنامه نویسی شی گرا ، پرکاربردترین استفاده اینترفیس  زمانی است که پیاده سازی های مختلفی از یک مسئله وجود داشته باشد مگر اینکه در حال توسعه SPI باشید  مثلا  لایه دیتا اکسس ، طبیعی است که می تواند اینترفیس باشد چون پیاده سازی های مختلفی برای پایگاه داده های مختلف وجود دارد. bean های اسپرینگ  resolve by type است . به کد زیر دقت کنید .

اسپرینگ از کجا میفهمد کدام کامپوننت آپلود منظور است ؟ عمرا نمیفهمد برای اینکار اسپرینگ یک annotation دیگری بنام Qualifier دارد. Seam Resolve by name است .

قضیه به اینجا ختم نمیشه و اینها تنها  مواردی در قسمت DI فریم ورک اسپرینگ استو این موارد به اسپرینگ mvc هم قابل تعمیم است . فریم ورک اسپرینگ در جاوا راه حلی برای مشکلی است که آن مشکل دیگر وجود ندارد .

با توجه به اینکه این پست خیلی مورد عنایت و توجه قرار گرفت بروز رسانی ای در آن انجام شد .

 

کدوم دولوپری نیست که از دیدن چنین کدی خسته نشده باشه ؟!

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

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

ابتدا یک annotation میسازیم

برای فهمیدن بقیه کد به جاواداک اسپرینگ مراجعه کنید

در فایل کانفیگ اسپرینگ

البته اسپرینگ از نسخه ۴٫۳ به بعد فهمیدن که این امکان خوبه و مفهومی بنام injectionpoint اضافه کرد تا برای انجام همچین کار ساده ای خون دماغ نشید .

Component driven event

اگر با pattern observer/observable آشنا باشید نیاز به توضیح اضافی در این قسمت نیست . فرض کنید بعد از اجرای یک متد میخواهید یک رویدادی در یک متد کامپوننت دیگر صدا کنید . در قریم ورک سیم اینکار به اینصورت است .

اسپرینگ امکان event رو به شما میده ولی استفاده از اون به این راحتی نیست ولی باز هم بعنوان یک دولوپر حرفه ای شما باید کدهای خوانا و قابل maintain تولید کنید و اینکار با فریم ورک اسپرینگ به این راحتی نیست برای پیاده سازی این امکان در اسپرینگ به صورت زیر عمل می کنیم . قبل از انجام اینکار باید با اسپرینگ AOP آشنا باشید .

 

تنظیمات در فایل کانفیگ اسپرینگ

همانطور که میبیند AOP در عین حال که یکی از قدرت های اسپرینگ به شمار میرود یکی از ضعف های آن هم هست چراکه در اسپرینگ meta programming های زیادی وجود دارد اگر از google Guice استفاده کرده باشید از آنجاییکه از کانفیگ های xml خبری نیست شما میدانید دقیقا چه اتفاقی در حال رخ دادن است ( این بحث خارج از اسکوپ این مقاله است )

هر احمقی می تونه با اسپرینگ کد بزنه و در نهایت شاید یک نرم افزار تولید کنه ولی هر کسی نمی تونه با این فریم ورک ، نرم افزاری قابل maintain با کدهای خوانا و معماری خوب تولید کنه .

اسپرینگ شاید فریم ورک خوبی باشه ولی فقط برای اون هاییکه درست بلدن از این ابزار استفاده کنن . شما باید از خودتان بپرسید که یک فریم ورک  چقدر در تولید یک نرم افزار با معماری خوب به کمکتان می آید ؟!

 

فریم ورک اسپرینگ معماری را به شما تحمیل می کند

فرض کنید در لایه سرویس چنین متدی داریم که چند رکورد در پایگاه داده insert می کنه و بعدش در همون متد می خوایم لیستی از رکوردهای insert شده را برگردونیم

 

اگر کد بالا را در لایه سرویس اجرا کنید از آنجاییکه اسپرینگ فقط در سطح متد تراکنش داره یا بهتر بگم entityManager رو proxy میکنه به SharedEntityManagerBean با وجود اینکه شما flush کرده اید و سه تا مشتری insert کردید لیست شما خالی خواهد بود و مجبور می شید تا کوئری native را در یک متد جداگانه کال کنید .

اگر هم بخواهید اسپرینگ را مجبور کنید که تراکنش رو بدست شما بسپاره با خطای زیر مواجه می شوید .

درسته که کد نمونه بد نوشته شده اما رفتارهای غیرمنتظرانه این فریم ورک همیشه شما را شگفت زده خواهد کرد .

 


درباره امید پورهادی

همچنین بررسی کنید

تصاویر چهارمین آزمون عمومی جاواکاپ

 

۷ نظر

  1. ضمن تشکر از مقاله تون باید عرض کنم که در این مطلب یک سری ادعا پشت سر هم مطرح شده است که نیاز به توضیح و شفاف سازی بیشتری دارد.
    Inject کردن prototype در singleton در spring به شکلی که شما مطرح کردید بدون دخالت Container و از طریق proxy قابل انجام است (مثل Inject کردن EntityManager در JPA یا Request در JaxRS). راستش این بحث یک مقدار به معماری فریم ورک و Application وابسته است. ما در Spring و معماری restful خیلی نیازی به این مسئله حس نمی کنیم. مثال هایی که شما زدید همه Inject کردن ورودی هایی است که از سمت کاربر آمده است و این موارد در Spring MVC از طریق متود، inject می شود. فکر نمی کنم از دیدگاه عملکردی تفاوتی وجود داشته باشد و بیشتر ناشی از تفاوت رویکرد است. (Request Based vs Component Based Frameworks)
    بحث circular dependency نیز در spring ممکن است و مشکلی ندارد. فقط زمانی که dependency ها در constructor تعریف شده باشند خطا می دهد که منطقی به نظر می رسد.

     
  2. سلام،
    با تجربه ای که دارم، همانطور که حسام اشاره کرد، مشکلاتی که فرمودید در اسپرینگ وجود ندارد. من فقط یک نکته اضافه کنم که در بحث inject کردن, اسپرینگ هم resolve by type، هست و هم resolve by name.

     
  3. «فریم ورک اسپرینگ در جاوا راه حلی برای مشکلی است که آن مشکل دیگر وجود ندارد .» یعنی چی ؟؟ میشه دقیق تر توضیح بدید …

     
  4. شما فریم ورک اسگرینگ به این بزرگی رو فقط در ایمپورت کردن چهارتا دیپندنسی میبینید؟

     
  5. فریم ورک اسپرینگ کلی تو دنیا طرفدار داره – کلی از پروژه ها دارن باهاش ران میشن ، اگه به وب سایتهای کاریابی خارجی مثل مانستر و… هم سر بزنید میبینید که فریم ورک اسپرینگ بخش جدایی ناپذیر نیازمندی های برنامه نویس جاوا هستش ، الان منظورتون چیه ، به نظرتون عمر اسپرینگ به سر اومده یا شرکت ها به سمت سیم مهاجرت کنند ؟ خیلی از شرکت های قدیمی غربی خصوصا توی اروپا وقتی میبینن تکنولوژی که استفاده میکنن و اگه قدیمی هم حتی شده باشه ولی اگه سیستمشون همچنان بالا باشه ازش استفاده میکنن و به زور رو میارن به چیزای جدیدتر مثل اسپرینگ بوت و… و نمیان دوباره کلی پول خرج کنن تا پروژه خودشون رو بیارن روی یه فریم ورک جدیدتر ، توی انگلیس من خودم پروژه هایی رو دیدم که به EJB 2.1, final درست شده بودن و هنوز هم سرویس میدادن

     
  6. سلام
    متن مطلب کاملا گواهی میده که شما به اسپرینگ مسلط نیستید
    به نظرم شما اگه اسپرینگ رو مسلط بشید نظرتون کامل تغییر می کنه

     

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *