mirbozorgi.com
mirbozorgi.com
خواندن ۴ دقیقه·۳ سال پیش

الگوهای طراحی (Design Patterns) رایج و کاربردی در فریم‌ورک spring (قسمت دوم)

  • الگوی پروکسی (proxy)

الگوی پروکسی یکی دیگر از الگوهای طراحی به شمار می رود. پروکسی‌ها ابزار‌های بسیار کاربردی و مفیدی در دنیای دیجیتال هستند و اغلب ما از آنها به صورت خارج از نرم افزار (مانند پروکسی‌های شبکه‌ای) استفاده می‌کنیم. در دنیای کد ها، الگوی پروکسی، تکنیکی است که به یک object (proxy) اجازه می‌دهد تا دسترسی به object دیگر، موضوع (subject) و یا سرویس را کنترل کند.


تراکنش ها (Transactions)

برای ایجاد یک پروکسی، ما ابتدا یک object ایجاد می‌کنیم که رابط کاربری مشابهی با موضوع (subject) ما را implement می‌کند و حاوی ارجاع به subject است. سپس می توانیم از پروکسی به جای subject استفاده کنیم.

در فریم‌ورک spring، bean‌ها برای کنترل دسترسی به bean زیرین proxied می‌شوند. ما این رویکرد را هنگام استفاده از transaction‌ها مشاهده می‌کنیم:

در کلاس BookManager ما، متد ایجاد شده را با حاشیه‌نویسی (annotation) @Transactional، حاشیه‌نویسی (annotate) می‌کنیم. این حاشیه نویسی (annotation) به Spring دستور می‌دهد تا متد ایجاد (create method) ما را به صورت اتمی (atomically) اجرا کند. بدون وجود پروکسی، Spring نمی‌تواند دسترسی به bean BookRepository ما را کنترل کند و از ثبات تراکنش‌های آن اطمینان حاصل کند.

پروکسی های CGLib

در عوض، Spring یک پروکسی ایجاد می‌کند که bean BookRepository ما را wrap می‌کند و bean ما را به صورت اتمی (atomically) برای اجرای متد create ما ابزار‌سازی می‌کند.

وقتی ما متد BookManager#create خود را فرا می‌خوانیم، می‌توانیم خروجی (output) را ببینیم:

به طور معمول، ما انتظار داریم که یک شناسه object استاندارد BookRepository را ببینیم، ولی در عوض، یک شناسه object EnhancerBySpringCGLIB را می‌بینیم.

در پشت صحنه، spring، object BookRepository ما را به عنوان object EnhancerBySpringCGLIB در داخل wrap کرده است. بنابراین Spring دسترسی به object BookRepository ما را کنترل می‌کند (اطمینان از سازگاری تراکنش ها یا transactional consistency).

به طور کلی، Spring از دو نوع پروکسی استفاده می‌کند:

  • پروکسی‌های CGLib - هنگام پروکسی سازی کلاس ها استفاده می‌شود
  • JDK Dynamic Proxies – هنگام پروکسی سازی رابط‌ها (interface) استفاده می‌شود

در حالی که ما از تراکنش‌ها (transaction) برای افشای پروکسی‌های زیربنایی استفاده می‌کنیم، Spring برای هر سناریویی که در آن باید دسترسی به یک bean را کنترل کند، از پروکسی‌ها استفاده می‌کند.

  • الگوی template

در بسیاری از فریم‌ورک ها، بخش قابل توجهی از کدها را کد‌های boilerplate تشکیل می‌دهند.

به عنوان مثال، هنگام اجرای یک پرس و جو (query) در پایگاه داده، همان سری از مراحل باید طی شوند:

  • برقراری یک ارتباط
  • اجرای query
  • اجرای تمیزسازی (cleanup)
  • اتمام ارتباط

این مراحل یک سناریوی ایده آل برای اجرای الگوی template هستند.

Template‌ها و callback‌ها

الگوی template، به عنوان یکی از الگوهای طراحی، تکنیکی است که وظیفه‌ی تعریف مراحل مورد نیاز برای انجام برخی از اقدامات را بر عهده دارد؛ همچنین این الگو مراحل boilerplate را اجرا می‌کند و مراحل قابل تنظیم (customizable steps) را به صورت انتزاعی قرار می‌دهد. سپس کلاس‌های فرعی (Subclasses) می‌توانند این کلاس انتزاعی را پیاده‌سازی کنند و یک پیاده‌سازی مشخص (concrete implementation) را برای مراحل از دست رفته ارائه کنند.

ما می توانیم به این صورت در مورد query پایگاه داده خود، یک template ایجاد کنیم:

از طرفی دیگر، ما می‌توانیم مرحله‌ی گمشده (missing step) را با ارائه یک متد callback ارائه کنیم.

روش callback روشی است که به subject اجازه می‌دهد تا به مشتری (client) سیگنال دهد که برخی از اقدامات مورد نظر انجام شده است.

در برخی موارد، subject می‌تواند از این callback برای انجام اقداماتی استفاده کند (مانند نتایج نقشه‌برداری (mapping)).

به عنوان مثال، به جای داشتن یک متد executeQuery، می توانیم به متد execute یک رشته‌ی query و یک متد callback برای رسیدگی به نتایج ارائه کنیم.

ابتدا، ما متد callback را ایجاد می‌کنیم که یک object Results را می‌گیرد و آن را به یک object از نوع T، map می‌کند:

سپس کلاس DatabaseQuery خود را برای استفاده از این callback تغییر می‌دهیم:

این مکانیسم callback دقیقاً رویکردی است که فریم‌ورک Spring با کلاس JdbcTemplate از آن استفاده می‌کند.

الگوی template Jdbc

کلاس JdbcTemplate متد query را ارائه می‌دهد که یک query String و object ResultSetExtractor را پذیرش می‌کند:

مواردی همچون ResultSetExtractor، object ResultSet را (که نشان دهنده‌ی نتیجه query است) به یک object دامنه (domain object) از نوع T تبدیل می‌کند:

در ادامه، کد boilerplate را با ایجاد واسط‌های callback خاص‌تری کاهش می‌دهد.

به عنوان مثال، رابط RowMapper برای تبدیل یک ردیف از داده‌های SQL به یک object دامنه از نوع T استفاده می‌شود.

برای تطبیق رابط RowMapper با ResultSetExtractor مورد انتظار، Spring کلاس RowMapperResultSetExtractor را ایجاد می‌کند:

به جای ارائه منطق (logic) برای تبدیل کل یک object ResultSet، از جمله تکرار در ردیف‌ها، می‌توانیم منطق نحوه‌ی تبدیل یک سطر را ارائه کنیم:

با این مبدل، می‌توانیم با استفاده از JdbcTemplate، یک پایگاه داده (database) را query کنیم و هر ردیف حاصل را map کنیم:

جدا از پایگاه داده JDBC، Spring همچنین از template‌ها برای موارد زیر نیز استفاده می‌کند:

  • Java Message Service (JMS)
  • Java Persistence API (JPA)
  • Hibernate (اکنون منسوخ شده)
  • Transactions

سخن آخر

در صورتی‌که انتقاد و یا پیشنهادی دارید، در قسمت نظرات ما را مطلع نمایید. همچنین می‌توانید برای دسترسی به کدهای بیشتر از سایت ما دیدن کنید.



javadesign patternproxytemplatespring
ما تیم mirbozorgi.com هستیم. اگر بعد از خوندن مقاله های ما فکر کردید جایی در اشتباه هستیم؛ لطفا بما ایمیل بزنید. اگر هم نیاز به یادگیری تکنولوژی جدیدی دارید خوشحال میشیم بهتون کمک کنیم.
شاید از این پست‌ها خوشتان بیاید