<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>نوشته های Parsa Mihandoost</title><link>https://virgool.ir/feed/@mihandoost.parsa</link><description>سرگردون ابدی</description><language>fa</language><pubDate>2026-11-10 23:46:04</pubDate><image><url>https://files.virgool.ir/upload/users/8543/avatar/KOvQQt.png</url><title>Parsa Mihandoost</title><link>https://virgool.ir/@mihandoost.parsa</link></image><item><title>نحوه تست نویسی در Spring boot</title><link>https://virgool.ir/@mihandoost.parsa/نحوه-تست-نویسی-در-spring-boot-exszjoggj5kd</link><description>سخت نگیرید!تست نوشتن رو اغلب کسایی آموزش میدهند و توصیه میکنند که ایده‌آل گرا هستند و اگر شماهم ایده‌آل گرا باشید جمعش میشه اینکه بیخیال اینهمه اصول میشید و حتی  تست نویسی رو با کیفیت متوسط هم انجام نمیدید. تجربه یکسال اخیر من توی جیرینگ بهم ثابت کرد تست نوشتن اونقدرم چیز وقت گیر و پیچیده‌ای نیست و کافیه چنتا مفهوم و تکنیک رو بلد باشید تا بشه باهاش تست نوشت و در اکثر موارد کار با همینا راه میوفته. پس شل کنید و با من همراه باشید :) .اصل اول : Unit tests are better than Integration testsخب همینجور که میدونیم تست واحد سریع‌تر و کم حجم‌تر از تست تجمیعیه و این باعث میشه حوصله ما کمتر سر بره و فرایند بیلد هم سریع‌تر بشه و در نتیجه باعث میشه productivity بهتری داشته باشیم. البته تست‌های تجمیعی هم لازم هستن اما جاهایی باید استفاده بشن که واقعا راهی جز اونا نیست.چجوری فقط یک واحد از کد رو مستقلا تست کنیم؟ مثلا برای اینکه بخوایم یک سرویس به نام ProductService که داخلش سعی میکنه یک محصول رو بفروشه و برای این کار با دیتا بیس چک میکنه که تعداد محصول برای فروش کافی هست یا نه شاید در نظر اول اینطور بنظر میاد که برای نوشتن تست باید دیتابیس رو هم بیاریم بالا و در نتیجه تست تجمیعی انجام بدیم. اما اگر بخواهیم تنها و تنها عملکرد خود کلاس ProductService رو تست کنیم نیازی به دیتابیس واقعی نداریم و میتونیم در نظر بگیریم که دیتابیس داره کارش رو درست انجام میده. چطور ؟ بله با Mockito. قبل از اینکه بگم چجوری با ماکیتو میشه کارکرد یه خلاصه‌ای از ساختار تست‌ها در Spring Boot میگم.نیازمندی‌های پروژه نیازمندی‌هایی که برای این مطلب نیاز دارید به spring boot اضافه کنید اینهاست :&lt;dependency&gt;&#xA;&#x9;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&#xA;&#x9;&lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;&#xA;&lt;/dependency&gt;&#xA;&#xA;&lt;dependency&gt;&#xA;            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&#xA;            &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;&#xA;            &lt;scope&gt;test&lt;/scope&gt;&#xA;  &lt;/dependency&gt;البته test رو خودش اضافه میکنه By Defaultساختار تست نویسی در Spring Bootدر ابتدا میخوام انوتیشن‌هایی  رو توضیح بدم که با باز کردن کلاس تست خود Spring boot اونو میبینید. کلاس به شکل زیر است :@SpringBootTest&#xA;class TestingApplicationTests {&#xA;@Test&#xA;void contextLoads() {&#xA; // loads application contex&#xA;      }&#xA;}این کلاس در واقع یک integration تست هست که داره کامل اپلیکیشن مارو میاره بالا. بالای هر کلاس تست اگر انوتیشن @SpringBootTest باشد معنیش اینه که اون کلاس کل اپ رو میاره بالا و میشه توش از امکانات اسپرینگ مثل @Autowired و هرچیزی که داخل context هست بصورت واقعی  استفاده کرد. و بدرد تست‌های تجمیعی میخوره. توی این کلاس میشه تمام قابلیت‌های Junit5 رو استفاده کرد و علاوره بر اون خود اسپرینگ کلی قابلیت‌های دیگه داره مثلا میتونه اپ رو با پورت رندوم بیاره بالا یا یه پروفایل خاص رو لود کنه و هزارتا چیز دیگه که الان لازم نیست همشو بلد باشیم کافیه موارد پایه رو بدونیم و بعدا باتوجه به شرایط سرچ کنیم و راه‌حل موردنظر حتما پیدا خواهد شد. برای اینکه این کلاس تبدیل بشه به یک یونیت تست کافیه اون انوتیشن رو برداریم تا هیچ چیز اضافی قبل از تست لود نشه.تفاوت‌های Junit4 و Junit5این مورد کمی آزاردهندس چون Junit4 همچنان زندس و خیلی از آموزش‌ها با این ورژن داده شده اما وقتی یک Spring boot جدید میسازید با Junit5 کارمیکنه و یکم دردسرسازه. حواستون به این مورد باشه که وقتتون هدر نره مثلا موقع سرچ حتما به ورژن اشاره کنید به تناسب نیازتون. من اینجا در ادامه سعی میکنم به تفاوت‌ها اشاره کنم. تفاوت‌ها رو میتونید اینجا ببینید چگونه یک آبجکت Mock درست کنیم؟ خب تصور کنید ما یک ‌DAO به شکل زیر داریم :public interface ProductDAO extends JpaRepository&lt;Product, Long&gt; {&#xA;}و میخوایم مثلا توی یک سرویس به شکل زیر ازش استفاده کنیم :@Service&#xA;public class ProductService {&#xA;    @Autowired&#xA;  private ProductDAO dao;&#xA;&#xA;    public Boolean addToBasket() {&#xA;        if (dao.count() &gt; 0) {&#xA;            System.out.println(&#34;It has been added to basket&#34;);&#xA;            return true;&#xA;        } else {&#xA;            System.out.println(&#34;It&#39;s not enough to sell&#34;);&#xA;            return false;&#xA;        }&#xA;    }&#xA;}خب اینجا ما از DAO استفاده کردیم تا ببینم تعداد کافی محصول برای فروش داریم یا نه.حالا اگر بخوایم این کد رو تست کنیم در حالت ابتدایی اینکار رو میکنیم: @SpringBootTest&#xA;public class ProductServiceTest {&#xA;    @Autowired&#xA;    private ProductService productService;&#xA;&#xA;    @Test&#xA;    public void addToBasketTest() {&#xA;        Assertions.assertTrue(productService.addToBasket());&#xA;    }&#xA;}که یک تست سادس و بهش گفتیم اپ رو بیار بالا (که دیتابیس در دسترس باشه) بعد انگار که میخوایم از سرویس توی کنترلر استفاده کنیم متد اونو صدا میزنیم و میگیم انتظار داریم چی برگردونه. این کد با Junit5 نوشته شده. ولی ما شاید به دیتابیس دسترسی نداشته باشیم یا دیتای مورد انتظار تست توش نباشه (ما فقط همین یدونه تست رو نداریم وهزارتا شرایط دیگه باید قابل تست باشه) بنابراین بهتره DAO ماک بشه و چیزی که ما صراحتا بهش میگیم رو برگردونه برای اینکار اینطور کد تغییر میکنه :@SpringBootTest&#xA;public class ProductServiceTest {&#xA;    @MockBean&#xA;    private ProductDAO dao;&#xA;    @Autowired&#xA;    private ProductService productService;&#xA;&#xA;    @Test&#xA;    public void addToBasketTest() {&#xA;        when(dao.count()).thenReturn(2l);&#xA;        Assertions.assertTrue(productService.addToBasket());&#xA;    }&#xA;}جای جذاب کار همینجاس ! @MockBean کاری که میکنه اینه که یه آبجکت ماک از نوع ProductDAO  رو جایگزین آبجکت واقعی داخل IOC Container میکنه. حالا پایین‌تر ، تو متد خط اول میبینید که بهش گفتیم وقتی dao.count() صدا زده شد عدد ۲ برگردانده بشه و بعد کار قبلی رو کردیم و حالا تست پاس شد :).به کاری که تو خط اول کردیم میگن Stubbing :Stubbing means simulating the behavior of a mock object’s method. We can stub a method on a mock object by setting up an expectation on the method invocationاما اگر دقت کرده باشید ما هنوز داریم اپ رو کامل بالامیاریم! چرا ؟ هیچ دلیلی نداره ما نیازی بهش نداریم و فقط داریم وقت هدر میدیم.برای اینکه اپ لود نشه اولین کار اینه که @SpringBootTest رو  از بالای کلاس برداریم. اما برای پاس شدن تست باید کارهای دیگه‌ای هم انجام بشه. کد تغییر یافته رو ببینیم :@ExtendWith(MockitoExtension.class)&#xA;public class ProductServiceTest {&#xA;    @Mock&#xA;    private ProductDAO dao;&#xA;&#xA;    @InjectMocks&#xA;    private ProductService productService;&#xA;&#xA;    @Test&#xA;    public void addToBasketTest() {&#xA;        when(dao.count()).thenReturn(2l);&#xA;        Assertions.assertTrue(productService.addToBasket());&#xA;    }&#xA;}تغییر اول استفاده از @ExtendWith(MockitoExtension.class) هست که به ما این امکان رو میده که از انوتیشن‌های Mockito استفاده کنیم. بعدی  @Mock هست که بجای    @MockBean داریم ازش استفاده میکنیم فرقشون اینه که mock فقط ماک میسازه و کاری به IOC Container نداره (ما اصلا Container لود نکردیم تو این تست که بخوایم باهاش کاری بکنیم) . تغییر بعدی استفاده از   @InjectMocks بجای  @Autowired هست که در غیاب Application context وظیفه تزریق وابستگی با ماک‌هایی که ما ساختیم (که اینجا فقط ProductDAO هست) رو برعهده دارد. حالا تست ما که خییییلی سبک ، سریع و راحت شده پاس میشود :).تفاوت MockBean و Autowired چیه؟همینطور که قبلا گفتیم MockBean بجای کامپوننت واقعی یک ماک از آبجکت میسازه و اونو میذاره داخل ‌IOC Container اما Autowired آبجکت واقعی است (همونیه تو کد معمولی ازش استفاده میکنید). جفتشون استفاده دارن با دونستن تفاوت در جای مناسب میشه ازشون استفاده کرد.روش‌ دیگر ساخت Mock روش‌هایی که گفتم مرسوم‌ترین و راحت ترینش بود اما روش دیگرش استفاده از متد استاتیک ‌‌mock هست به این شکل :ArrayList mockList = Mockito.mock(ArrayList.class);که معادل :@Mock&#xA;ArrayList mockList;ویژگی +۱۸ ماکیتو  UnnecessaryStubbingExceptionاین قابلیت واقعا عالیه و از پیچیدگی کد تست جلوگیری میکنه. به این شکل که اگر یک stubbing بشکل زیر بنویسیم :when( dao.findById(anyLong()) ).thenReturn( Optional.of(new Product()) );اما در هیج‌جای تست ، متد dao.findById صدا زده نشده باشد یک اکسپشن میگیریم به نام UnnecessaryStubbingException و تست fail میشه و بهمون میگه کدوم stubbing زیادیه و با پاک کردنش مشکل حل  میشه.استفاده از دیتابیس تستی در Integration testهااگر بخواهیم Integration test بنویسیم که با دیتابیس کارمیکنه بهتره که از دیتابیسی جدا از اونی که داریم برای development استفاده میکینیم استفاده کنیم. معمولا از دیتابیس های In-memory برای تست استفاده میکنن که مرسوم‌ترینش تو جامعه جاوایی H2 هست. برای این‌کار کافیه در فایل properties که داخل پکیج  /src/test/resources/application.properties هست این کانفیگ‌هارو برای DataSource قرارا بدیم :spring.datasource.driver-class-name=org.h2.Driver&#xA;spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1&#xA;spring.datasource.username=sa&#xA;spring.datasource.password=sa&#xA;spring.jpa.hibernate.ddl-auto=create-dropو همینظور در داخل فایل POM هم نیازمندی H2 رو اضافه کنید :&lt;dependency&gt;&#xA;&#x9;&lt;groupId&gt;com.h2database&lt;/groupId&gt;&#xA;&#x9;&lt;artifactId&gt;h2&lt;/artifactId&gt;&#xA;&#x9;&lt;scope&gt;test&lt;/scope&gt; &#xA;&lt;/dependency&gt;حالا برای اجرای تست‌ها از H2 استفاده میشه.مبحث پیشرفته‌تربرای مباحث پیشرفته‌تر میتونید مطالعه‌ای روی @Spy داشته باشید که نوعی ماک هست. این لینک توضیحات خوبی داره دربارش.ممنون که همراه بودید برای بحث درباره این مطلب میتونید به توئیتر من سربزنید.منابع مطلبhttps://springframework.guru/mocking-unit-tests-mockito/https://www.baeldung.com/java-spring-mockito-mock-mockbeanhttps://howtodoinjava.com/mockito/mockito-mock-injectmocks/https://www.baeldung.com/junit-5https://www.baeldung.com/mockito-unnecessary-stubbing-exception</description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Thu, 10 Jun 2021 17:31:08 +0430</pubDate></item><item><title>کلاس خود را زندانی کنید تا Thread safe شود! (جاوا)</title><link>https://virgool.ir/@mihandoost.parsa/کلاس-خود-را-زندانی-کنید-تا-thread-safe-شود-جاوا-yhafzuwyruws</link><description>یکی از مشکلات و دردسرهای برنامه‌نویس‌های جاوا که سمت وب کار میکنن Thread safe بودن کلاس‌هاشون هست. میدونیم که Web container های جاوا مثل tomcat برای جواب دادن به درخواست‌ها از thread استفاده میکنند و هر درخواست در thread جدا پاسخ داده میشود بنابراین محیط کار ما multi-thread هست و باید حواسمون به shared-resource هامون باشه. امروز ساده‌ترین راه برای Thread-safe بودن رو بررسی میکنیم که میگه : اگر میخوای کلاست مشکل همروندی نداشته باشه و متغیرهات توسط تردهای دیگه دست‌کاری نشن بیا و همه متغیرهای سطح کلاست رو پاک کن و اونها رو در متد تعریف کن! بکارگیری این روش خیلی سادس مثلا مثال زیر :public class SafeClass{&#xA;    public void a(){&#xA;       int a=1;&#xA;       Object a=new Object()&#xA;      // do something&#xA;     }&#xA;}حالا اگر صدتا thread هم روی یک آبجکت متد a رو صدا بزنند هرکدوم متغیرهای خودشونو دارن و تداخلی باهم پیدا نخواهند کرد!اما چرا این اتفاق میوفته؟ چرا وقتی اونهارو در سطح کلاس تعریف کنیم این اتفاق نمیوفته؟جواب در فهم دقیق Stack و Heap نهفته‌س ، متغیرهایی که در متدتعریف میشن در Stack ‌ذخیره میشن و متغیرهایی که در سطح کلاس تعریف میشوند چه primitive باشند و چه آبجکت در ‌Heap ذخیره خواهند شد.حالا این چه ارتباطی به ‌thread-safe شدن کلاس ما داره ؟خب باید بگم که هر ترد stack مخصوص بخودش رو داره در نتیجه با این روش ما هیچ متغیری رو در هیچ حالتی با ترد دیگه‌ای به اشتراک نمی‌ذاریم و در نتیجه مشکلی در همروندی نخواهیم داشت.فقط بیاید یه ابهامی رو برطرف کنیم ، اول مثال زیر رو ببینید :public class UnSafeClass{  &#xA;   private List list;&#xA;   public void a(){ &#xA;        List mList = list;&#xA;        // do something&#xA;      }&#xA; } در کد بالا mList امن هست؟ خیر امن نیست چون ما ارجا آنرا برابر با متغیری قرار دادیم که در سطح کلاس تعریف شده و قابل دسترسی بوسیله بقیه thread هاست. یعنی اگه دوتا thread این متد رو روی یک آبجکت اجرا کنن درواقع دارن روی یک متغیر کار میکنن ???. شکل زیر در حافظه درست میشه :اگر بخوایم مشکل حل بشه باید mlist رو توی متد خودمون new کنیم و مقادیر list رو توش کپی کنیم و از اونجا به بعد با mList کارکنیم. که شکل زیر درست بشه :این راه بسیار ساده و بدون دردسره اما جاوا به OOP معروفه و این روش عملا همه مزیت‌های یک زبون OOP رو از شما میگیره بنابراین همیشه و همه‌جا نمیشه ازش استفاده کرد اما بسیار دم‌دستی و مطمئنه. اگر خواستید سرچ بیشتر بکنید در این رابطه کلیدواژه Confinement strategy رو در مباحث Thread جاوا سرچ کنید.ممنون که تا اینجا همراه بودید اگر خواستید بحث کنیم راجع‌به این موضوع یا حرفی سخنی بود اینجا یا توئیتر هستم.امن باشید :)</description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Sat, 19 Dec 2020 19:52:15 +0330</pubDate></item><item><title>ریست کردن مدت ۳۰ روزه Intellij IDEA (لینوکس)</title><link>https://virgool.ir/@mihandoost.parsa/ریست-کردن-مدت-۳۰-روزه-intelij-idea-لینوکس-velpcresaudq</link><description>سلام به اهالی خوب خاورمیانه میدونم درگیری دارین شمام با این IDE و IDE های مشابه مثل phpstorm و نه صرفه داره بخرید نه اصن بهتون میفروشن اما خیلی اعصاب خوردکنه چنوقت یبار دنباله روش کرک مناسب گشتن اگه به کسی نمیگید میخوام بگم چجوری تو لینوکس اون مدت ۳۰ روزه رو ریست کنید. کافیه سه تا فایل رو پاک کنید ولی قبلش باید جایی که Intelij فایلای کانفیگش رو ذخیره میکنه رو پیدا کنید معمولا توی home میذارتش ولی ممکنه تغییرش بده ورژن به ورژن و راه سادش اینه برید توی home و عبارت intellijIdea2020 رو سرچ کنید (به تناسب ورژنتون ) من اینکارو با همون file explore گنوم انجام میدم با رفتن تو home و زدن ctrl+f که نتیجه شبیه این میشه :ممکنه چنتا نتیجه بیاره که ما دنباله اونی هستیم که توش دایرکتوی eval رو داره (میشه حدس زد که اونیه که تو config. عه). برید توش و اونجا یه ترمینال باز کنید و به ترتیب :۱ - فایلای داخل eval رو پاک کنید ۲ - دنبال فایل other.xml بگردید که معمولا توی دایرکتوری options هست اگه نبود سرچش کنید به روش بالا. وقتی پیدا شد پاکش کنید۳ - کارمون با دایرکتوری کانفیگ intelij تمون شد حالا به این آدرس برید : cd  ~/.java/.userPrefs/jetbrains/idea/اینجا یه دایرکتوری با اسم رندوم باید باشه اونم پاک کنید.حالا پاک‌سازی تموم شد سیستم رو ریست کنید باید کارکنه (ریست نکنید باز نمیشه اصلا احتمالا)</description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Thu, 07 May 2020 00:08:26 +0430</pubDate></item><item><title>آموزش دیزاین پترن Decorator با طعم پیتزا</title><link>https://virgool.ir/javacup/آموزش-دیزاین-پترن-decorator-با-طعم-پیتزا-a2cafgs4dffl</link><description>خب این پترن بسیار جذاب همیشه درنگاه اول پیچیده میاد و آدم نمیتونه کاربرد اصلیشو توی دنیای واقعی و توی تسک‌های روزمره که میزنه پیدا کنه. با سرچ ها و تمرینایی که خودم کردم به این مثال رسیدم و امیدوارم بتونه شمارو هم تو این الگو راه بندازه.اول بریم سراِغ مفاهیم کلی که هرجا سرچ کنید هم نوشته مثلا اینکه این پترن یک پترن Structural است و برای این استفاده میشود که ما در runtime بتونیم یک آبجکت دیگر رو ارث بری کنیم بدون اینکه مستقیما در کد اون رو extend کرده باشیم. میدونم که اگر اولین باره که این جمله‌ها رو میشنوید اصلا تصوری ازشون ندارید اما بامن همراه باشید!بریم سراغ مثالمون. فرض کنید یه پیتزا فروشی زدیم :). توی پیتزا فروشیمون یه پیتزا normal داریم که هرکی سفارش بده و چیز خاصی نخواد اونو براش میبریم. اما دو نوع پیتزا دیگه‌ام داریم پپرونی و مخصوص که تفاوتشون برای ما تو کد فقط در قیمت و اسمیست که توی فاکتور نشون داده میشه. حالا برای پیاده کردن این مجموعه میتونیم یه کلاس پیتزا داشته باشیم و توش با فیلد مشخص کنیم که نوعش چیه که در این صورت هی باید کلاسمون رو تغییر بدیم که کار خوبی نیست و یا به ازای هر نوع کلاس بنویسیم که به نسبت کار قبلی منطقی تره ولی بیاید با decorator پیادش کنیم که هم یادبگیریم و هم راه حل هوشمند رو ببینیم!در این الگو ما یک کلاس base داریم که اینجا میشه NormalPizza و هرچنتا کلاس decorator که کلاس base مارو میتونن دکور کنن و بهش چیزای جدید اضافه کنند. اول بریم کلاس بیس رو پیاده کنیمpublic interface Pizza {&#xA;&#x9;int price();&#xA;&#x9;String name();&#xA;}&#xA;&#xA;public class NormalPizza implements Pizza{&#xA;&#xA;&#x9;@Override&#xA;&#x9;public int price() {&#xA;              return 10;&#xA;         }&#xA;&#xA;&#x9;@Override&#xA;&#x9;public String name() {&#xA;&#x9;&#x9;return &#34;Pizza&#34;&#xA;         }&#xA;}&#xA; خب اون اینترفیس که اول نوشتیم قلب ماجراس و هم base و هم decorator ها در نهایت این واسط رو پیاده میکنند. مقادیر متدها hardcode شده بدلیل سادگی.الان هرکی از ما پیتزا بخواد یه pizza بهش میدیم به قیمت ۱۰ (دلار ، ریال ، تومن ...) حالا بریم سراغ دکوراتورها. قبل از پیاده سازیشون باید یک کلاس ابسترکت بسازیم که اولا از pizza ارث بری کنه و دوما کلاس های دکوراتر رو مجبور کنه که در سازنده خودشون یک آبجکت از نوع Pizza بگیرن چون باید بدونیم چیو میخوایم دکور کنیم :public abstract class PizzaDecorator implements Pizza{&#xA;   protect Pizza pizza;&#xA; &#xA;   public PizzaDecorator(Pizza pizza){&#xA;       this.pizza=pizza;&#xA;    }&#xA;}توجه دارید که نمیتونستیم از اینترفیس استفاده کنیم چون اینترفیس نمیتونه سازنده داشته باشه.حالا همه دکوراتر ها باید از این کلاس ارث بری کنند و کارشون رو انجا بدن. مثلا کلاس PizzaSpecial ما اینجوری خواهد بود :public class PizzaSpecial extends PizzaDecorator{&#xA;  public PizzaSpecial(Pizza pizza){&#xA;       super(pizza);&#xA;   }&#xA;&#xA;  @Override &#x9;&#xA;   public int price() {&#xA;               return 5 + this.pizza.price();        &#xA;    } &#xA;&#xA;  @Override&#xA; public String name() {&#xA;&#x9;return    &#34;Special &#34; + this.pizza.name()&#xA;        }&#xA;}حالا ما هر پیتزایی رو به این دکوراتر بدیم اسم فاکتورش رو درست میکنه و قیمتش رو هم اضافه میکنه :Pizza pizza= new NormalPizza();&#xA;Pizza sPizza=new SpecialPizza(pizza);&#xA;&#xA;println(sPizza.name());خروجی برنامه  Special Pizza  خواهد بود :)حالا میتونیم تا هرچندتا دکوراتر دیگه هم براش درست کنیم و اونا به ترتیب اجرا میشن. مثلا دکوراتر آویشن‌(Thyme) :public class Thyme extends PizzaDecorator{   &#xA;&#xA;public Thyme(Pizza pizza){        super(pizza);    }&#xA;&#xA;    @Override&#xA;    public int price() {&#xA;              return 2 + this.pizza.price();&#xA;        }  &#xA;&#xA;   @Override  public String name() {&#xA; &#x9;return  this.pizza.name() + &#34; with Thyme&#34;&#xA;         }&#xA; }حالا برای استفاده مینویسم :Pizza pizza= new NormalPizza(); &#xA;Pizza sPizza=new SpecialPizza(pizza);&#xA;Pizza sPizzaThyme=new Thyme(sPizza);&#xA;&#xA;  println(sPizzaThyme.name());که خروجی میشه &#34;Special Pizza  with thyme&#34; و اگر قیمت رو هم پرینت کنید میشه ۱۷ و میبیند چقدر میتونیم توسعه بدیم کاررو بدون تغییر تو کلاس‌های دیگر و به این میگن loose coupling :)حالا میتونید خودتون برای تمرین کلاس پپرونی رو درست کنید و تست کنید و لذت ببرید.امیدوارم بدردتون خورده باشه سئوالی بود اینجا یا توئیتر هستم. </description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Sat, 25 Apr 2020 20:10:19 +0430</pubDate></item><item><title>توضیح کاربردی متد flatMap (جاوا استریم)</title><link>https://virgool.ir/@mihandoost.parsa/توضیح-کاربردی-متد-flatmap-جاوا-استریم-yiqgj8dnfeyo</link><description>برای خود من این متد یکم عجیب بود و کاربردش رو نمیفهمیدم. خب متدی نیست که تو روال عادی بهش نیاز پیدا کنید و برای موقعیت‌های خاص کاربرد داره و واسه همین درکش سخت میشه. ولی یه مثال واقعی پیدا کردم که فک کنم کامل متوجه موضوع بشید.قبلش یه توضیح کوتاه بدم : ما وقتی میتونیم رو استریم کار کنیم و متدهایی مثل filter و sort و غیره رو اجرا کنیم که آبجکت استریم ما همچین نوع‌ ساده‌ای داشته باشند: Stream&lt;String&gt;اما اگه همچین نوعی داشته باشیم بجای بالایی :Stream&lt;String[]&gt;&#xA;\\OR &#xA;Stream&lt;List&lt;String&gt;&gt;&#xA;\\ OR something like theseدر این حالت ما ارور نداریم اما کارها انجام نمیشن! اینجا اونجاییه که ما به flatMap نیاز داریم بریم سراغ مثال:این کلاس ساده Student رو درنظر بگیرید :public class Student {&#xA;&#xA;    private String name;&#xA;    private Set&lt;String&gt; book = new HashSet&lt;&gt;();&#xA;&#xA;    public void addBook(String book) {&#xA;        this.book.add(book);&#xA;    }&#xA;&#xA;    //getters and setters&#xA;}حالا بیاین دوتا آبجکت دانش آموز بسازیم  و بذاریمشون تو یه لیست:Student obj1 = new Student();&#xA;        obj1.setName(&#34;Parsa&#34;);&#xA;        obj1.addBook(&#34;Java 8 in Action&#34;);&#xA;        obj1.addBook(&#34;Spring Boot in Action&#34;);&#xA;        obj1.addBook(&#34;Effective Java (2nd Edition)&#34;);&#xA;&#xA;        Student obj2 = new Student();&#xA;        obj2.setName(&#34;Kiarash&#34;);&#xA;        obj2.addBook(&#34;Effective Java (2nd Edition)&#34;);&#xA;&#xA;        List&lt;Student&gt; list = new ArrayList&lt;&gt;();&#xA;        list.add(obj1);&#xA;        list.add(obj2);حالا فرض کنید میخوایم یه لیست داشته باشیم از کل کتابای دانش آموزا. خب اگه map کنیم همچین خروجی خواهیم داشت :List&lt;Set&lt;String&gt;&gt; books = list.stream()&#xA;     .map( s-&gt;s.getBook() )&#xA;     .collect(Collectors.toList()) ;خب همچین خروجی بدرد ما نمیخوره و بعدا نمیشه باهاش کار مفیدی کرد باتوجه به نکته‌ای که اول گفتم. پس بعد از اینکه map کردیم میام یه flatMap هم روش اجرا میکنیم. دقت کنید اینجا map موقعی که اجرا میشه و تموم میشه آبجکتی استریمی از ست‌ها برمیگردونه و واسه همین آخرشم لیست از ست‌ها درست میشه اما ما باید استریمی از String ها داشته باشیم و واسه همین از flatMap استفاده میکنیم :Stream&lt;Set&lt;String&gt;&gt; booksStream = list.stream().map( s-&gt;s.getBook() );&#xA;Stream&lt;String&gt; booksStream = booksStream.flatMap(b-&gt;b.stream());&#xA;List&lt;String&gt;      booksList = booksStream.collect(Collectors.toList()) ;&#xA;مرحله به مرحله ریختمشون تو متغیر که متوجه بشید تغییرات type رو.سوالی بود اینجا یا توئیتر هستم.</description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Wed, 22 Apr 2020 21:12:00 +0430</pubDate></item><item><title>شروع سریع Spring Security (قسمت دوم)</title><link>https://virgool.ir/@mihandoost.parsa/شروع-سریع-spring-security-قسمت-دوم-lkgg4opa9zcq</link><description>سلام ، امروز ادامه بخش اول رو مینویسم و به این میپردازم که چجوری از دیتابیس برای ذخیره کاربرها استفاده کنید.برای اینکار نیازی ندارید که بدونید که برای فلان دیتابیس باید چیکارکنید و درواقع solution سکیوریتی اصلا وابسته به دیتابیس خاصی نیست. شما میتونید دیتای خودتون رو باهر ساختاری که مدنظرتون هست هرجایی که دلتون میخواد ذخیره کنید (تو فایل ، mysql ، اکسل ‌، منگو ....).قلب این ماجرا دوتا interface هست به نام های UserDetails و UserDetailsService و کافیه بدونیم این دوتا interface چیکارمیکنن تا هر دیتابیسی رو بتونیم به پروژه سکیوریتی خودمون وصل کنیم.از UserDetails شروع کینم این واسط دوراقع اطلاعاتی که سکیوریتی از مدل یوزر انتظار داره رو براش فراهم میکنه اطلاعاتی از قبیل : نام کاربری ، پسورد ، نقش‌ها ، فعال بودن و چنتا چیز دیگه که البته میتونید همه رو بهش پاس ندید و چیزای اصلی رو فقط پیاده کنید ، منم اینجا خیلیاشو بهش مقدار پیش فرض میدم و پیاده نمیکنم. قبل از اینکه پیاده سازی این واسط رو ببینیم بریم مدل یوزرمون رو تعریف کنیم. من از mysql استفاده کردم اما آخر این آموزش وقتی همه پیاده سازی هارو دیدید متوجه میشید که چجوری میتونید با هرچیزی این واسط هارو پیاده کنیدپیاده سازی مدل User من به این شکله :@Entity&#xA;public class User {&#xA;    @Id&#xA;    @GeneratedValue(strategy = GenerationType.IDENTITY)&#xA;    private Long id;&#xA;    @Column(nullable = false)&#xA;    private String username;&#xA;    @Column(nullable = false)&#xA;    private String password;&#xA;    private int active;&#xA;    private String roles;&#xA;    private String authorities;&#xA;&#xA;    public User(String username, String password, String roles, String authorities) {&#xA;        this.username = username;&#xA;        this.password = password;&#xA;        this.roles = roles;&#xA;        this.authorities = authorities;&#xA;        active = 1;&#xA;    }&#xA;&#xA;    protected User() {&#xA;    }&#xA;&#xA;    public List&lt;String&gt; getAuthoritiesList() {&#xA;        if (!this.authorities.isBlank())&#xA;            return Arrays.asList(this.authorities.split(&#34;,&#34;));&#xA;&#xA;        return new ArrayList&lt;&gt;();&#xA;    }&#xA;&#xA;&#xA;    public List&lt;String&gt; getRolesList() {&#xA;        if (!this.roles.isBlank())&#xA;            return Arrays.asList(this.roles.split(&#34;,&#34;));&#xA;&#xA;        return new ArrayList&lt;&gt;();&#xA;    }&#xA;}&#xA;من برای اختصار getter و setter هارو حذف کردم. دوتا متد اضافه ام برای گرفتن لیست authority ها و role ها نوشتم چون دارن در دیتابیس بصورت comma separated ذخیره میشن این دوتا رو نوشتم جهت تبدیلشون به لیست.خب طبق روال Spring Data هر مدلی برای صحبت با دیتابیس نیاز به Repository داره که اونم تعریف میکنیم.@Repository&#xA;public interface UserRepo extends JpaRepository&lt;User, Long&gt; {&#xA;    User findByUsername(String username);&#xA;}فقط یک متد برای گرفتن یوزر با username گذاشتیم که بعدا باهاش کارداریم.حالا آماده ایم که واسط UserDetails را پیاده سازی کنیم. فقط هول نکنید :) خیلی سادس و قدم به قدم میگم که داریم چیکارمیکنیم و پیشنهاد میکنم اول کد رو نخونید و اول توضیحات زیرشو بخونید و همزمان ببینید کدش چیه.public class UserPrincipal implements UserDetails {&#xA;&#xA;   final private User user;&#xA;&#xA;    public UserPrincipal(User user) {&#xA;        this.user = user;&#xA;    }&#xA;&#xA;    /**&#xA;     * In this method we&#39;ve mixed user&#39;s roles and authorities because in Spring security&#xA;     * both of these store in a collection with different name convention that roles have ROLE_ in the first of string&#xA;     *&#xA;     * @return Collection&#xA;     */&#xA;    @Override&#xA;    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {&#xA;        List&lt;GrantedAuthority&gt; authorities = new ArrayList&lt;&gt;();&#xA;&#xA;        this.user.getAuthoritiesList().forEach(authority -&gt; {&#xA;            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(authority);&#xA;            authorities.add(grantedAuthority);&#xA;        });&#xA;&#xA;        this.user.getRolesList().forEach(role -&gt; {&#xA;            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(&#34;ROLE_&#34; + role);&#xA;            authorities.add(grantedAuthority);&#xA;        });&#xA;&#xA;        return authorities;&#xA;    }&#xA;&#xA;    @Override&#xA;    public String getPassword() {&#xA;        return this.user.getPassword();&#xA;    }&#xA;&#xA;    @Override&#xA;    public String getUsername() {&#xA;        return this.user.getUsername();&#xA;    }&#xA;&#xA;    @Override&#xA;    public boolean isAccountNonExpired() {&#xA;        return true;&#xA;    }&#xA;&#xA;    @Override&#xA;    public boolean isAccountNonLocked() {&#xA;        return true;&#xA;    }&#xA;&#xA;    @Override&#xA;    public boolean isCredentialsNonExpired() {&#xA;        return true;&#xA;    }&#xA;&#xA;    @Override&#xA;    public boolean isEnabled() {&#xA;        return this.user.getActive() == 1;&#xA;    }&#xA;}خب اسم کلاس رو گذاشتیم UserPrincipal ، چون یوزری که لاگین شده در سیستم میگن principal.یک سازنده داره که مدل یوزر رو میگیره. بعدم داره متدهای UserDetails رو پیاده میکنه که سخت ترینش هم همون متد اولیه‌س یعنی getAuthorities خب اونجا درواقع داریم از متدهایی که تو مدل یوز نوشتیم که لیست نقش‌ها و مجوزهارو میداد استفاده میکنیم و اونهارو تبدیل به یک لیست میکنیم چون سکیوریتی اونهارو تو یه لیست میخواد با این تفاوت که نقش‌ها حتما اولشون باید _ROLE قرار بگیرد. آبجکتی هم که داخل لیست داریم میذاریم باید از نوع  GrantedAuthority باشد. آخرشم لیستی که ساختیم رو برمیگردونیم و تامام. بقیه متد صرفا مپ کردن فیلدهای مدلمون با ویژگی‌های UserDetails‌ است. یسری از متدهارم بهشون مقدار ثابت دادیم و درواقع پیادشون نکردیم و بهشون نیازی نداشتیم که حالا شما بسته به کارتون ممکنه بکارتون بیاد.خب قسمت سختشون رد کردیم و اگه تا الان دووم آوردین میتونم بگم مطلبو تموم کردین چون واسط ‌ UserDetailsService چیز خاصی نداره. این واسط درواقع برای اینه که سکیوریتی بدونه چجوری با یوزرنیم کاربر رو پیدا کنه و ما قراره بهش بگیم و حالا میتونید حدس بزنید متد find که تو ریپوزیتوری نوشتیم به چه درد میخوره.پیاده سازی UserDetailsService به این شکل خواهد بود @Service&#xA;public class UserPrincipalDetailService implements UserDetailsService {&#xA;    @Autowired&#xA;  private UserRepo repo;&#xA;&#xA;    @Override&#xA;    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {&#xA;        User user = repo.findByUsername(s);&#xA;        if (user == null)&#xA;            throw new UsernameNotFoundException(s);&#xA;        return new UserPrincipal(user);&#xA;    }&#xA;}خب اینجا فقط اومدیم گفتیم اگه پیدا نکردی یه اکسپشن پرتاپ کن تا بتونیم ارور نشون کاربر بدیم که اشتباه وارد کرده ورودی هارو و اگر نه مدل یوزر رو پاس بده به کلاسی که ما نوشتیم تا  UserDetails رو پیاده کنیم و در نهایت اون رو به عنوان خروجی برگردون که به وسیله اطلاعاتی که ما توی کلاس UserPrincipal قرار دادیم میتونه پسورد و نقش و چیزای دیگه رو چک کنه و درصورت اوکی بودن کاربر رو لاگین کنه و در نهایت همین آبجکتی که ما اینجا ساختیم در ‌session به عنوان یوزر لاگین شده قرار میگیره.مرحله نهایی اینه که بریم توی کلاس کانفیگ سکیوریتی و بجای تعریف دستی کاربر بگیم که از سرویس ما استفاده کنه که اینجوری میشه :@Autowired&#xA;UserPrincipalDetailService detailService;&#xA;&#xA;@Override&#xA;protected void configure(AuthenticationManagerBuilder auth) throws Exception {&#xA;    auth.userDetailsService(detailService);&#xA;}دقت کنید که توی کدوم متد configure اینکارو کردم.و تمام حالا شما میتونید با دیتابیس یوزرهاتونو نگهدارید و البته با هرچیز دیگری و هر نوع دیتابیسی.سوالی بود اینجا یا توئیتر هستم مرسی از وقتتون. </description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Fri, 17 Apr 2020 19:58:13 +0430</pubDate></item><item><title>شروع سریع Spring Security (قسمت اول)</title><link>https://virgool.ir/@mihandoost.parsa/شروع-سریع-spring-security-قسمت-اول-iszbvc4fzoep</link><description>سلام دوستان طبق روال همیشه بدون مقدمه میرم سر اصل داستان. برای شروع آموزش باید Spring boot رو بلد باشین تا بتونین درست مراحل رو اجراکنید و به نتیجه برسین. خب همونجوری که احتمالا حدس میزنید سکیوریتی برای اعمال فرایندهای احراز هویت کاربر به هر طریقی که تاحالا دیدید از قبیل فرم‌لاگین ، توکن ، لاگین با شبکه‌های اجتماعی مختلف و ... . و همچنین authorize کردن کاربر یعنی مشخص کردن اینکه کدوم کاربر اجازه انجام چه کارهایی رو داره ایجاد شده.خب تصور کنید یه وب اپلیکیشن دارید که مثلا یه لیست کانتکت نشون میده (یا هرچیز دیگه‌ای) که میشه عملیات CRUD رو روش انجام داد.خب درحال حاضر هرکسی میتونه لیست رو تغییر بده و ما نیاز داریم که براش سیستم ادمین درست کنیم و بگیم آدمایی که Authenticate شدن (ما میشناسیمشون) بتونن لیست رو تغییر بدن و بقیه فقط بتونن تماشا کنن :).مرحله اول اضافه کردن سکیوریتی به پروژه‌س. من maven استفاده میکنم که کدش این شکلی میشه :&lt;dependency&gt;&#xA;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&#xA;    &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;&#xA;&lt;/dependency&gt;بدون اینکه کار اضافه‌تری انجام بدیم اگه یسر به اپمون بزنیم (درحالی که ریستارت کردیم سرور رو) میبنیم که یه فرم لاگین نشونمون میده :) .فقط مشکل اینجاست که همه مسیرهای اپمون به لاگین نیاز داره و محافظت میشه که ما اینو نمیخوایم و میخوایم لیستمون پابلیک باشه.یه نکته‌ای ام بگم که الان یه یوزر هم به اسم admin خودش ساخته و پسوردشم تو لاگتون بگردید میبینید (زیاد درگیرش نشید و ادامه رو بخونید بدرد بخور نیست زیاد)خب بریم سر اینکه چجوری میشه مسیرهایی که میخوایم آزاد باشند رو به سکیوریتی بگیم.برای اینکار نیاز دارید یه کلاس بسازید و کلاس‌ابسترکتِ WebSecurityConfigurerAdapter رو extend کنید.و از انوتیشن @EnableWebSecurity استفاده کنید. اینجا مرکز کارهاییه که با سکیوریتی میتونید انجام بدید و تقریبا هرکار بخواید بکنید یه سری به این کلاس باید بزنید و اگر این کلاس رو خوب بفهمید خیلی کمک کنندس. مشخصا در ابتدا سه تا متد خیلی کوتاه و ساده باید اینجا بنویسید دوتاش متد configure عه و یکیش  هم یک ‌‌‌‌Bean برای تعریف password encoder. پیاده سازی کلاس رو ببینیم که توضیح بدم روش:@EnableWebSecurity&#xA;public class Config extends WebSecurityConfigurerAdapter {&#xA;    @Override&#xA;    protected void configure(AuthenticationManagerBuilder auth) throws Exception {&#xA;        auth&#xA;                .inMemoryAuthentication()&#xA;                .withUser(&#34;admin&#34;)&#xA;               .password( passwordEncoder().encode(&#34;1234&#34;) )&#xA;               .roles(&#34;Admin&#34;)&#xA;              .authorities(&#34;ACCESS_CRUD&#34;);&#xA;    }&#xA;&#xA;    @Override&#xA;    protected void configure(HttpSecurity http) throws Exception {&#xA;        http&#xA;                .authorizeRequests()&#xA;                .antMatchers( &#34;/&#34;).permitAll()&#xA;                .anyRequest().authenticated()&#xA;                .and()&#xA;                .formLogin();&#xA;    }&#xA;&#xA;    @Bean&#xA;    public PasswordEncoder passwordEncoder() {&#xA;        return new BCryptPasswordEncoder();&#xA;    }&#xA;}خب در متد اول ما اومدیم یوزر تعریف کردیم که الان شبیه متغیر ذخیره میشه و دیتابیس خاصی نداریم ، کلا اون متد برای تعریف دیتاسورس هست ، میشه درواقع اونجا بهش بگیم یوزرهارو چجوری باید پیدا کنه (تو قسمت دوم میگم چجوری از دیتابیس بخونید) . اما الان تمرکز کنید رو متد دوم اونجا ما روی آبجکت ‌HttpSecurity میتونیم کلی چیزای جالب تعریف کنیم. مثلا اینجا با صدا زدن متد authorizeRequests بهش میگیم که میخوایم مشخص کنیم کدوم مسیرها آزادن و کدوم‌ها باید محافظت بشن که با متد  antMatchers اینکارو کردیمو مسیر روت(root) رو باز گذاشتیم و گفتیم بقیه مسیرها باید محافظت بشه (مسیرهای css و js  هم باید باز میذاشتم ولی بخاطر سادگی کد اینکار رو نکردم چیزیه که خودتون بهش میرسین و نیازی به گفتن نیست) . یه متد and زدیم که دوباره آبجکت  HttpSecurity رو بهمون میده و حالا براش رَوش احراز هویت رو مشخص کردیم و گفتیم از فرم لاگین‌ استفاده کن که الان از صفحه لاگین‌فرم دیفالتش استفاده میکنه (تو قسمت سوم میگم چجوری عوضش کنید خیلی سادس) . توی متد سوم هم اومدیم یه کلاس ‌encoder تعریف کردیم و برای پسورد ذخیره کردن ازش استفاده میکنیم (هم اینجا هم بعدا توی دیتابیس)حالا اپ ما داره درست کار میکنه و وقتی بریم داخلش  مسیر &#34;/&#34; آزاده و بدون لاگین قابل مشاهدس و مابقی مسیرها نیاز به لاگین داره. ناگفته پیداس که میتونه مسیرهای آزاد بیشتر باشه و قوانین بیشتری روش اعمال بشه. اگه کدهای بالارو تست کردید حتما حدس میزنید که ما نیاز داریم یکسری مسیر‌هارو گروهی آزاد بذاریم و تا هم ما کمتر خسته‌شیم هم  خواننده کد پس میتونیم تو antMatcher اینجوری‌هم بگیم.antMatchers( &#34;/free/test1&#34; , &#34;/free/test2&#34;  ).permitAll()&#xA;// OR&#xA;.antMatchers( &#34;/free/**&#34;  ).permitAll()اینجا قانونی که ما گذاشتیم فقط authenticate بودن بود اما باز ناگفته پیداس که سکیوریتی براساس role هم میتونه اجازه دسترسی بده که یعنی میتونه یه کاربر لاگین باشه اما به بخشهای دسترسی نداشته باشه و ۴۰۳ بگیره . ریز تر از اون براساس authority هم میتونه دسترسی بده. (authority به دسترسی‌های کوچک‌تر میگن معمولا یک ‌role یه دسته‌ای از authority هارا دارد و مثلا نقش Manager ممکنه اجازه EDIT_CONTACT را داشته باشد یا نداشته باشد ). برای مشخص کردن این قوانین هم کار سختی نداریم مثلا اینجا ما نقش Admin رو برای کاربرمون تعریف کردیم میتونیم اینجوری عمل کنیم :.anyRequest( &#34;/&#34; ).hasRole(&#34;Admin&#34;)&#xA;//OR &#xA;.anyRequest( &#34;/&#34; ).hasAuthority(&#34;ACCESS_CRUD&#34;)حواستون باشه دقیقا همون رشته‌ای که برای تعرف یوزر ازش استفاده کردید رو اینجاهم باید استفاده کنید که اشتباهی نشه.خب برای شروع کافیه تو قسمت بعدی میگم که چجوری از دیتابیس برای ذخیره کاربرها استفاده کنیم.امیدوارم مفید بوده باشه خوشحال میشم توی توئیترم بیشتر باهم معاشرت کنیم. </description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Wed, 15 Apr 2020 17:57:12 +0430</pubDate></item><item><title>یک نکته چالشی در ارجاع متد جاوا  (Method Reference)</title><link>https://virgool.ir/@mihandoost.parsa/یک-نکته-چالشی-در-ارجا-متد-جاوا-method-reference-vt5ydksokhqe</link><description>دیروز که داشتم اتفاقی با کالکشن‌های جاوا بازی‌بازی میکردم و متدهای جدیدشونو تمرین میکردم متوجه نکته‌ای تو Comparator  شدم که قبلا حواسم بهش نبود و یهو مغزم ارور داد. مشکل در ارجا متد توی Comparator بود. خب میدونستم که هرجا که ورودی و خروجی متدی که قراره پیاده کنیم با متدی که قبلا نوشتیم برابر بود میتونیم از Method Reference استفاده کنیم و متدی که وجود دارد رو به جای لامبدا یا کلاس بی‌نام استفاده کنیم. اما در سناریویی که الان میگم امضا یا همون signature متدی که   پاس میدیم با لامبدا معادل اون نمیخونه!!!سناریو سادس یک لیست مثل زیر داریم که با آبجکت های کلاس Person پر شدن. کلاس ‌Person تنها یک فیلد به نام name دارد همراه با getter و setter و یک سازنده که با دریافت name کار میکنه :List&lt;Person&gt; people = new LinkedList&lt;&gt;();&#xA;people.add(new Person(&#34;Bita&#34;));&#x9;&#xA;people.add(new Person(&#34;Parsa&#34;));&#xA;people.add(new Person(&#34;Ali&#34;));خب حالا میخوایم این لیست رو مرتب کنیم برای اینکار به راحتی میتونیم از متد جدید واسط Comparator استفاده کنیم که comparing باشه :people.sort( Comparator.comparing( Person::getName ) );دقیقا نکته اینجاست متد comparing یک function به عنوان ورودی میگیره و ما باید متد apply رو پیاده سازی کنیم که متدی است با یک ورودی. بیاید معادل لامبدا مثال بالا رو باهم ببینیم :(Person p) -&gt; p.getName();خب همینطور که میبینید ما یک ورودی داریم اما در عبارت ارجا که نوشتیم getName ورودی نداره!!!پس چجوری match میشه؟جواب اینه که وقتی جاوا میبینه که نوع آبجکتی که در ورودی هست با کلاسی که شما دارین بهش رفرنس میدید (Person) یکیه و متد به اون آبجکت نیاز داره برای اجرا خودش اونو پاس میده و امضا متد رو درست در نظر میگیره.یعنی عبارت Person::getName همون ()p.getName ترجمه میشه. اما مثلا اگر بنویسیم OtherClass::getName ارور میگیریم چون نوع ورودی ما Person است و با OtherClass فرق میکنه پس match نمیشه.عنوان این نوع ارجا اگر خواستید سرچ کنید اینه :Reference to an instance method of an arbitrary object of a particular typeدر عنوان بالا arbitrary object منظور همون ورودیه که در لامبدا هست اینجا (Person p)امیدوارم کمک کرده باشم خوشحال میشم توی توئیترم بیشتر معاشرت کنیم تا بعد :)</description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Sat, 04 Apr 2020 01:09:56 +0430</pubDate></item><item><title>داکرایز کردن پروژه Spring Boot بهمراه MongoDB</title><link>https://virgool.ir/@mihandoost.parsa/داکرایز-کرده-پروژه-spring-boot-بهمراه-mongodb-detdqfwpgti2</link><description>خب میخوام براحت‌ترین شکلی که پیدا کردم بگم چجوری میتونید پروژه اسپرینگ‌بوت خودتون رو که دیتابیسش هم منگو هست رو تبدیل به داکر بکنید و براحتی هرجا خواستید اونو deploy کنید. من این کار رو با docker-compose انجام میدم که نصبش تو لینوکس خیلی راحته و کافیه اونو تایپش کنید که اگه نبود خودش نصبش کنه. اول از همه بریم سراِِغ پروژه اسپرینگمون که باید داخل یک کانتینر جدا بیاد بالا. برای اینکار باید jar پروژه رو تولید کنید.برای سادگی و فهم بهتر از همین اول هر فایلی که میسازیم رو بذاریم توی پوشه lab (هرجا خواستید پوشه رو بسازید فرق نداره) . پس jar تولید شده رو بذارید توی پوشه lab. اگر پروژتون maven هست jar اینجوری تولید میشه: mvn clean package  دستور بالا رو باید توی root پروژه بزنید و فایل تولیدی هم که توی target/yourApp.jar قرار میگیره که کپیش کنید داخل پوشه lab.حالا یک فایل به نام Dockerfile داخل پوشه lab و بدون پسوند و پیشوند بسازید با محتویات زیرFROM openjdk:11&#xA;&#xA;COPY yourApp.jar app.jar&#xA;&#xA;ENTRYPOINT java -jar app.jarساده‌ترین حالت یک داکرفایل رو دارید میبینید که در عین‌حال کار بزرگی انجام میده. خط اول میگه از چه image استفاده میکنه برای اجرای اسپرینگمون که براساس ورژن جاوا خودتون تنظیمش کنید. خط دوم فایل jar رو داخل image کپی میکنه  و در خط سوم میگه چه دستوری باید بعد از اجرا انجام بشه که بهش گفتیم فایل jar رو اجرا کنه. حالا ما موفق شدیم وب اپلیکیشنمون رو داکرایز کنیم اما دیتابیس نداره و حتما به مشکل میخوره و باید یه داکر دیگه بیاریم بالا برای منگو که این یکی داکرمون یا همون containerمون ازش استفاده کنه. درواقع هربار که بخوایم داکر اپلیکیشنمون رو بیاریم بالا به یک داکر منگو هم نیاز داریم پس از docker-compose استفاده میکنیم. docker-compose این قابلیت رو بهمون میده که تو یه فایل بگیم که چه داکرهایی لازم داریم که باید باهم اجربشن و اپ مارو بیارن بالا.پس یه فایل دیگه توی پوشه lab به اسم  docker-compose.yml بسازید و توش اینارو بزاریدversion: &#34;3&#34;&#xA;services:&#xA;  web:&#xA;    build: .&#xA;    ports:&#xA;      - &#34;8080:8080&#34;&#xA;    environment:&#xA;      - SPRING_DATA_MONGODB_URI=mongodb://mongo:27017/your-database-name&#xA;    depends_on:&#xA;      - mongo&#xA;  mongo:&#xA;    image: mongo&#xA;    ports:&#xA;      - &#34;27017:27017&#34;خب تو این فایل و توی تگ services مگیم که چه سرویس‌هایی داریم اینجا گفتیم دوتا سرویس به نام های web و mongo داریم. سرویس web باید build بشه یعنی image آماده‌ای نداره و باید از فایل Dockerfile ما بخونه برای همین &#34;.&#34; گذاشتیم که مشخص میکنه توی همین پوشه باید دنبالش بگرده که بسازتش. توی سرویس mongo اما گفتیم از image رسمی خود منگو استفاده بشه که اسمش همون mongo هست.نکته‌ای که باید بدونید اینه که از طریق environment میتونید همه‌ی ویژگی‌های فایل ‌application.properties رو override کنید. مثلا اینجا با SPRING_DATA_MONGODB_URI ( اگه آندرلاین رو با دات عوض کنید میبینید همون اسمیه که تو application.properties استفاده میکنید )  بهش گفتیم کجا میتونه به منگو وصل بشه و حواستون باشه که توی این رشته mongo:27017  منگویی که نوشتیم اسم سرویس منگو توی فایل docker-compose است. در آخر هم دوتا کامند میزنید و سیستم آماده استفادس که به ترتیب :docker-compose build با این دستور داکر اپتون رو که تو یه فایل توصیفش کردیم رو میسازه و بعد این دستور :docker-compose upو سیستم در اختیار شماس :)ممنون از وقتی که گذاشتید امیدوارم کمک کرده باشم. خوشحال میشم توی توئیترم بیشتر باهم معاشرت کنیم.</description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Sat, 28 Mar 2020 03:59:57 +0430</pubDate></item><item><title>چگونه از یک آبجکت کپی بگیریم؟؟!</title><link>https://virgool.ir/@mihandoost.parsa/چگونه-از-یک-آبجکت-کپی-بگیریم-s3rndvtz4uzv</link><description>این مطلب براساس زبان جاوا نوشته شده اما احتمالا اشتراکاتی با زبانی که شما کارمیکنید هم دارد.موضوع آنقدر پیچیده نیست اما ممکن است اگر دو مفهوم اصلی را ندونید کمی گیچ کننده باشد.مسئله سادست ما آبجکتی مثل زیر داریم :Person a=new Person();&#xA;a.name=&#34;parsa&#34;&#xA;a.age=&#34;22&#34;(کلاس person هیچ چیز جز همین دوتا فیلد نداره و برای سادگی از getter و setter استفاده نشده). حالا میخواهیم از این آبجکت کپی بگیریم. اولین راهی که به ذهن ما میرسد ساختن یک متغیر دیگر و مساوی قرار دادنش با متغیر a است به این شکل :Person b= a;به این روش کپی   shallow copy  میگونید یعنی کپی کم‌عمق. در این روش کپی انجام میشود اما مشکل اینجاست که اگر با مفهوم Reference آشنا باشید میدونید که متغیر a حاوی خود آبجکت نیست بلکه حاوی اشاره‌گری‌ایست که به آبجکت a اشاره میکند. و الان اون اشاره‌گر رو ریخته تو b بنابراین اگر بگیم :b.name= &#34;Ali&#34;آبجکت a هم فیلد اسمش برابر با ali میشه که در اکثر موارد ما نمیخوایم اینجوری بشه . برای این که اینجوری نشه باید بیایم دونه دونه ویژگی‌های آبجکت a رو برابر با آبجکت b قرار بدیم :b.name = a.name;&#xA;b.age = a.age`به این روش که دیگه مشکل قبلیو نداره و دوتا آبجکت با مقادیر مساوی اما سوای از هم میسازه deep copy میگن که اکثر ما به این نیاز داریم.خب خیلی وقتا کلاس ما به سادگی کلاس Person نیست و کپی کردنش ممکنه حوصله سربر و دردسرساز باشه. برای همین توی جاوا میشه از فیچر cloneAble استفاده کرد. یکم فیچر گیج کننده‌ایه پس به این بخش حرفم خوب توجه کنید : در کلاس Object که والد همه کلاس‌ها در جاوا هست و همه متدهای اونو ارث میبرند متدی وجود داره به اسم clone که میشه باهاشم آبجکتتون رو کپی کنید اونم به روش deep اما نکته اینه که این متد protect است. بنابراین باید اونو داخل کلاس خودتون در یک متد صدا بزنید و پس از اون از بیرون کلاس قابل استفادس. مثال تو همین کلاس Person :public class Person {&#xA;      public Person clone() throws CloneNotSupportedException{&#xA;        return (Person) super.clone(); &#xA;     }&#xA;}خب اون cast بخاطر اینه که clone داخل کلاس Object مقدار برگشتیش همون Object است.حالا میتونید این متد رو از بیرون کلاس صدا بزنید :try{&#xA;Person b = a.clone();&#xA;}catch(CloneNotSupportedException e){&#xA;     // Handle exp&#xA;}اما این کد خطا خواهد داشت !!!متد clone یک متد امنیتی محسوب میشه و شما صراحتا باید به کلاستون اجازه این کار رو بدید. اون اکسپشن هم دقیقا برای همینه که اگه کلاس شما اجازه اینکار رو نداشت پرتاب (throw) بشه و اجازه ادامه نده.اما چگونه دسترسی رو بدیم؟کافیه اینترفیس Cloneable رو implement کنیم این واسط یک marker هست و هیچ متدی واسه پیاده سازی نداره و تنها به JVM میگه که این کلاس میتونه super.clone رو صدا بزنهpublic class Person  implements Cloneable{     &#xA;  public Person clone() throws CloneNotSupportedException{&#xA;         return (Person) super.clone();&#xA;       }&#xA; }حالا شما میتونید آبجکت خودتونو براحتی clone کنید. در پست بعدی به بدی‌های روش clone اشاره میکنم و پیشنهاد جایگزین خودم رو بهتون میدم.مرسی که وقت گذاشتید امیدوارم تونسته باشم کمکتون کنم. خوشحال میشم توی صفحه توئیترم بیشتر باهم معاشرت کنیم.</description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Wed, 25 Mar 2020 16:15:32 +0430</pubDate></item><item><title>یک شروع خیلی سریع - Thymeleaf</title><link>https://virgool.ir/@mihandoost.parsa/یک-شروع-خیلی-سریع-thymeleaf-nduygqk9txw1</link><description>بدون مقدمه بریم سر اصل مطلب. برای استفاده از تایم‌لیف در spring boot کافیه وابستگی اون رو داخل بیلدسیستم خودتونون چه maven و چه gradle قرار بدید. اما اصلا چرا باید اینکار رو بکنید :/ تایم‌لیف چه ویژگی داره که بتونه شمارو قانع به اینکار بکنه :تایم‌لیف برای دفاع از خودش اول به شما میگه که من یک Natural Template هستم. اما این یعنی چی؟تصور کنید در یک سناریو معمولی و متداول به ما یک صفحه ‌Html دادن مثل این صفحه و به ما گفته شده اون رو داینامیک کنیم و اطلاعات صفحه رو از دیتابیس بخونیم. خب اگر حواسمون نباشه (که دیگه معمولا هست ولی حالا فرض محال) و بدون اینکه پسوند فایل رو به JSP تغییر بدیم شروع به کد زدن کنیم نتیجه شبیه تصویر پایین میشه اما در ‌Thymeleaf  نیازی به این کار نیست و پسوند فایل همون ‌html هست که از فرانت کارتون تحویل گرفتید. شاید بنظرتون این ویژگی پیش‌پا افتاده بیاد ولی باید بگم داستان به اینجا ختم نمیشه!یکی از رسالت‌های زیبای این ‌ Template engine اینه که قراره تعامل شما با فرانت کارتون رو بهبود ببخشه.یکی از راه‌های بهتر کردن رابطه دونفر که باهم تقابل دارن همیشه کاهش ارتباطشون و جلوگیری از رودرویی اونهاست :) (چه شخصی چه کاری ! )  بنابراین تایم‌لیف قراره جلوی تقابل شما با فرانت‌کار رو بگیره به این شکل که شما مطلقا و تاکید میکنم مطلقا نیازی به دست بردن در ساختار کدهای Html ندارید!!!خب پس چجوری اون رو داینامیک میکنیم؟جواب اینه که با Attribute  هایی که تایم‌لیف در اختیار ما گذاشته و با این کار دیگه به ‌Body تگ‌ها دست نمیزنیم و مقادیر مثالی‌ای که فرانت کار گذاشته‌هم میذاریم باشه و نیازی به حذفشون نیست که جلوتر میگم چرا.بیاید یه مثال بزنیم مثلا یک تگ ساده در نظربگیرید :&lt;p&gt;Hello World!!!&lt;/p&gt;حالا برای داینامیک کردن اون کافیه بگیم :&lt;p th:text = &#34;${varName}&#34; &gt; Hello World!!! &lt;/p&gt;در مثال بالا varName اسم attribute‌ای هست  که احتمالا در کنترلر تعریف کردید اگر پیدا نشه به هر دلیلی مقدار پیش فرض نشون داده میشه. نکته اینه که مقدار پیش‌فرضی که از طرف بک‌اند درنظر دارید هم باید در ‌th:text قراربگیره و body تگ قلمرو فرانت‌اند کار باقی میمونه :).تایم‌لیف به ازای هر یک از attribute های معمولی html یک معادل با پسوند th دارد که بشه اون رو داینامیک کردمثال&lt;a href=&#34;#&#34; th:href=&#34;@{\folan}&#34;&gt;test&lt;/a&gt;اما نگران نباشید راه دررو هم داره که البته پیشنهاد نمیشه اما میدونیم همیشه ما وقت رعایت اصول رو نداریم مثلا برای نمایش متغییرها هرکجای صفحه میتونید اینجوری عمل کنید[[ ${varName} ]]چیزی که میخواستم جلوتر بگم اینه که اگر اصول رو رعایت کنید اتفاق جذابی که میوفته اینه که وقتی Html هارو بصورت offline و بدون وب‌سرور هم که باز کنید کاراکترهای عجیب نمیبینید و حتی ممکنه اونها رو بدرستی و با همون استایل‌ها ببینید که البته احتمالش کمه اما ویژگی‌‌های اجرای ‌‌offline وجود داره و با یکم وقت گذاشتن کاملا میتونید اینکار رو بکیند.اگر تا اینجا قانع شدید که اون رو به پروژه اضافه کنید باید بگم بعد از اضافه کردن Html های خودتون رو در مسیر/resources/templatesبذارید و اونهارو فقط با نام و بدون پسوند از کنترلر خودتون صدابزنید.دوتا دستور کاربردی دیگه میگم و ختم جلسه :شرط گذاشتن برای نمایش یک تگ :&lt;div th:if=&#34;${varBoolean}&#34;&gt;&#xA;   &lt;div&gt;Welcome, Guest&lt;/div&gt;&#xA;&lt;/div&gt;بجای varBoolean همونجور که حدس میزنید میشه شرط هم گذاشت .ایجاد حلقه برای تکرار تگ‌ها :&lt;div th:each = &#34;contact : ${contacts} &#34;&gt;&#xA;     &lt;h3 th:text= &#34; ${contact.name} &#34; &gt;&lt;/h3&gt;&#xA;&lt;/div&gt;در آخر بگم که برای بهتر شدن پیشنهادهایی که ‌IDE میخواد بده و درکل شناختن ویژگی های ‌th باید برای تگ html صفحه‌ای که روش کار میکند namespace  تعریف کنید که بدونه این th ها چی هستن و از کجا میان. این کار مطلقا برای تفسیر template مشکلی درست نخواهد کرد اما برای شما هنگام توسعه ممکنه مشکل ساز باشه. تعریف namespace به همین راحتی انجام میشه:&lt;html  xmlns:th = &#34;http://www.thymeleaf.org&#34;&gt;امیدوارم که تونسته باشم بهتون کمک کنم خوشحال میشم توی صفحه توئیترم باهم بیشتر معاشرت کنیم ممنون که وقت گذاشتید  </description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Mon, 23 Mar 2020 02:04:12 +0430</pubDate></item><item><title>نقدی بر این عبارت که هر زبان برای یک کار مناسب است</title><link>https://virgool.ir/@mihandoost.parsa/نقدی-بر-این-عبارت-که-هر-زبان-برای-یک-کار-مناسب-است-o6kxnai3xjti</link><description>همین اول بگم که من مهارتی تو زمینه نوشتار ندارم و فقط میخوام چالشی که باهاش مواجه ام رو بهتون نتیجش رو بگم.یکی از نقص هایی که محیط های آکادمیک دارند که البته از دید من نقصه این هست که اونا همیشه بهترین راه و بهترین کار رو به شما پیشنهاد میدن (در زمینه فنی و در بهترین شرایط منظورمه). و خب این وسط این که شما چه شرایطی داری یا جامعه یا اساسا واقعیت گفته نمیشود. خب همه ماهایی که برنامه نویسی کردیم و یکم پیش رفتیم حتما در جواب به این سوال که کدوم زبان خوب هست این رو شنیدیم که هر زبان در یک زمینه خاص از بقیه برتری هایی دارد و اگر شما مفاهیم پایه را بلد باشید سوئیچ کردن بین زبان ها زیاد دشواری نداره اما!تو دنیای واقعی زیاد این حرف عملی بنظر نمیرسد وقتی شما کسانی رو میبینید که سالها در یک زبان خاص کار میکنند و اظهار میکنند که هنوز هم همه چیز را بلد نیستن چجوری تغییر زبان انقد باید ساده باشد.بنظر من این حرف بیشتر برای کارفرما کاربرد دارد که واقعا هم درست است وی خرج میکند برای کارخاص بهترین زبان را انتخاب کرده و متخصص مربوطه را نیز به خدمت درمیاورد خیلی هم شیک.ولی در این سمت اون متخصص میتواند خود را به همین شیکی با نیاز خاص وقف دهد؟جواب بله اما هزینه دارد و هزینه کمی هم نیست.موضوع فقط شرط و حلقه و کلاس و بطور کلی سینتکس زبان نیست!مثلا خود بنده درحال حاضر درحال تغییر فیلد کاری از PHP به جاوا هستم جفتشون سی بیس هستند و تقریبا هفتاد درصد سینتکس مشابه است اما در کمال نا باوری با وجود زمانی نزدیک به شش ماه هنوز تسلطی که روی PHP دارم روی جاوا ندارم از خنگی من که بگذریم. موضوع ابزار است امتحان کنید و بطور مثال یک هفته در IDE جدید کدبزنید یا ORM یا ... . تازه خبر بدتر این است که شما نمیتوانید معادل سازی کنید و مثلا بگردید ببینید تو جاوا مایگریشن چجوری انجام میشه از کجا معلوم که جاوا راه بهتری بجای مایگریشن نداشته باشد و این موضوع در ده ها ابزاری که شما باید یادبگیرید صدق میکند.حتما که شما از کسی که برنامه نویسی بلد نیست بسیار بسیار جلوتر هستید ولی تغییر زبان و حرفه ای شدن در اون انقدر هاهم آسان نیست حتی اگر درزبان دیگری حرفه ای باشید.والسلام و علیم و رحمت الله و برکاتووو.</description><category>Parsa Mihandoost</category><author>Parsa Mihandoost</author><pubDate>Tue, 18 Dec 2018 20:17:32 +0330</pubDate></item></channel></rss>