<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های مهدی معروف به سقراط مناطق محروم :)</title>
        <link>https://virgool.io/feed/@m_11008415</link>
        <description>سوال های احمقانه ایست مرا که جوابی بر آن نیست، پس بنواز ای ساز گیتی تا ز آن خرد آموزم</description>
        <language>fa</language>
        <pubDate>2026-04-15 04:45:56</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/383625/avatar/j6zUWW.jpg?height=120&amp;width=120</url>
            <title>مهدی معروف به سقراط مناطق محروم :)</title>
            <link>https://virgool.io/@m_11008415</link>
        </image>

                    <item>
                <title>Would, Could, Should</title>
                <link>https://virgool.io/@m_11008415/would-could-should-vp8eydrjfaad</link>
                <description>اول از همه میریم سراغ Would اگه بخوایم درباره یه چیز خیالی صحبت کنیم که اتفاق نیافتاده از این ساختار استفاده میکنیماگه بخوایم درباره اتفاقی که احتمال رخداد اون ها توی گذشته بوده حرف بزنیمرخداد های همین الان (مثلا درخواست غذا)نمونه های معروفش این ها هستند:if were ... I would (I&#x27;d)if he were ... he would (he&#x27;d)توی این داریم میگیم اگه فلان چیز میشد چی میییشد! مثلا میگیم if were a doctor I would working on hospital اما الان من دکتر نیستم دارم درباره احتمال خیالی حرف میزنم، و این احتمال یکم بلند پروازانه و با اعتماد به نفس هم هست.if I had ... I would have (would&#x27;ve)if she had ... she would have مثلا میگم If I had woken up earlier I would have been on time foe school که میگم اگه زود تر بیدار میشدم به موقع به مدرسه میرفتم.برای منفی کردن هم خیلی ساده میشه = would not have یه موقع دیگه ای که از would استفاده میکنیم موقعی هست که میخوایم درباره آینده توی گذشته حرف بزنیم.مثلا اینجوری: وقتی بچه بودم فکر میکردم که دکتر میشم؛when I was a kid, I thought I would have a doctor.میتونیم از would یه جای دیگه هم استفاده کنیم، بجای نفر دیگه که گفته will میتونیم بگیم wouldمثلا: علی گفت پول کافه رو حساب میکنم. حالا من دارم یه داستان تعریف میکنم که علی گفته پول کافه رو میخواد حساب کنه، میگم:Ali said he would pay for coffee. اما علی چی گفت؟ Ali: I will pay for the coffee.توجه1: برای درخواست های رسمی میتونیم استفاده کنیم.
مثلا: 
?would you like some more coffee
توجه2: میتونیم برای کارهایی که قبلا به صورت معمولی انجام میدادیم هم استفاده کنیم. (شبیه used to)
مثلا:
when I was kid I would eat ice cream every day.حالا میریم به سمت و سوی Could وقتی میخوایم همین الان درباره چیزی حرف بزنیم که احتمالش وجود داره  چیزی الان درست نیستاحتمالاتی که توی گذشته وجود داشتوقتی میخوایم یه احتمال وقوع یه چیز در گذشته رو روشن و واضح کنیم وقتی میخوایم یه احتمال وقوع ندادن یه چیز در گذشته رو روشن و واضح کنیمفرقش با would همینه، احتمالش هست، مثل اون رویا گونه و با اعتماد به نفس نیست. مثلا &quot;ما میتونیم با هم یه فیلم ببینیم.&quot; خوب دیدن فیلم احتمال وقوعش هست. و این جا دیگه نمیتونیم بگیم would.We could see a movie today.You could try a new recipe. یه موقع دیگه ای که میتونیم از could استفاده کنیم، برای اتفاقی هست که احتمالش توی گذشته ممکن بوده. مثلا: اگه درس میخوندم توی امتحان قبول میشدم.If I had studied harder I could pass the test.میتونستیم این جا از would هم استفاده کنیم اما اگه از would استفاده میکردیم انگار داریم با اعتماد به نفس بیشتری حرف میزنیم اما وقتی میگیم could میگیم احتمالش بود که قبول بشم. برای مورد سوم و چهارم هم الان مثال میزنم: مثلا میگم، وقتی بچه بودم میتونستم اسپانیایی حرف بزنم. when I was little, I could speak Spanish.و اگه هم میخوایم درباره عدم احتمال یه چیز حرف بزنیم درواقع چیزی که 100٪ امکان نداره اتفاق بیوفته میتونیم میتونیم از could not استفاده کنیم. مثلا: این نمیتونه آدرس درست باشه (یعنی صد در صد غلط غلوطه غلطه) This could not be the correct address.توجه1: وقتی استفاده میکنیم که بخوایم یه درخواستی رو بخوایمتوجه2: وقتی میخوایم اجازه یه کاری رو هم بگیریم میتونیم از این ساختار استفاده کنیم.و در نهایت Shouldبرای موقعی که میخوایم دیگران رو توصیه به کاری کنیموقتی میخوایم درباره کار مهمی که توی گذشته باید انجام میدادیم حرف بزنیم (معمولا با پشیمانی)You should find a new apartment.I should have studied more.امیدوارم لذت برده باشید.میخوام چالش 10 روز انگلیسی برای خودم بذارم و این جا مینویسم و بعدش توی جا های مختلف لینک میدم امیدوارم کلی چیز جدید در کنار هم یاد بگیریم.چالش 10 روز زبان - روز اول  </description>
                <category>مهدی معروف به سقراط مناطق محروم :)</category>
                <author>مهدی معروف به سقراط مناطق محروم :)</author>
                <pubDate>Wed, 26 Feb 2025 17:16:51 +0330</pubDate>
            </item>
                    <item>
                <title>نحوه پیاده سازی view binding توی DuckDuckGo چجوریه؟</title>
                <link>https://virgool.io/@m_11008415/%D9%86%D8%AD%D9%88%D9%87-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-view-binding-%D8%AA%D9%88%DB%8C-duckduckgo-%DA%86%D8%AC%D9%88%D8%B1%DB%8C%D9%87-fjsomh8qfams</link>
                <description>داشتم کد DuckDuckGo رو نگاه میکردم که دیدم نحوه پیاده سازی view binding جالبی داره و گفتم بیام این جا یه دل سیر دربارش بنویسم! https://github.com/duckduckgo/Android/tree/develop/common/common-ui/src/main/java/com/duckduckgo/common/ui/viewbinding توی این لینک میتونید کل کد های view binding رو ببینید.اول از همه چیزی که برام جالب بود این قطعه کد بود.private val binding: ActivityAboutDuckDuckGoBinding by viewBinding()که شبیه فرم نرمالی که ما استفاده میکنیم تا اکتیویتی ها رو bind کنیم نبود. (شاید بپرسید شکل نرمال چیه)این شکلی هست:private lateinit var binding: ResultProfileBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ResultProfileBinding.inflate(layoutInflater)
    val view = binding.root
    setContentView(view)
}عینا کد های موجود در داکیومنت رو این جا ببینید.همون طور که میبینید، هم کد های کمتری نوشته شده و از بازنویسی کد اجتناب کرده، وقتی شما اکتیویتی های زیادی دارید حتی همین کار های ساده هم برای شما وقت گیر هستند و بهتر هست از روشی استفاده کنیم که ساده تر بتونیم برنامه نویسی کنیم. و خودمون رو درگیر این ها نکنیم.حالا بریم سراغ این که چجوری میتونیم این کار رو بکنیم، inline fun &lt;reified T : ViewBinding&gt; AppCompatActivity.viewBinding() =
    ActivityViewBindingDelegate(T::class.java, this)توی این جا ما کلمه کلیدی reified رو داریم (تو کامنت ها بنویسید آیا ازش استفاده کرده بودیدش یا نه، من که شخصا نه) این برای این هست که حتما جنریک (یعنی هر کلاس دلخواه T) باید از ViewBinding ارث بری کرده باشه، اون موقع میتونیم کلاس ActivityViewBindingDelegate رو اجرا کنیم. اما چرا inline ؟چون حتما وقتی میخوایم از reified استفاده کنیم باید حتما تابع ما inline باشه، اگه نباشه برنامه نمیتونه بفهمه که T از نوع ViewBinding هست. چون در حالت معمولی T فقط در زمان کامپایل وجود داره اما با خطی کردن اون با inline حالا ما در زمان اجرا هم به نوع T دسترسی داریم. (امیدوارم رسونده باشم و گیج نشده باشید.)معنی لغوی reify (که ریشه همین reified هست رو اینجا ببینید)حالا بریم سراغ همین کلاس عزیز و دلبند :)پانوشت:‌گالوم به اون حلقه میگفت عزیزم :)class ActivityViewBindingDelegate&lt;T : ViewBinding&gt;(    bindingClass: Class&lt;T&gt;,    val activity: AppCompatActivity,) : ReadOnlyProperty&lt;AppCompatActivity, T&gt; اول از همه کلاس اینجوری تعریف میشه، همون اولش میگه که T از نوع ViewBinding هست. و متغیر bindingClass بهش اشاری میکنه. و همون طور که activity که از نوع AppCompatActivity هست. (این همونی هست که تقریبا همه اکتیویتی ها ازش ارث بری میکنند.) و درنهایت از interface ی به نام ReadOnlyProperty رو implement میکنه. راستش خودم خیلی دقیق متوجه نشدم دقیقا این چیکار میکنه اما خلاصه اش این هست که با استفاده از این میتونید کلاس ViewBinding رو به طرز دلخواه خودتون برگردونید، یعنی میتونید layout inflater در این جا بهش بدیم و درگیر اضافه کردن اون در اکتیوتی نشیم.مزایای استفاده از ReadOnlyPropertyپیاده سازی بصورت lazy انجام میشه و هر وقت لازم بود نمونه گیری انجام میشهتضمین میکنه که مقدار همیشه ثابت هست و دوباره نمونه گیری نمیکنه و فقط همون یکبار نمونه میگیرهکد مون تمیز تر و قشنگ تر میشهدر ادامه کد ها :private var binding: T? = null
private val bindMethod = bindingClass.getMethod(&amp;quotinflate&amp;quot, LayoutInflater::class.java)یه متغیر binding و متغیر bindMethod  رو تعریف میکنه، از bindClass که نسخه ای از AppCompatActivity هست استفاده میکنه و متد(تابع) به اسم inflate رو بگیره و برای پارامتر های ورودی تابع LayoutInflater رو بده.در قسمت بعدی میام و تابع getValue رو که تنها تابع interface مون یعنی ReadOnlyProperty هست رو override میکنیم: override fun getValue(
    thisRef: AppCompatActivity,
    property: KProperty&lt;*&gt;,
): T {توش:اولش چک میکنیم که اگه binding مون وجود داشت و null نبود برامون return اش کنه:binding?.let {
    return it
}بعدش هم اگه null نبود باید بسازیمش binding رو:val lifecycle = thisRef.lifecycle
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
    error(&amp;quotCannot access viewBinding activity lifecycle is ${lifecycle.currentState}&amp;quot)
}

binding = bindMethod.invoke(null, thisRef.layoutInflater).cast&lt;T&gt;()

return binding!!اولش چک میکنه که اگه حالت اکتیویتی مون از شروع شدن گذشته بود برامون ارور بنویسه که نمیتونه به چرخه حیات اکتیویتی دسترسی پیدا کنه. و در نهایت هم binding رو برامون میسازه.اگه نمدونید invoke چیه، باید بگم میشه باهاش میتونیم نمونه یا همون instanceی که از یه کلاس میسازیم رو مثل تابع استفاده کنیم، یعنی برای همون binding که بالا تعریفش کردیم الان میتونیم layoutinflater یی که دقیقا مال همین AppCompatActivity هست رو بهش بدیم (واقعا نبوغ انگیزه، سادست ولی باحاله، میدونید ترکیبش جالبه)و درنهایت میاد و cast رو تعریف میکنه (اگه دقت کرده باشید binding  آخرش باید به همون کلاس T تبدیل بشه)@Suppress(&amp;quotUNCHECKED_CAST&amp;quot)
private fun &lt;T&gt; Any.cast(): T = this as Tو این طوری ما میتونیم بدون دردسر ویو هامون رو bind کنیم.امیدوارم لذت برده باشید. خودمم همینجوری داشتم سرچ میکردم در کنارش و یاد میگرفتم، میدونید خوبی نوشتن این هست که باید اولش چیز ها رو خودت یاد بگیری و بتونی توضیحش بدی، واقعا روش خوبیه، یعنی برای من جواب داده، نوشتن متن طول میکشه، اما میدونم که اگه خودم میخواستم بشینم و فقط یه فایل مو به مو بررسی کنم حوصلم سر میرفت :)برای فرگمنتش هم تقریبا شبیه همین هست. میتونید خودتون ببینید.اگه جایی اشتباه کردم یا توضیحاتم ناقص بوده از بلد نبودن من هست. و خوشحال میشم که توی کامنت ها به یادبدید :)مخلصیم</description>
                <category>مهدی معروف به سقراط مناطق محروم :)</category>
                <author>مهدی معروف به سقراط مناطق محروم :)</author>
                <pubDate>Sat, 20 Jul 2024 20:35:58 +0330</pubDate>
            </item>
                    <item>
                <title>خوندن کد های code lab اندروید | Dagger 2</title>
                <link>https://virgool.io/codenevis/%D8%AE%D9%88%D9%86%D8%AF%D9%86-%DA%A9%D8%AF-%D9%87%D8%A7%DB%8C-code-lab-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-dagger-2-pubaer2p9x5w</link>
                <description>نوشته که بدون عکس نمیشه!من قبلا با کتاب خونه koin کار کردم اما بنا به دلایلی متوجه شدم که کتاب خونه Dagger بهتر و پر‌استفاده تر هست در شرکت های بزرگ پس گفتم بیام و درکنار یادگرفتن dagger بیام و این متن رو هم بنویسم.تا اون جایی که تونستم کلمات رو مهم رو ترجمه نکردم، یا اگر هم ترجمه کردم لینک دیکشنری و این ها رو گذاشتم. سعی کردم خیلی خودمونی و ساده بنویسم، اولش میخواستم بیشتر خلاصه باشه تا ترجمه، ولی فصل ها طوری شدند که بیشتر شبیه به ترجمه شده.یه نکته قبل از شروع: بعضی جا ها از کلمه &quot;جنس&quot; یا &quot;نوع&quot; استفاده کردم. این همون DataType ها هستن. مثلا int یا هر چی.یادتون نره از خوندن و یادگیری لذت ببرید، متن یک کم طولانی هست، میدونم، فصل فصل جداشون کردم تا راحت باشید. بریم تو کارلینک مقاله اصلی: https://developer.android.com/codelabs/android-dagger#0 چون دو فصل اول مفاهیم و مقدمات بود اون هارو خلاصه کردم.شما هم میتونید با من پروژه رو باز کنید و انجام بدیددانلود پروژه اولیه (این پروژه خام هست و شما توش dagger رو پیاده سازی میکنید)یا میتونیداز گیت هاب clone کنید:$   git clone -b solution https://github.com/android/codelab-android-daggerخوب من دوست داشتم به صورت پروژه محور و بسیار خلاصه مفاهیم رو متوجه بشم، همیشه دوست دارم چیز های سخت رو آسون متوجه بشم. پس رفتم سراغ سایت اصلی!!راستی این سایت تحریم هست، و با انواع روش های تحریم شکن برین توش، پیشنهاد: سایت شکن و سایت ۴۰۳همون طور که توی لینکی که گذاشتم میتونید ببینید، ۱۶ تا فصل داره، که اولین مقدمه هست، و دومیش هم برای clone کردن از Github هست پس من از فصل ۳ میخوام شروع کنم.فصل ۳ | اجرا کردن برنامهخوب، میگه برنامه ۴ تا Activity داره:احراز هویتورودخانهتنظیماتاین برنامه از معماری MVVM استفاده میکنه.شمای کلی از پروژه ای که میخوایم روش کار کنیمتوجه: اگه با معماری MVVM آشنایی ندارید، اول اون رو مطالعه کنید: نمونه ای با زبان کاتلین، نمونه ای با زبان جاوا ، داکیومنت اصلیدر عکس بالا پیکان ها بیانگر وابستگی‌ها هستند.گراف برنامه (Application Graph): تمامی کلاس هایی بین اون ها وابستگی (Dependency) وجود دارهما بجای این که خودمون به صورت دستی بیایم و وابستگی ها رو ایجاد کنیم باید این کار ها رو به Dagger محول کنیم. (تقریبا تمام ایده پشت Dagger همینه)چرا Dagger؟[این قسمت رو خودم میگم] ببینید، کلا مفهوم تزریق وابستگی واسه این هست که ما توی پروژه داریم کار میکنیم و همین جوری پیش میریم، اما بعد مدتی بنا به هر دلیلی دیگه نمیخوایم از اون وابستگی ها استفاده کنیم. مثال بزنم (انیشتین میگه: چیزی رو نمیشه فهمید جز با مثال): مثلا شما برای احراز هویت کاربرانتون در حال حاضر از ایمیل استفاده میکنید اما بعد مدتی تصمیم میگیرید بجای این کار از احراز هویت گوگل و SMS استفاده کنید. مشکل کجاست؟ این که باید توی کل برنامه بچرخید و سیستم جایگزین رو عوض کنید. یا مثلا شما برای ارسال request از Retrofit استفاده میکنید و به هر دلیلی میخواید از یه کتاب خونه دیگه استفاده کنید. به اصطلاح به این کد ها میگن boilerplate code. کتابخونه Dagger به ما کمک میکنه تا این داستان ها رو رفع کنیم. برای مطالعه بیشترفصل ۴ | اضافه کردن Dagger به پروژهخوب، بسیار ساده هست، برید توی فایل app/build.gradle و این تنظیمات رو اضافه کنید:plugins {
   id &#039;com.android.application&#039;
   id &#039;kotlin-android&#039;
   id &#039;kotlin-android-extensions&#039;
   id &#039;kotlin-kapt&#039;
}

...

dependencies {
    ...
    def dagger_version = &amp;quot2.40&amp;quot
    implementation &amp;quotcom.google.dagger:dagger:$dagger_version&amp;quot
    kapt &amp;quotcom.google.dagger:dagger-compiler:$dagger_version&amp;quot
}شما همچنیم میتونید آخرین نسخه Dagger رو توی این لینک پیدا کنید.توجه: دلیل استفاده Dagger از kapt این هست، چون Dagger برای اجرا نیاز به حاشیه نویسی داره (annotation) و این حاشیه نویسی ها توی زمان کامپایل اتفاق میوفته. اطلاعات بیشترفصل ۵ | حاشیه نویسی Inject@مثالی که میزنه این هست: میگه برید توی RegistrationViewModel.kt، و بجای این که توی پارامتر  constructor متغیر userManager رو پاس بدید، بگذارید که Dagger اون رو برای شما فراهم کنه.  به این صورت:// @Inject tells Dagger how to provide instances of this type
// Dagger also knows that UserManager is a dependency
class RegistrationViewModel @Inject constructor(val userManager: UserManager) {
    ...
}وقتی شما از Inject@ استفاده میکنید، به Dagger میگویید که او مسوول این فراهم کردن userManager هست و البته نباید فراموش کنید که کلمه constructor رو حتما بعدش بنویسید.شاید براتون جالب باشه: معنی کلمه inject: دیکشنری Longman ، دیکشنری Oxfordراستی یه نکته مهم دیگه: پروژه رو کنارتون باز کنید و کار ها رو انجام بدید.  چون توی پروژه گیت هاب که clone کردید این تغییرات اعمال نشده، شما میخواهید به پروژه ای که دارید Dagger اضافه کنید.[توضیحات من] به عکس بالا که فایل های پروژه رو نشون میداد برگردید. همون طور که میتونید ببینید با اضافه کردن قطعه کد بالا اون فلش (پیکان) از RegistrationViewModel به UserManager متصل میشه.با اضافه کردن Inject@ کتاب خونه Dagger متوجه میشه که :چجوری یک نمونه (instance) از جنس RegistrationViewModel بسازهو متوجه میشه که خود RegistrationViewModel وابستگی هایی داره و باید برای ساختن این کلاس قبلش userManager رو بسازه.خوب حالا Dagger میدونه که چجوری باید فراهم کنه نمونه هایی از جنش RegistrationViewModel و userManager رو.از اون جا که userManager هم وابستگی داره که وابستگی اون به interface‌ی هست به نامه Storage ما باید به Dagger بگیم که چجوری باید این نمونه ها (instance) ها رو بسازه. که به این مساله بعدا میپردازیم.توجه: یادتون نره که همین کار رو برای کلاس UserManager هم انجام بدیدلایه View نیاز داره به شی (Object)ی از این گرافclass RegistrationActivity : AppCompatActivity() {

    // @Inject annotated fields will be provided by Dagger
    @Inject
    lateinit var registrationViewModel: RegistrationViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Remove following line
        registrationViewModel = RegistrationViewModel((application as MyApplication).userManager)
    }
}همون طور که میبینید در RegistrationActivity - که لایه view برای ویو مدل بالا هست -در بیرون بخش onCreate ، ما متغیری رو تعریف کردیم به صورت lateinit و حاشیه گذاری Inject@ براش اعمال میکنیم. توی onCreate اومدیم و اون خطوط رو حذف میکنیم. [خطوطی که برای ساختن نمونه ای از viewModel هست درواقع]. اما ما باید به Dagger بگیم که باید خودش یه instance از همین view model بهمون بده، (در‌واقع خودش همه چیز هایی که باید inject کنه رو inject کنه) به این صورت: override fun onCreate(savedInstanceState: Bundle?) {
     (application as MyApplication).appComponent.inject(this) // add this line
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_registration)
        supportFragmentManager.beginTransaction()
            .add(R.id.fragment_holder, EnterDetailsFragment())
            .commit()
    }نکته خیلی مهم:‌ یادتون باشه که حتما باید این فراخوانی (همون خطی که اضافه کردیم) قبل از super.onCreate باشهفصل ۶ | حاشیه گذاری Component@ما میخوایم که Dagger بتونه برای ما گراف برنامه رو بسازه و اون ها رو برامون مدیریت کنه. برای این کار باید یه interface بسازیم و اون رو با نماد Component@ حاشیه گذاری کنیم.برای این کار یه package جدید میسازیم به اسم di (اگه گفتی مخفف چیه؟) و بعدش از نوع Kotlin interface یک فایل میسازیم و اسم اون رو AppComponent میذاریم.package com.example.android.dagger.di

import com.example.android.dagger.registration.RegistrationActivity
import dagger.Component

// Definition of a Dagger component
@Component
interface AppComponent {
    // Classes that can be injected by this Component
    fun inject(activity: RegistrationActivity)
}توی این interface متد injectی که تعریف کردیم توش کلاسی که اجازه استفاده کردن از این Component رو دارند رو اضافه میکنیم.  توجه: بعد از این داستان ها حتما باید یه build از پروژمون بگیریم.آماده یه ارور میشیم دوستان!این ارور (Error) رو میبینیم، (اروری هست که احتمالا زیاد ببینید):dagger/app/build/tmp/kapt3/stubs/debug/com/example/android/dagger/di/AppComponent.java:7: error: [Dagger/MissingBinding] com.example.android.dagger.storage.Storage cannot be provided without an @Provides-annotated methodچون ما به Dagger نگفتیم که چطور Storage رو برای ما فراهم کنه از توی userManager این مشکل رو دریافت کردیم. بریم فیکسش کنیم ؟؟؟ فصل ۷ |  Module, @Bind, @BindInstance@راه حل استفاده Dagger از Storage متفاوته چون یک interface هست و نمیشه به صورت مستقیم از آن نمونه ساخت، ما باید کاری کنیم که Dagger بتونه خودش یه نمونه ازش بسازه و از اون استفاده کنه، برای انجام این کار ما از Dagger Module استفاده میکنیم. و کلاس مون رو با Module@ حاشیه نویسی میکنیم.همانند کامپوننت ها، Module ها به Dagger میگن که چجوری باید از این نوع نمونه ای بسازه. وابستگی ها تعریف میشن با Provide@ و ‌Bind@ توی پوشه di میایم و کلاس جدیدی به اسم StorageModule میسازیم: package com.example.android.dagger.di

import dagger.Module

// Tells Dagger this is a Dagger module
@Module
class StorageModule {

}نکته ۱ : وقتی از Bind@ استفاده میکنیم که بخوایم یه interface رو به Dagger معرفی کنیمنکته ۲ : برای اضافه کردن Bind@ باید حتما کلاس مون abstract باشه. خوب، بعدش میبینیم که چه کسی (چه کلاسی) داره این interface رو داره implementation میکنه؟ (در‌ واقع میشه این طوری پرسید که توابعی که داریم کجا داره بدنه شون تامین میشه؟) که جوابش این هست:SharedPreferencesStorage .حالا میایم و با حاشیه گذاری Bind@ یه abstract fun میسازیم که قراره هست برای ما Storage رو فراهم کنه:// Tells Dagger this is a Dagger module
// Because of @Binds, StorageModule needs to be an abstract class
@Module
abstract class StorageModule {

    // Makes Dagger provide SharedPreferencesStorage when a Storage type is requested
    @Binds
    abstract fun provideStorage(storage: SharedPreferencesStorage): Storage
}حالا طبق کاری که قبلا هم کردیم میایم توی SharedPreferencesStorageو :// @Inject tells Dagger how to provide instances of this type
class SharedPreferencesStorage @Inject constructor(context: Context) : Storage { ... }مثلا کاری که قبلا هم کردیم Inject@ و constructor رو اضافه میکنیم تا به کلاس هایی که Dagger به اون ها دسترسی داره اضافه بشه.حاشیه گذاری BindInstance@ خوب حالا همین SharedPreferencesStorage داره به عنوان ورودی constructor داره context رو دریافت میکنه. حالا چجوری ما میتونیم این context رو به Dagger بفهمونیم؟؟ context رو که ما نمیسازیم، خود سیستم اندروید میسازه. چجوری باید این سیستم رو پیاده سازی کنیم؟؟میریم توی AppComponent و این کار ها رو میکنیم:@Component(modules = [StorageModule::class])
interface AppComponent {

    // Factory to create instances of the AppComponent
    @Component.Factory
    interface Factory {
        // With @BindsInstance, the Context passed in will be available in the graph
        fun create(@BindsInstance context: Context): AppComponent
    }

    fun inject(activity: RegistrationActivity)
}یه interface توش میسازیم و حاشیه گذاری میکنیم با Component.Factory@، و یه متد توش میسازیم که AppComponent رو برامون برمیگردونه (return میکنه) و توی پارامتر ورودی هم با حاشیه BindInstance@ میایم و context رو برمیداریم.(به عنوان پارامتر ورودی متد)حالا وقت آزمون هست: یه بار Build اش کنید ببینید کار میکنه یا نه، این دفعه باید بدون مشکل اجرا بشه.توجه توجه: همون طور که گفتم من هم دارم همراه این مقاله همون طور که این رو مینویسم دارم کد ها رو هم مینویسم (هم دارم خلاصه ترجمه رو مینویسم هم یاد میگیرم و هم کد میزنم)برای اجرا کردن به یه مشکلی خورده بودم که دیدم تو Github یکی براش راه حل گذاشته، چون برای من کار کرد این جا هم براتون میذارمش : بزن رو لینکیه مرور بکنیم؟تا حالا ما این کار ها رو انجام دادیم حال ندارم عکس رو توضیح بدم!! خودتون یک کم دقت کنید دستتون میاد :)فصل ۸ | اینجکت (Inject) کردن گراف توی Activityشما معمولا میخواهید که گراف برنامه شما تا وقتی که برنامه در‌حال اجرا هست ازش استفاده کنید و توی حافظه دستگاه تون باشه. برای همین اون رو توی کلاس Application باید تعریف کنیم. توی پروژه ما، ما نیاز به context برنامه هم داریم که باید بهش دسترسی داشته باشیم.خوب پس بریم توی فایل MyApplication.kt:open class MyApplication : Application() {

    // Instance of the AppComponent that will be used by all the Activities in the project
    val appComponent: AppComponent by lazy {
        // Creates an instance of AppComponent using its Factory constructor
        // We pass the applicationContext that will be used as Context in the graph
        DaggerAppComponent.factory().create(applicationContext)
    }

    open val userManager by lazy {
        UserManager(SharedPreferencesStorage(this))
    }
}همون طور که توی فصل قبل اشاره کردیم (شایدم اشاره نکردیم، یادم نیست :) Dagger برای ما کلاس هایی میسازه که یکی از اون ها DaggerAppComponent هست که شامل implementation از کلاس AppComponent خودمون هست. البته زمانی که build بگیریم. (همون دکمه چکش رو میگم)همون طور که مشاهده میکنید برای ساختن appComponent ما با استفاده از Component.Factory@ که قبلا توی فصل قبل ساختیم استفاده میکنیم تا context رو به گرافمون بدیم. که توی مورد ما context همون applicationContext هست، کلمه lazy چیه ؟ و داستانش چیه؟ بیشتر دربارش بخونیددقت و توجه کافی :عزایزان دقت کنید که dagger میتونه برای شما کد بسازه (generate کنه) پس اگه دیدید که DaggerAppComponent رو برنامه شما نمیشناسه باید حتما build اش کنید.خوب همه کار ها رو کردیم و به نظر میرسه هیچ مشکلی وجود نداره، و میریم که یه بار برنامه مون رو اجرا کنیم. انتظار میره که برنامه ما یه باگ داشته باشه، چرا؟چون صفحه Main باید بعد از جریانات احراز هویت بیاد بالا و جریانات Main هنوز گراف Dagger از اون ها اطلاعی نداره. در واقع MainActivity هنوز داره از UserData استفاده میکنه اما الان UserData تحت کنترل Dagger هست و Dagger باید نمونه ای رو MainActivity بده. استفاده از Dagger در در Main Flow اول از همه باید بریم سراغ AppComponent.kt و به Dagger بگیم که میتونه چیز ها رو تو MainActivity اینجکت کنه. اینجوری:@Component(modules = [StorageModule::class])
interface AppComponent {
    ...

    // Classes that can be injected by this Component
    fun inject(activity: RegistrationActivity)
    fun inject(activity: MainActivity)
}بعد باید بریم توی MainActivity و چیز هایی که میخوایم inject کنیم رو بیاریم (که viewModel و userManager هست) :class MainActivity : AppCompatActivity() {

    // @Inject annotated fields will be provided by Dagger
    @Inject
    private lateinit var userManager: UserManager

    @Inject
    private lateinit var mainViewModel: MainViewModel

    ...
}اون قسمت هایی که داره نمونه میسازه رو حذف میکنیم، چون Dagger داره برامون میسازه (فراهم میکنه):class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        // Remove this line
        userManager = (application as MyApplication).userManager
        if (!userManager.isUserLoggedIn()) {
           ...
        } else {
           ...
           // Remove this line too
            mainViewModel = MainViewModel(userManager.userDataRepository!!)
           ...
        }
    }
    ...
}و بجاش:class MainActivity : AppCompatActivity() {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {

        (application as MyApplication).appComponent.inject(this)

        super.onCreate(savedInstanceState)
        ...
    }
}حالا view model و repository هم به صورتی که قبلا کار کرده بودیم به Dagger میفهمونیم.view model:class MainViewModel @Inject constructor(private val userDataRepository: UserDataRepository) { ... }و همینطور user data repository :class UserDataRepository @Inject constructor(private val userManager: UserManager) { ... }حالا شمای کلی کارمون به این شکل شده:گراف برنامه ما همین الان یهوییخوب اما این داستان تموم نشده!!! باز هم ارور داریم (ای بابا) باید اون private ها رو برداریم از توی MainActivity ، اینجوری:class MainActivity : AppCompatActivity() {    @Inject    lateinit var userManager: UserManager    @Inject    lateinit var mainViewModel: MainViewModel    ...}حالا باید پروژه ما به درستی build بشه، و به درستی هم اجرا شه. اگه شما احراز هویت شده باشید قبلا شما به صفحه login screen میرید. و میتونید دکمه &quot;Unregister&quot; رو بزنید و دوباره مراحل رو از اول ثبت نام از سر بگیرید. اما وقتی شما احراز هویت کردید شما رو به صفحه اصلی منتقل نمیکنه بلکه شما رو دوباره به Login Activity هدایت میکنه. دوباره مثل این که باگ داریم!!اما چرا؟‌   main و جرایانات registration هر دو از گراف برنامه userManager رو دریافت میکنند.مشکل اینجاست که Dagger به صورت پیشفرض همیشه نمونه جدیدی از از همون نوع (توی پروژه ما userData) تعریف میکنه، اما چگونه میتونیم کاری کنیم که فقط یه نمونه برای هر دفعه که میخوایم از اون استفاده کنیم داشته باشیم و هر دفعه اون رو مورد باز‌استفاده قرار بدیم؟؟ با Scoping معنی کلمه Scoping:  دیکشنری لانگمن، دیکشنری آکسفوردفصل ۹ | استفاده از Scope ها بعضی وقت ها شما میخواید یک نمونه از وابستگی رو در یک کامپوننت داشته باشید. بنابر دلایلی مثل: میخواید همه یک نمونه رو داشته باشند چون میخواید یه سری داده ها رو با اون ها به اشتراگ بگذارید (مثل پروژه ما)زمانی درست کردن یک شی(object) جدید بسیار پر‌هزینه باشه. و شما نمیخواید همینجوری نمونه های زیادی بسازید (مثلا مفسر (parser) های json) با استفاده از Scope ها شما یک نمونه یکتا برای اون نوع از (Data type) برای کامپوننت تون دارید. در واقع اسکوپ ها در چرخه زمانی کامپوننت ها وجود دارند. برای AppComponent ما میتونیم از Singleton@ که یک حاشیه گذاری اسکوپ هست استفاده کنیم. (از پکیج javax.inject) خوب برای این که ما هم بتونیم توی برنامه خودمون از این استفاده کنیم باید چیکار کنیم؟؟AppComponent:@Singleton
@Component(modules = [StorageModule::class])
interface AppComponent { ... } حال کلاس هایی که با Singleton@ حاشیه گذاری بشن اسکوپ میشن در AppComponent، خوب حالا بریم و UserManager رو کاری کنیم که نمونه های یکتا بسازه:@Singleton
class UserManager @Inject constructor(private val storage: Storage) {
    ...
}حالا ما کاری کردیم که کلاس UserManager فقط یه نمونه داشته باشه و این نمونه رو بین بقیه درخواست ها به اشتراک بگذاره. به همین سادگی و به همین خوشمزگی. بریم پروژه رو اجرا کنیم ببینیم چی میشه.ایول درست شد گراف برنامه:یه مرور کنیم ببینیم تا حالا چیکار کردیم؟فصل۱۰ | Subcomponent هابگذارید همینطور ادامه بدهیم، فرگمنت های احراز هویت هنوز از Dagger استفاده نمیکنند. از اون جا که ما میخوایم EnterDetailsFragment و TermsAndConditionsFragment توسط Dagger اینجکت بشن باید اجازه این کار رو به AppComponent بدیم:@Singleton
@Component(modules = [StorageModule::class])
interface AppComponent {
    ...
    fun inject(activity: RegistrationActivity)
    fun inject(fragment: EnterDetailsFragment)
    fun inject(fragment: TermsAndConditionsFragment)
    fun inject(activity: MainActivity)
}و همینطوری ادامه میدهیم (کار هایی هست که قبلا انجام دادیم):EnterDetailsFragment.ktclass EnterDetailsFragment : Fragment() {

    @Inject
    lateinit var registrationViewModel: RegistrationViewModel
 
    @Inject
    lateinit var enterDetailsViewModel: EnterDetailsViewModel

    ...
}در ادامه خطوط زیر رو هم باید حذف کنید:class EnterDetailsFragment : Fragment() {

    override fun onCreateView(...): View? {
        ...
        // Remove following lines
        registrationViewModel = (activity as RegistrationActivity).registrationViewModel
        enterDetailsViewModel = EnterDetailsViewModel()

        ...
    }
}حالا همون طور که قبلا هم انجام داده بودیم توی onAttach :class EnterDetailsFragment : Fragment() {

    override fun onAttach(context: Context) {
        super.onAttach(context)

        (requireActivity().application as MyApplication).appComponent.inject(this)
    }
}
توجه - Best practices:اکتیویتی  Dagger رو توی تابع onCreate قبل از super صدا میزنهتوی فرگمنت ها توی onAttach بعد از superبعدش هم نوبت ویو مدل هست:EnterDetailsViewModel.ktclass EnterDetailsViewModel @Inject constructor() { ... }الان EnterDetailsFragmentآماده شده، حالا نوبت TermsAndConditionsFragmentهست حاشیه گذاری Inject@ در ترم اند کاندیشن و حذف کردن private حذف کردن نمونه گیری معمولی registrationViewModelاینجکت کردن توی onAttachTermsAndConditionsFragment.ktclass TermsAndConditionsFragment : Fragment() {

    @Inject
    lateinit var registrationViewModel: RegistrationViewModel

    override fun onAttach(context: Context) {
        super.onAttach(context)

        (requireActivity().application as MyApplication).appComponent.inject(this)
    }

    override fun onCreateView(...): View? {
         ...
         // Remove following line
         registrationViewModel = (activity as RegistrationActivity).registrationViewModel
         ...
    }
}حالا برنامه رو اجرا میکنیم. چی اتقاقی افتاد؟ آیا برنامه بعد از احراز هویت کرش کرد؟؟ مشکل اینجاست که نمونه های متفاوتی از RegistrationViewModel اینجکت شده: در RegistrationActivity  و EnterDetailsFragmentو TermsAndConditionsFragment. درحالی که این چیزی نیست که ما میخوایم که یک نمونه اینجکت بشه اکتیویتی و فرگمنت ها. چی میش در RegistrationViewModelاز Singleton@ استفاده کنیم؟ این مشکل ما رو برای الان حل میکنه اما در آینده ممکن هست مشکلاتی رو بوجود بیاره:ما نمیخوایم که نمونه ای از RegistrationViewModel در حافظه باشه زمانی که پروسه احراز هویت کارش تموم شده. ما میخوایم نمونه های متفاوتی از RegistrationViewModel وجود داشته باشه وقتی که جریان متفاوتی از پروسه های احراز هویت در جریان باشه، وقتی که کاربر داره ورود یا خروج میکنه.به قولی ما نمیخوایم داده های احراز هویت قبلی رو برای احراز هویت جدید داشته باشیم.ما میخوایم که فرگمنت های احراز هویت ویو مدلی که از اکتیویتی میاد رو مورد باز استفاده قرار بدن (از یه ویو مدل استفاده کنند) اما اگه اکتیویتی مورد تغییر قرار گرفت، ما نمونه جدیدی لازم داریم .ما نیاز داریم که  RegistrationViewModel اسکوپ کنیم برای RegistrationActivity. برای این کار ما میتونیم کامپوننت دیگه ای برای فرایند احراز هویت و اسکوپ کردن برای ویو مدل بسازیم. برای این که بتونیم این کار رو عملی کنیم از Dagger subcomponent ها استفاده میکنیم.ساب‌کامپوننت (زیر‌کامپوننت) های Daggerساب‌کامپوننت ها، کامپوننت هایی هستند که اشیاء  گراف کامپوننت پدر(مادر) رو به ارث میبرند. همچنین همه اشیاء فراهم شده توسط کامپوننت پدر فراهم خواهند شد در ساب‌کامپوننت. در این روش objectی که از ساب‌کامپوننت هست میتونه به objectی از کامپوننت پدر وابسته باشه.خوب برای این کار یه فایل میسازیم به اسم RegistrationComponent.kt توی پکیج registration٫. حالا میتونیم interfaceی بسازیم به اسم RegistrationComponent. و اون با نماد Subcomponent@ حاشیه گذاری کنیم:registration/RegistrationComponent.ktpackage com.example.android.dagger.registration

import dagger.Subcomponent

// Definition of a Dagger subcomponent
@Subcomponent
interface RegistrationComponent {

}این کامپوننت نیاز داره که شامل اطلاعات احراز هویت بشه. برای این کار:اضافه کردن متد های inject از AppComponent که به طور خاص به احراز هویت مربوط هستند (RegistrationActivityوEnterDetailsFragmentوTermsAndConditionsFragment)ساختن ساب‌کامپوننت factory که باهاش بتونیم نمونه ای از این ساب‌کامپوننت بسازیمRegistrationComponent.kt// Definition of a Dagger subcomponent
@Subcomponent
interface RegistrationComponent {

    // Factory to create instances of RegistrationComponent
    @Subcomponent.Factory
    interface Factory {
        fun create(): RegistrationComponent
    }

    // Classes that can be injected by this Component
    fun inject(activity: RegistrationActivity)
    fun inject(fragment: EnterDetailsFragment)
    fun inject(fragment: TermsAndConditionsFragment)
}توی AppComponent، ما باید همه inject هایی که به ساب‌کامپوننت بردیم رو پاک کنیم. چون این ها دیگه قرار نیست استفاده بشه. بجای اون ها RegistrationActivity برای ساختن نمونه هایی از RegistrationComponent، ما نیاز داریم که factory رو در معرض AppComponent قرار بدیم: AppComponent.kt@Singleton
@Component(modules = [StorageModule::class])
interface AppComponent {

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance context: Context): AppComponent
    }

    // Expose RegistrationComponent factory from the graph
    fun registrationComponent(): RegistrationComponent.Factory

    fun inject(activity: MainActivity)
}ما با استفاده از تابعی با خروجی RegistrationComponent.Factory ساب‌کامپوننت مون رو در معرض AppComponent گذاشتیم.دو راه برای تعامل با گراف Dagger وجود داره:۱. درست کردن متدی با خروجی Unit و دادن کلاس به عنوان پارامتر ورودی ۲. درست کردن متد با خروجی یه typeی که بازیابی میکنه اون رو از گراف(مثل همینی که الان ساختیم)خوب حالا باید یه جوری به AppComponent بفهمونیم که این RegistrationComponent یه ساب‌کامپوننت هست و میتونه برای ما کد بسازه. و باید برای این یه ماجول Dagger بسازیم.میریم توی di و میسازیمش:app/src/main/java/com/example/android/dagger/di/AppSubcomponents.kt// This module tells AppComponent which are its subcomponents
@Module(subcomponents = [RegistrationComponent::class])
class AppSubcomponentsحالا باید این رو توی AppComponent هم اضافه کنیم:@Singleton
@Component(modules = [StorageModule::class, AppSubcomponents::class])
interface AppComponent { ... }برنامه مون چجوری شده؟؟اینجوری شدهدوستان برید یه آبی بخوید، دستی به آب بزنید، از وسط های این نوشته هی به خودم میگفتم مهدی این که کاری هست داری میکنی!  :) فصل ۱۱ | اسکوپ کردن ساب‌کامپوننت هاما ساب‌کامپوننت رو احتیاج داشتیم تا بتونیم یک نمونه از RegistrationViewModel بین اکتیویتی و فرگمنت به اشتراک بگذاریم. همون طوری که قبلا هم انجام دادیم، اگه در حاشیه گذاری کامپوننت و کلاس مورد نظر از اسکوپ استفاده کنیم، میتونیم نمونه یکتا براش درست کنیم. اما نمیتونیم از Singleton٬@ استفاده کنیم  چون هم اکنون در‌حال استفاده توسط AppComponent هست. پس ما یه نوع دیگه ای نیاز داریم.در این مورد ما میتونیم اسکوپ RegistrationScope@ رو صدا بزنیم. اما این برای تمرین خوب نیست. چون نام حاشیه گذاری اسکوپ باید کاملا دقیق باشه تا بتونه منظور رو برسونه. اون باید نامیده بشه بسته به مدت زمانی که این حاشیه گذاری قابل باز‌استفاده هست توسط کامپوننت های هم‌رده (کامپوننت های برادر) که (برای این مثال: LoginComponent ،SettingsComponent و ...) برای همین منظور بجای استفاده از  RegistrationScope@ از ActivityScope@ استفاده میکنیم.قوانین اسکوپ:۱. وقتی نوعی نشانه گذاری میشه با حاشیه گذاری اسکوپ، اون فقط میتونه توسط کامپوننت هایی که حاشیه گذاری  با همون اسکوپ شدند استفاده بشه.۲. زمانی که کامپوننت با حاشیه‌گذاری اسکوپ نشانه گذاری شده، اون فقط میتونه نوع هایی رو فراهم کنه که همون حاشیه گذاری رو دارند یا نوع هایی که حاشیه گذاری نشده‌ اند.۳. یک ساب‌کامپوننت نمیتونه از حاشیه گذاری اسکوپی استفاده کنه که قبلا توسط کامپوننت پدر استفاده شدهکامپوننت ها همچنین میتونن ساب‌کامپوننت ها رو با همون context درگیر کنن.حالا بیاید و ActivityScope.kt رو در پکیچ di ایجاد کنیم:app/src/main/java/com/example/android/dagger/di/ActivityScope.kt@Scope
@MustBeDocumented
@Retention(value = AnnotationRetention.RUNTIME)
annotation class ActivityScopeحالا برای این که RegistrationViewModel رو بتونیم در RegistrationComponent اسکوپ کنیم. باید کلاس و اینترفیس مون از حاشیه گذاری ActivityScope@ بشن:app/src/main/java/com/example/android/dagger/di/ActivityScope.kt// Scopes this ViewModel to components that use @ActivityScope
@ActivityScope
class RegistrationViewModel @Inject constructor(val userManager: UserManager) {
    ...
}RegistrationComponent.kt// Classes annotated with @ActivityScope will have a unique instance in this Component
@ActivityScope
@Subcomponent
interface RegistrationComponent { ... }حالا RegistrationComponent برای ما همیشه همون نمونه از RegistrationViewModel رو فراهم میکنه.چرخه حیات (lifecycle) ساب‌کامپوننتاول از همه AppComponent میاد توی چرخه حیات برنامه، چون ما میخوایم یه گراف برنامه تا زمانی که برنامه توی حافظه هست داشته باشیم.حالا چرخه حیات RegistrationComponent: یکی از دلایل این که چرا ما نیاز داریم این هست که ما میخوایم یک نمونه از RegistrationViewModel رو بین اکتیویتی و فرگمنت داشته باشیم. اما همچنین میخوایم نمونه جدیدی ایجاد کنیم اگه جریانات جدیدی برای احراز هویت رخ بده.اکتیویتی RegistrationActivity دقیقا چرخه حیات RegistrationComponent رو داره: برای هر اکتیویتی جدید ما یک RegistrationComponent جدید خواهیم ساخت و فرگمنت ها هم از همون نمونه از RegistrationComponent استفاده کنند.تا زمانی که RegistrationComponent متصل شده در چرخه حیات RegistrationActivity، ما باید نگهداریم منبعی (reference) از این کامپوننت رو در اکتیویتی، به همون صورت که kept منبعی از appComponent  رو در کلاس Application نگه میداره. با این روش فرگمنت ها در دسترس قرار میگیرند.دوستان اگه میتونید این قسمت رو بهتر ترجمه کنید ممنون میشم! لینک (توان من همین بود :)RegistrationActivity.ktclass RegistrationActivity : AppCompatActivity() {

    // Stores an instance of RegistrationComponent so that its Fragments can access it
    lateinit var registrationComponent: RegistrationComponent
    ...
}کار های زیر رو انجام میدیم:RegistrationActivity.ktclass RegistrationActivity : AppCompatActivity() {
    ...

    override fun onCreate(savedInstanceState: Bundle?) {

        // Remove lines 
        (application as MyApplication).appComponent.inject(this)

        // Add these lines

        // Creates an instance of Registration component by grabbing the factory from the app graph
        registrationComponent = (application as MyApplication).appComponent.registrationComponent().create() 
        // Injects this activity to the just created registration component
        registrationComponent.inject(this)

        super.onCreate(savedInstanceState)
        ...
    }
    ...
}همون طور که مشاهده میکنید registrationComponent نیازی نداره به Inject@ چون نمیخوایم Dagger اون رو برای ما بسازه.حالا registrationComponent در RegistrationActivity موجود هست و میتونیم همون نمونه رو برای فرگمنت احراز هویت اینجکت کنیم. توی onAttach تغییرات رو انجام میدیدم:EnterDetailsFragment.ktclass EnterDetailsFragment : Fragment() {
    ...
    override fun onAttach(context: Context) {
        super.onAttach(context)

        (activity as RegistrationActivity).registrationComponent.inject(this)
    }
    ...
}و همین کار رو حالا برای TermsAndConditions این هم انجام میدیم:class TermsAndConditionsFragment : Fragment() {
    ...
    override fun onAttach(context: Context) {
        super.onAttach(context)

        (activity as RegistrationActivity).registrationComponent.inject(this)
    }
}دوستان بهتون پیشنهاد میکنم برای هر فصلی که میخونید و اجرا میگیرید و درست کار میکنه یه git commit بزنید. برای اجرای این فصل یه اشتباه خیلی ساده که توی فصل قبلی انجام داده بودم ۲ ساعت و نیم وقتم رو گرفت!!حالا اشتباه چی بود؟ یادم رفته بود توی AppComponent بیام توی تابع registrationComponent خروجی رو با Factory. بزنم!بریم ببینیم الان پروژمون چه شکلی شده:اینجوریتفاوت دیاگرام قبلی با این دیاگرام این هست که RegistrationViewModel اسکوپ شده به RegistrationComponent که اون رو با نقطه نارنجی نشون دادیم. فصل ۱۲ | ریفکتو کردن جریانات Login (ورود)جدای از اسکوپ کردن اشیا در چرخه های حیات متفاوت، ساختن ساب‌کامپوننت ها تمرین خوبی برای encapsulate کردن بخش های مختلف برنامه شماست.ساختار سازی برنامه شما با ساختن زیر‌گراف های متفاوت در Dagger بسته به جریانات برنامه شما کمک میکنه که بتونید برنامتون رو توسعه پذیر کنید اون هم در زمان حافظه و در لحظه شروع. از ساختن کامپوننت های خیلی بزرگ که تغییر دادن اون ها سخت هست امتناع کنید.  کامپوننت هایی که تعداد زیادی از اشیاء برنامه شما رو فراهم میکنند. این کار باعث میشه کامپوننت های Dagger خوانایی کم و قطعه قطعه (modularize) شدن بشه.حالا بریم Login رو درست کنیم، فایل زیر رو ایجاد میکنیم:login/LoginComponent.kt// Scope annotation that the LoginComponent uses
// Classes annotated with @ActivityScope will have a unique instance in this Component
@ActivityScope
// Definition of a Dagger subcomponent
@Subcomponent
interface LoginComponent {

    // Factory to create instances of LoginComponent
    @Subcomponent.Factory
    interface Factory {
        fun create(): LoginComponent
    }

    // Classes that can be injected by this Component
    fun inject(activity: LoginActivity)
}بعدش توی view model :LoginViewModel.ktclass LoginViewModel @Inject constructor(private val userManager: UserManager) {
    ...
} در این مورد LoginViewModel نیازی نیست که مورد باز‌استفاده توسط بقیه کلاس ها قرار بگیره، برای همین نیازی به استفاده ActivityScope@ نیست.همچنین ما باید این ساب‌کامپوننت مون رو به لیست ساب‌کامپوننت های AppComponent اضافه کنیم در ماجول AppSubcomponents :AppSubcomponents.kt@Module(subcomponents = [RegistrationComponent::class, LoginComponent::class])
class AppSubcomponentsبرای LoginActivity هم که بتونه فکتوریLoginComponent رو در دسترس داشته باشه باید در اینترفیس AppComponent :AppComponent.kt@Singleton
@Component(modules = [StorageModule::class, AppSubcomponents::class])
interface AppComponent {
    ...
    // Types that can be retrieved from the graph
    fun registrationComponent(): RegistrationComponent.Factory
    fun loginComponent(): LoginComponent.Factory

    // Classes that can be injected by this Component
    fun inject(activity: MainActivity)
}حالا باید بریم و توی LoginActivity و همون سه تغییر همیشگی رو انجام بدیم:LoginActivity.ktclass LoginActivity : AppCompatActivity() {

    // 1) LoginViewModel is provided by Dagger
    @Inject
    lateinit var loginViewModel: LoginViewModel

    ...

    override fun onCreate(savedInstanceState: Bundle?) {

        // 2) Creates an instance of Login component by grabbing the factory from the app graph
        // and injects this activity to that Component
        (application as MyApplication).appComponent.loginComponent().create().inject(this)

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        // 3) Remove instantiation
        loginViewModel = LoginViewModel((application as MyApplication).userManager)
        ...
    }
}حالا اگه شما برنامتون رو اجرا کنید باید به درستی اجرا بشه.برناممون چه شکلی شده؟اینجوری!فصل ۱۳ | چند اکتیویتی با یه اسکوپروی دکمه تنظیمات کلیک کنید شما خواهید دید که برنامه شما کرش (crash) میکنه. بیاید این مشکل رو با Dagger حل کنیم.وقتی ما میخوایمSettingsActivity توسط Dagger اینجکت بشه: باید به Dagger بگیم که چجوری باید نمونه ای ازSettingsActivity و وابستگی هاش بسازه (SettingsViewModel) :SettingsViewModel.ktclass SettingsViewModel @Inject constructor(
    private val userDataRepository: UserDataRepository,
    private val userManager: UserManager
) { ... }۲.   بهSettingsActivity اجازه بدیم تا توسط Dagger اینجکت بشه با اضافه کردن متدی کهSettingsActivity رو در اینترفیسAppComponent دریافت میکنه:AppComponent.kt@Singleton
@Component(modules = [StorageModule::class, AppSubcomponents::class])
interface AppComponent {
    ...
    fun inject(activity: SettingsActivity)
}۳.  درSettingsActivity،باید کار های همیشگی رو انجام بدیم:SettingsActivity.ktclass SettingsActivity : AppCompatActivity() {

    // 1) SettingsViewModel is provided by Dagger
    @Inject
    lateinit var settingsViewModel: SettingsViewModel

    override fun onCreate(savedInstanceState: Bundle?) {

        // 2) Injects appComponent
        (application as MyApplication).appComponent.inject(this)

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_settings)

        // 3) Remove following lines
        val userManager = (application as MyApplication).userManager
        settingsViewModel = SettingsViewModel(userManager.userDataRepository!!, userManager)
        ...
    }
}اگه برنامه رو اجرا کنید میبیند که فیچر(feature) بروز رسانی نوتیفیکیشن ها در تنظیمات کار نمیکنه. این بخاطر این هست که ما از یک نمونه ازUserDataRepository بینMainActivity وSettingsActivity استفاده نمیکنیم!آیا ما میتونیمUserDataRepository رو درAppComponent اسکوپ کنیم با حاشیه گذاری Singleton@؟ بنا به دلایلی قبلا بحث شده ما نمیخوایم این کار رو انجام بدیم، چون اگه کاربر خروج انجام بده یا (unregisters) کنه ما نمیخوایم نمونه ای ازUserDataRepository در حافظه داشته باشیم چون این داده ها برای هر کاربر ورود(login) شده بخصوص هست. پس ما میخوایم کامپوننتی بسازیم که تا وقتی کار کنه که کاربر در حالت ورود هست. همه اکتیویتی هایی باید بعد از ورود کاربر در دسترس قرار بگیرند در اون کامپوننت قرار بگیرند. ( MainActivity و SettingsActivity)پس اجازه دهید تا یک ساب‌کامپوننت جدیدی بسازیم و اسمش رو همUserComponent بگذاریم و همون طور که تویLoginComponent وRegistrationComponent این کار رو انجام دادیم: ساختن فایل کاتلین به نامUserComponent.kt در پوشهuser.ساختن اینترفیسی به نامUserComponent و اون رو باSubcomponent@ حاشیه گذاری کنیم که میتونه کلاس هایی که باید بعد از ورود کاربر به حساب کاربری اتفاق میوفتند رو داشته باشه و یک factoryapp/src/main/java/com/example/android/dagger/user/UserComponent.kt// Definition of a Dagger subcomponent
@Subcomponent
interface UserComponent {

    // Factory to create instances of UserComponent
    @Subcomponent.Factory
    interface Factory {
        fun create(): UserComponent
    }

    // Classes that can be injected by this Component
    fun inject(activity: MainActivity)
    fun inject(activity: SettingsActivity)
}۳.  اضافه کردن این ساب‌کامپوننت جدید به لیست کامپوننت هایAppComponent :AppSubcomponents.kt@Module(subcomponents = [RegistrationComponent::class, LoginComponent::class, UserComponent::class])
class AppSubcomponents چه چیزی چرخه حیاتUserComponent رو شارژ میکنه؟LoginComponent و RegistrationComponent توسط اکتویتی ها مدیریت میشوند اماUserComponent میتونه بیش از یک اکتیویتی اینجکت بشه و تعداد اکتیویتی ها پتانسیل بیشتر شدن رو داره.ما باید چیزی به چرخه حیات این کامپوننت اضافه کنیم که بفهمه کاربر کی ورود یا خروج کرده. در مورد ماUserManager هست.که  احراز هویت رو بر عهده داره. و ورود و خروج به نظر میرسه که مربوط به وضایف این کلاس هست. پس این جا باید نمونه ای ازUserComponent ایجاد بشه.اگهUserManager نیاز داره که نمونه ای ازUserComponent رو داشته باشه، باید به UserComponent دسترسی داشته باشه، اگه ما به عنوان پارامتر سازنده(constructor) بدیم، Dagger خودش نمونه ای از UserManager رو برای ما فراهم میکنه.UserManager.kt@Singleton
class UserManager @Inject constructor(
    private val storage: Storage,
    // Since UserManager will be in charge of managing the UserComponent lifecycle,
    // it needs to know how to create instances of it
    private val userComponentFactory: UserComponent.Factory
) {
    ...
}در تزریق وابستگی دستی، ما باید نشستی(session) از داده کاربر درUserManager نگهداری میکردیم. که تصمیم میگیره کاربر ورود یا ورود کنه. ما میتوینم همین رو درUserComponent درست کنیم.ما میتونیم نمونه ای ازUserComponent درUserManager داشته باشیم تا چرخه یات اون رو مدیریت کنیم. کاربر زمانی ورود میکنه کهUserComponent خالی (null) نباشه. وقتی هم که کاربر خروج میکنه ما میتونیم اون نمونه ازUserComponent رو حذف کنیم. با این روش وقتی این کامپوننت نابود میکنیم، همه داده ها همراهش در حافظه حذف میشه.تغییر دادن UserManager برای استفاده از نمونه‌ای ازUserComponent بجای UserDataRepository:UserManager.kt@Singleton
class UserManager @Inject constructor(...) {
    //Remove line
    var userDataRepository: UserDataRepository? = null

    // Add or edit the following lines
    var userComponent: UserComponent? = null
          private set

    fun isUserLoggedIn() = userComponent != null

    fun logout() {
        userComponent = null
    }

    private fun userJustLoggedIn() {
        userComponent = userComponentFactory.create()
    }
}همون طوری که در قطعه کد بالا میبینید، ما زمانی نمونه ایuserComponent ساختیم که کاربر ورود میکنه با استفاده از factory. و وقتی هم که خروج میکنه اون رو نابود میکنیم.ما میخوایمUserDataRepository اسکوپ بشه در UserComponent تا MainActivity و SettingsActivity هر دو بتونن یک نمونه ازش داشته باشند. وقتی که از حاشیه گذاری اسکوپ ActivityScope@ برای حاشیه گذاری کردن کامپوننت هایی که مدیریت زمان اکتیویتی رو برعهده دارند استفاده میکنم، ما نیاز به اسکوپی داریم که بتوینم چند اکتیویتی رو تحت پوشش قرار بدیم اما همه برنامه رو نه،  ما هنوز همچین چیزی رو پیاده سازی نکردیم. پس باید اسکوپ جدیدی بسازیم. زمانی که اسکوپ کاور میکنه زمان حیات رو اگه کاربر ورود انجام بده، ما میتونیم LoggedUserScope رو صدا بزنیم.ساختن یه فایل کاتلین به اسم LoggedUserScope.kt در پکیج user و تعریف کردن حاشیه گذاری اسکوپ LoggedUserScope :app/src/main/java/com/example/android/dagger/user/LoggedUserScope.kt@Scope
@MustBeDocumented
@Retention(value = AnnotationRetention.RUNTIME)
annotation class LoggedUserScopeما میتونیم UserComponent و UserDataRepository با این حاشیه گذاری کنیم تا  UserComponent بتونه همیشه یک نمونه از UserDataRepository  رو فراهم کنه.UserComponent.kt// Scope annotation that the UserComponent uses
// Classes annotated with @LoggedUserScope will have a unique instance in this Component
@LoggedUserScope
@Subcomponent
interface UserComponent { ... }UserDataRepository.kt// This object will have a unique instance in a Component that 
// is annotated with @LoggedUserScope (i.e. only UserComponent in this case).
@LoggedUserScope
class UserDataRepository @Inject constructor(private val userManager: UserManager) {
    ...
}توی کلاس MyApplication ما نمونه ای از userManager نگهداری میکنیم که احتیاج داره به پیاده سازی دستی تزریق وابستگی، از اون جا که کل برنامه توسط Dagger تغییر کرده (refactored شده) ما دیگه اون رو نمیخوایم. پس MyApplication به این شکل درمیاد:MyApplication.ktopen class MyApplication : Application() {

    val appComponent: AppComponent by lazy {
        DaggerAppComponent.factory().create(applicationContext)
    }
}ما باید AppComponent رو هم تغییر بدیم:@Singleton
@Component(modules = [StorageModule::class, AppSubcomponents::class])
interface AppComponent {
    ... 
    // 2) Expose UserManager so that MainActivity and SettingsActivity
    // can access a particular instance of UserComponent
    fun userManager(): UserManager

    // 1) Remove following lines
    fun inject(activity: MainActivity)
    fun inject(activity: SettingsActivity)
}در SettingsActivity ویو مدل رو با Inject@  حاشیه گذاری (وقتی که ما میخوایم توسط Dagger اینجکت بشه) و private هم حذف میکنیم. برای گرفتن نمونه UserComponent که باید مقدار اولیه داده بشه بهش، چون کاربر ورود انجام داده، ما میتونیم متد ()userManager رو در appComponent قرار بدیم. حالا میتونیم به userComponent در اکتیویتی userComponent داشته باشیم.SettingsActivity.ktclass SettingsActivity : AppCompatActivity() {
    // @Inject annotated fields will be provided by Dagger
    @Inject
    lateinit var settingsViewModel: SettingsViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        // Gets the userManager from the application graph to obtain the instance
        // of UserComponent and gets this Activity injected
        val userManager = (application as MyApplication).appComponent.userManager()
        userManager.userComponent!!.inject(this)

        super.onCreate(savedInstanceState)
        ...
    }
    ...
}MainActivity.ktclass MainActivity : AppCompatActivity() {

    // 1) Remove userManager field
    @Inject
    lateinit var userManager: UserManager

    @Inject
    lateinit var mainViewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_settings)

        // 2) Grab userManager from appComponent to check if the user is logged in or not
        val userManager = (application as MyApplication).appComponent.userManager()
        if (!userManager.isUserLoggedIn()) { ... }
        else {
            setContentView(R.layout.activity_main)
            // 3) If the MainActivity needs to be displayed, we get the UserComponent
            // from the application graph and gets this Activity injected
            userManager.userComponent!!.inject(this)
            setupViews()
        }
    }
    ...
}نکته مهم:انجام دادن اینجکت کردن شرطی (همین کاری که ما توی  MainActivity.kt انجام دادیم برای چک کردن ورود بودن کاربر)  خیلی خطرناک هست. برنامه نویس نباید ریسک گرفتن NullPointerException وقتی که داره با فیلد های اینجکت شده ارتباط میگیره و باید از این کار اجتناب کنه.برای اجتناب از این داستان، میتونیم کار هایی انجام تا این کار غیر مستقیم باشه مثل ساختن صفحه splash.حالا برنامه رو اجرا میکنیم. برناممون چجوری شده حالا؟!؟!اینجوری، عزیزکانم :)))خبر خوب، الان کل برنامه داره از dagger استفاده میکنه.فصل ۱۴ | تست نویسی برای Daggerاما آیا کار ما برنامه تموم شده؟؟ نه معلومه که نه! دیگه نبینم برنامه هاتون وقتی درست کار میکنه به امون خدا بسپورینش ها!!!! آفرین حتما براش تست بنویسید، باریکلا، ثواب داره :)-- مادر عروس :))یکی از مهم ترین مزایای استفاده از تزریق وابستگی و Dagger این هست که تست نویسی برای برنامه رو بسیار ساده میکنه.یونیت تست (Unit tests)نیازی نیست کد هایی که به Dagger مربوط هست برای یونیت تست بنویسید. وقتی شما از کلاسی استفاده میکنید که به صورت سازنده (constructor) اینجکت شده شما نیازی ندارید به Dagger بگید تا برای شما نمونه از کلاس ایجاد کنه. شما میتونید به صورت مستقیم سازنده رو صدا بزنید و بهش داده های فیک(fake) یا mock شده رو بهش پاس بدید. انگار که حاشیه گذاری ها وجود ندارند.برای مثال اگه به LoginViewModelTest.kt نگاه کنید، ما فقط UserManager رو mock میکنیم و بهش پاس میدیم. مثل این که اصلا Dagger وجود نداره:LoginViewModelTest.ktclass LoginViewModelTest {
    ...
    private lateinit var viewModel: LoginViewModel
    private lateinit var userManager: UserManager

    @Before
    fun setup() {
        userManager = mock(UserManager::class.java)
        viewModel = LoginViewModel(userManager)
    }

    @Test
    fun `Get username`() {
        whenever(userManager.username).thenReturn(&amp;quotUsername&amp;quot)

        val username = viewModel.getUsername()

        assertEquals(&amp;quotUsername&amp;quot, username)
    }
    ...
}همه یونیت تست های ما مثل قبلا باقی میمونه بجز یکی، وقتی ما UserComponent.Factory رو به UserManager اضافه کردیم، یونیت تست مون رو خراب کردیم. ما باید چیزی که Dagger میخواد برگردونه رو mock کنیم.  زمانی که ()create صدا زده میشه در factory.فایل UserManagerTest.kt رو باز کنید و mock رو پیکربندی کنید برای فکتوریUserComponent:UserManagerTest.ktclass UserManagerTest {
    ...

    @Before
    fun setup() {
        // Return mock userComponent when calling the factory
        val userComponentFactory = Mockito.mock(UserComponent.Factory::class.java)
        val userComponent = Mockito.mock(UserComponent::class.java)
        `when`(userComponentFactory.create()).thenReturn(userComponent)

        storage = FakeStorage()
        userManager = UserManager(storage, userComponentFactory)
    }

    ...
}حالا باید همه تست ها درست کار کنند.تست های End-to-end ما integration test (ترجمه) ها مون رو بدون dagger انجام دادیم. وقتی برناممون با dagger رو آشنا شد، و  پیاده سازی MyApplication رو تغییر داد، ما اون ها رو خراب کردیم.ساختن Application شخصی سازی شده در تست های instrumentation (ترجمه)قبل این داستان، تست های end-to-end های ما از اپلیکیشن شخصی شده ای به نام MyTestApplication استفاده میکنند. برای این که از یه اپلیکیشن دیگه استفاده کنیم، ما باید یک TestRunner بسازیم. این کد در برنامه وجود داره و نیاز نیست اون رو دوباره بسازید:app/src/androidTest/java/com/example/android/dagger/MyCustomTestRunner.ktclass MyCustomTestRunner : AndroidJUnitRunner() {

    override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
        return super.newApplication(cl, MyTestApplication::class.java.name, context)
    }
}این پروژه TestRunner رو میشناسه و نیاز داره تا استفاده بشه وقتی که instrumentation تست ها اجرا میشن. چون این در فایل app/build.gradle مشخص شده:app/build.gradle...
android {
    ...
    defaultConfig {
        ...
        testInstrumentationRunner &amp;quotcom.example.android.dagger.MyCustomTestRunner&amp;quot
    }
    ...
}
...استفاده از تست های instrumentation در  Daggerما باید MyTestApplication رو پیکربندی کنیم تا از Dagger استفاده کنه. برای تست های integration، ساختن TestApplicationComponent تمرین خوبی هست تا منظور رو برسونه. ساختن (production) و تست کردن  از پیکرندی کامپوننت های متفاوتی استفاده میکنند.تفاوت بین پیکربندی تست و پیکربندی ساختن (production) ما چیه؟ بجای استفاده از SharedPreferencesStorage در UserManager، ما میخوایم از FakeStorage استفاده کنیم. چه چیزی برای ما SharedPreferencesStorage? StorageModule رو ایجاد میکنه. ما باید StorageModule رو با کلاس دیگه ای جایگزین کنیم که از FakeStorage استفاده میکنه. تا زمانی که این فقط در تست های instrumentation  نیاز هست، ما کلاس دیگه‌ای در پوشه androidTest میسازیم. بعد پکیج جدیدی به نام di ایجاد میکنیم درون app/src/androidTest/java/com/example/android/dagger/.app/src/androidTest/java/com/example/android/dagger/di/TestStorageModule.kt// Overrides StorageModule in android tests
@Module
abstract class TestStorageModule {

    // Makes Dagger provide FakeStorage when a Storage type is requested
    @Binds
    abstract fun provideStorage(storage: FakeStorage): Storage
}به دلیل چگونگی کارکرد Binds@ ، بجای ساختن متد در  SharedPreferencesStorage به عنوان پامتر، برای TestStorageModule،ما FakeStorage به عنوان پارامتر پاس میدیم. که اون TestAppComponent رو میسازه که پیاده سازی Storage بعدی رو انجام میده.کتابخونه Dagger نمیدونه چجوری میتونه نمونه ای از FakeStorage ایجاد کنه، مثل همیشه: FakeStorage.ktclass FakeStorage @Inject constructor(): Storage { ... }حالا وقتی Dagger نوع Storage رو درخواست میده ما نمونه ای از FakeStorage  رو فراهم میکنیم. زمانی که هم روی پروداکشن و هم روی تست از پیکربندی کامپوننت متفاوتی استفاده میکنیم، ما باید کامپوننت دیگه ای بسازیم که مانند AppComponent رفتار کنه. ما اسمش رو میذاریم TestAppComponent.فایل رو در این مسیر میسازیم:app/src/androidTest/java/com/example/android/dagger/di/TestAppComponent.kt@Singleton
@Component(modules = [TestStorageModule::class, AppSubcomponents::class])
interface TestAppComponent : AppComponentهمچنین ما نیاز داریم همه ماژول (module) ها رو براش مشخص کنیم.دور ازTestStorageModule، ما همچنین باید ماژول AppSubcomponents را شامل (include) کنیم که اطلاعاتی درباره ساب‌کامپوننت ها اضافه میکنه. از اون جا که به Context احتیاج نداریم برای برای تست گراف مون (تنها وابستگی که به کانتکست احتیاج داره همون SharedPreferencesStorage بود) نیازی به ساختن factory برای  TestAppComponent نیست.اگه شما پروژه رو build کنید (علامت چکش)، MyTestApplication  خطای کامپایل (compilation error) رخ میده. چون شما باید یک نمونه ای از userManager کلاس حذف کنید. همچنین شما خواهید دید که dagger ـ implementation رو برای TestAppComponent نمیسازه، بلکه کلاس DaggerTestAppComponent با گراف تست میسازه. بخاطر این که kapt  در  پوشه androidTest کار نمیکنه. شما باید آرتیفکت (ترجمه) حاشیه گذاری های پردازنده Dagger مانند قطعه کد زیر به androidTest اضافه کنید.app/build.gradle...
dependencies {
    ...
    kaptAndroidTest &amp;quotcom.google.dagger:dagger-compiler:$dagger_version&amp;quot
}حالا اگه پروژه تون رو sink کنید و بعد build کنید، DaggerTestAppComponent در دسترس خواهد بود، اگه هنوز در دسترس نیست دلیلش اینه که هنوز کاری با androidTest نداره. سعی کنید تا تست های instrumentation رو انجام بدید، با کلیک راست کردن روی درون پوشه androidTest و کلیک بر روی Run &#x27;All Tests&#x27; (البته دستی هم تک تک میتونید دکمه تست رو بزنیم !!)بعدش باید چند تا تغییر درMyApplication انجام بدیم تا به MyTestApplication اجازه بدیم تا کامپوننت Dagger خودش رو بسازه.MyApplication.ktopen class MyApplication : Application() {

    val appComponent: AppComponent by lazy {
        initializeComponent()
    }

    open fun initializeComponent(): AppComponent {
        return DaggerAppComponent.factory().create(applicationContext)
    }
}حالا ما میتونیم در MyTestApplication که ارث‌بری میکنه از MyApplication از TestAppComponent استفاده کنیم:MyTestApplication.ktclass MyTestApplication : MyApplication() {

    override fun initializeComponent(): AppComponent {
        // Creates a new TestAppComponent that injects fakes types
        return DaggerTestAppComponent.create()
    }
}توجه:اگه نمیتونید DaggerTestAppComponent رو import کنید، تلاش کنید تا تست های instrumentation رو اجرا کنید تا چیز هایی که درونش هست رو مشخص کنه. وقتی که پروژه نمیتونه تست های android رو پیکربندی کنه،  کلاسی هم نمیسازه.خوب دوستان توجه:توجه:من برای پیاده سازی این قسمت (تست نویسی) چیزی بیش از ۱۰ ساعت وقت گذاشتم!! چون فایل های پروژه نیازی به تغییرات بود که من نمیدونستم چجوری باید اون ها رو برطرف کنم. و خوندن و انجام دادن این لینک و این لینک کمک کرد تا بتونم این قسمت رو هم پیاده سازی کنم (چون متن نوشته ام خیلی طولانی شده به اون ها نمی پردازم) اما در این جد بگم سیستم ساختن sharedTest که توی پروژه وجود داره کاربردی نیست و میتونید بجاش از یک ماژول (ماژول های اندروید) جدید استفاده کنید. همون دو تا رو بخونید کاملا توضیح داده و کد ها هم هست، اون دو تا لینک مربوی به pull request های همین پروژه گیت هاب هست.درس ۱۵ | حاشیه گذاری Provides@ و Qualifier هاترجمه Qualifierیکی دیگه از حاشیه گذاری های پروژه که میتونه پر‌کاربرد باشه توی پروژه provides هست:حاشیه گذاری Provides@بجز Inject@ و Binds@ ، میتونید از Provides@ استفاده کنید تا به dagger بگید چجوری نمونه جدیدی  بگیره از کلاس های درون ماجول های dagger.نوع خروجی توابع Provides@ (مهم نیست که چجوی صدا زده میشه) به dagger میگه که چه نوعی به گراف اضافه شده. پارامتر های تابع، وابستگی هایی هست که dagger به اون ها نیاز داره تا بتونه اون ها رو برای این تابع فراهم کنه. در مثال ما میتونیم پیاده سازی provide رو هم برای Storage داشته باشیم:StorageModule.kt@Module
class StorageModule {

    // @Provides tell Dagger how to create instances of the type that this function 
    // returns (i.e. Storage).
    // Function parameters are the dependencies of this type (i.e. Context).
    @Provides
    fun provideStorage(context: Context): Storage {
        // Whenever Dagger needs to provide an instance of type Storage,
        // this code (the one inside the @Provides method) will be run.
        return SharedPreferencesStorage(context)
    }
}شما میتونید از حاشیه گذاری Provides@ در dagger استفاده کنید تا به dagger بگید چجوری فراهم کنه:پیاده سازی interdace (گرچه Binds@ پیشنهاد میشه، چون کد کمتری میسازه و بهره‌وری بیشتری داره)کلاس هایی که برای خود پروژه شما نیست (مثلا Retrofit)واجد شرایط ها Qualifiersما نباید از واجد شرایط ها توی برناممون استفاده کنیم، تازمانی که برنامه مون به همین سادگی که هست، هست. واجد شرایط ها موقعی استفده میشن که ما میخوایم استفاده دیگه ای از یک نوع(data type) توی پروژمون داشته باشیم توی گراف برناممون. برای مثال ما میخوایم Storage دیگه ای توی برناممون داشته باشیم که میخوایم فراهم بشه، میتونیم با گذاشتن کیفیت ساز اون ها رو از هم تمییز بدیم.برای مثال اگه بخوایم توی SharedPreferencesStorage یک نام رو به عنوان ورودی بگیریم:SharedPreferencesStorage.ktclass SharedPreferencesStorage @Inject constructor(name: String, context: Context) : Storage {

    private val sharedPreferences = context.getSharedPreferences(name, Context.MODE_PRIVATE)

    ...
}میتونیم پیاده سازی های متفاوتی با Provides@ در StorageModule داشته باشیم. ما میتونیم از واجد شرایط استفاده کنیم تا  نوع پیاده سازی رو مشخص کنیم:StorageModule.kt
@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class RegistrationStorage

@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class LoginStorage

@Module
class StorageModule {

    @RegistrationStorage
    @Provides
    fun provideRegistrationStorage(context: Context): Storage {
        return SharedPreferencesStorage(&amp;quotregistration&amp;quot, context)
    }

    @LoginStorage
    @Provides
    fun provideLoginStorage(context: Context): Storage {
        return SharedPreferencesStorage(&amp;quotlogin&amp;quot, context)
    }
}در این مثال ما محافظت کردیم از دو واجد شرایط: RegistrationStorage و LoginStorage که میتونیم از حاشیه گذاری Provides@ استفاده کنیم. ما دو نوع از Storage در گراف برنامه داریم. RegistrationStorage و LoginStorage . که هر دو متد خروجیش Storage هست، که پارامتر ها (وابستگی های) یکسانی دارند. اما نام های متفاوتی دارند. چون نام در توابع Provides@ هیچ خاصیتی نداره. و ما باید اون ها رو از گراف برنامه بازیابی کنیم.استفاده از واجد شرایط به صورت زیر است:چجوری واجد شرایط رو از بازیابی کنیم به عنوان وابستگی// In a method
class ClassDependingOnStorage(@RegistrationStorage private val storage: Storage) { ... } 

// As an injected field
class ClassDependingOnStorage {

    @Inject
    @field:RegistrationStorage lateinit var storage: Storage
}شما میتونید چند خاصیت مشابه هم به عنوان واجد شرایط با استفاده از @Named annotation بسازید. گرچه واجد شرایط ها پیشنهاد میشن چون:میتونید اون ها رو در Proguard یا R8 استفاده کنیدشما نیاز ندارید تا نگهدارید ثابت (constant)ی برای مچ کردن نام هااون ها میتونن مستند (document) بشندو فصل آخر | ۱۶و۱۷فصل ۱۶ میگه تلاش کنید کد هایی به پروژه اضافه کنید (این هم فکر کنم توی pull request ها بود) اگه دوست دارید ببینید.فصل ۱۷ هم داره میگه ایول به شما که تونستید تموم کنید این بخش رو :))پس ایول به شماسخن پایانی | کلا تو وب فارسی ما محتوای تخصصی خوبی نداریم!! همه جا پر شده از این که چجوری پول دار بشیم، کلا کسی حوصله نمیکنه محتوای عمیق بسازه و از این حرف ها، پیشنهاد میکنم که بیکار هستید برای افزایش علم جمعی شما هم بنویسید.اگر هم وبلاگ دارید و یا دوستانی دارید که مینویسن این حتما کامنت بذارید تا بخونیم و یاد بگیریم :))نوشتن این متن و انجام داد کد ها همراهش تقریبا یه هفته وقت گرفت! البته تنبلی و این ها هم داشتم! ولی اگه خواستید حمایتی از بنده بکنید، فقط این محتوا رو برای دوستانتون به اشتراک بگذارید. بیشتر دیده شدن، تنها انگیزه من برای نوشتن هست. امیدوارم از این نوشته لذت بره باشید. اگه کم و کسری داشت ببخشید :)))میتونید آخرین نرم افزاری که ساختم رو از این جا دانلود کنید.برنامه با من درس بخون</description>
                <category>مهدی معروف به سقراط مناطق محروم :)</category>
                <author>مهدی معروف به سقراط مناطق محروم :)</author>
                <pubDate>Fri, 05 Jul 2024 00:19:12 +0330</pubDate>
            </item>
                    <item>
                <title>این قسمت، افسانه های ناتمام شروع نشده!</title>
                <link>https://virgool.io/@m_11008415/%D8%A7%DB%8C%D9%86-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%81%D8%B3%D8%A7%D9%86%D9%87-%D9%87%D8%A7%DB%8C-%D9%86%D8%A7%D8%AA%D9%85%D8%A7%D9%85-%D8%B4%D8%B1%D9%88%D8%B9-%D9%86%D8%B4%D8%AF%D9%87-i7rpaqfq8ofa</link>
                <description>از اون جا که قرارمون این شد که قدم به قدم کار هایی که تو جامعه اوپن سورس (open-source) میکنم رو این جا به اشتراک بگذارم، امروز روز دوم هست، اگه قسمت قبلی رو نخوندید پیشنهاد میکنم بخونید: این لینکعکس هکری :))خوب امروز به ذهنم رسید برای این که بتونم مرور کنم که تکلونوژی های مختلف اندروید چجوری کار میکنه، بعد از اون جا که ساخت برنامه هایی مثل to−do خز شده بود، گفتم بیام و یه چالش جالب تر رو انجام بدم.گفتم و بیام جعبه لایتنر بسازم، چند وقت پیش یکی از دوستان میخواست زبان بخونه اما هیچ اپ رایگان و خوبی برای جعبه لایتنر پیشنهاد نداد، منم بهش اپ اوپن سورس انکی (Anki) رو پیشنهاد دادم اما خیلی باهاش حال نکرد، گفتم این ایده خوبیه برای این که بیام شروع کنم.تازه چون باید خیلی چیز ها تو خود اندروید مدیریت بشن (زمان خوندن فلش کارت ها و این ها) دیدم چالش خوبیه تا با مفاهیمی که قبلا کار نکردم کار کنم. مثلا استفاده از WorkMangaer و چیز های دیگه. مثلا من برای تزریق وابستگی (dependency injection) از Koin استفاده کرده بودم اما میخواستم از Dagger هم استفاده کنم تا ببینم که چجوری میتونم از این هم استفاده کنم (تو بیشتر اپ هایی که دیدم از Dagger استفاده میکنند)چرا داری برا خودت الکی پروژه میزنی؟با این که من شاید بیشتر ۶ تا اپ نوشتم اما به نظرم هیچکدومشون اصولی نبودن و میخوام یه برنامه خوب و اصولی بنویسم و با چالش های اون روبرو بشم. البته چون من مدتی بود که از برنامه نویسی اندروید دور بودم تصمیم گرفتم که برای مرور هم که شده پروژه بزنم.(درگیر درس های دانشگاه و خوندن برای کارشناسی ارشد بودم) یه جمله ای رو میخوندم قبلا خیلی جالب بود، میگفت:دانشی که در بازو نیست، افسانه ای بیش نیست!!چرا داری هنوز ادامه میدی به خوندن!!! حداقل یه ۱۰ ثانیه تأمل کنید:)توضیح پروژه پروژه رو میتونید تو گیت هاب من پیدا کنید، در این لینکتا الان فقط view های برنامه رو ساختم، چرا با view کار میکنم و jetpack compose  نمیزنم، چون هم مرور کنم و هم این که بیشتر پروژه های بزرگ هنوز هم روی view هستند و مثل همون DuckDuckGo البته من compose هم کار کردم اما برای همین.توی این جا میتونید view ها رو ببینید. تا الان فقط ۵ تا view بسیار ساده ساختم و هنوز براش theme و نه style و font ست نکردم، ایده ام این هست که بجای صرف زمان برای درست کردن یه UI قشنگ میتونم جزییات تکنیکی بیشتر یاد بگیرم و سریع تر این پروژه رو تموم کنم. و به قول یه ضرب المثلی که میگه:Sometime less is more!یعنی &quot;بعضی وقت ها زیبایی در سادگیه&quot; (نقل به مضمون!)تازه چون میخواستم این وبلاگ رو هم بنویسم، میخواستم یه چیزی باشه که زیاد درگیر زیبایی و این ها نشه و سعی کنه که بیشتر روی کارایی (Performance) توجه کنه.توی MainActivity هم فعلا کار خاصی نکردم، فقط یه چیز تستی نوشتم تا بتونم view هایی که نوشتم رو تست کنم.کلا قراره برنامه دو تا فیچر داشته باشه، خوندن کارت ها و اضافه کردن کارت (لایتنر چیست؟) قراره که تو این برنامه از کتابخونه Room استفاده کنم و از Modern Android App Architecture که توی سایت خود اندروید هست. و همون طور که گفتم برای تزریق وابستگی هم از Dagger. امروز هم این مقاله رو که درباره این بود که لایه UI باید چجوری باشه خوندم. و نکته جالبی بود که نمیدونستم و توی این قسمت و این قسمت از برنامه پیاده سازی کردم.اگه ایده یا سخنی هست، خوشحال میشم که توی گیت هاب برام بنویسید، اگه تازه کار هستند اصلا مشکلی نیست، برید توی قسمت issue ها و هرچی دلتون میخواد بنویسید، اینجا =================میخوام سعی کنم تا جایی که میتونم این پروژه رو با داکیومنت بنویسیم، ببینم چی میشه :)اگه نکته ای چیزی هم هست که دوست دارید اشاره کنید، حتما بگید خجالت نکشید!اگه کتاب یا منبع خوب میشناسید، بسیار خوشحال میشم معرفی کنید.</description>
                <category>مهدی معروف به سقراط مناطق محروم :)</category>
                <author>مهدی معروف به سقراط مناطق محروم :)</author>
                <pubDate>Sun, 17 Mar 2024 21:03:31 +0330</pubDate>
            </item>
                    <item>
                <title>چجوری کد های اندروید بخونیم؟!</title>
                <link>https://virgool.io/@m_11008415/%DA%86%D8%AC%D9%88%D8%B1%DB%8C-%DA%A9%D8%AF-%D9%87%D8%A7%DB%8C-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-%D8%A8%D8%AE%D9%88%D9%86%DB%8C%D9%85-li2b0evbazyr</link>
                <description>نوشته که بدون عکس نمیشه، میشه؟!این سوالی بود که مدت ها ذهن منو درگیر کرده بود. دوست داشتم روی یه پروژه بزرگ منبع باز (Open-source) مشارکت کنم. اما نمیدونستم چجوری. خودم برای خودم کد میزدم و تجربه کار کردن تو شرکت رو نداشتم، خلاصه که یه روز تصمیم گرفتم این کار رو بکنم. رفتم توی گیت هاب و دل و زدم به دریا، بعد از چند باری که خواسته بودم توی این فضا کار کنم و چند تا پروژه هم حتی clone کرده بودم اما نمیتونستم حتی ازش سر دربیارم. اما واقعا پروژه هایی مثل پیامرسان سیگنال واقعا داستان داشت، چون فیلتر بود نمیتونستی متوجه بشی که یه باگ هست توی نرم افزار یا تو توی یه باگ بزرگتر داری دست و پنجه نرم میکنی!!تازه کار کردن روی پیام رسان داستان های زیادی داشت، باید برای تست کردن فیچر ها مخ یکی رو میزدی تا بیاد باهات چت کنه یا تماس بگیره تا بتونی فیچر ها رو تست کنی.این جا بود که درس اول رو گرفتم،۱− پروژه درست درمون پیدا کن مشتی خوب بعدش با خودم فک کردم که چیکار کنم چیکار نکنم، بیام دنبال یه پروژه خوب بگردم، رفتم دیدم خوب من به چی علاقه دارم؟ اندروید بعدش گفتم خوب حالا مثلا میخوام یه پروژه بروز تر و خفن تر کار کنم نه یه پروژه قدیمی، تا با تکلونوژی های روز آشنا بشم. یادمه یکی از دوستان لینکدین بهم گفت پروژه باید بزرگ باشه و چندین نفر روش کار کنن، خودمونی بگم باید پول دربیار و بزرگ باشه، تا بعدا از امکاناتی که توش هست بتونی استفاده کنی.چه امکاناتی؟؟اها این قسمت مهم داستان هست، مهم ترین چیز نتورک هست، شما با چندین آدم آشنا میشید که از شما خفن تر هستن و اون قدر مهربون هستن که بتونید ازشون چیز یاد بگیرین.یکی دیگه از اون امکانات، فهمیدن این هست که یه کد بزرگ که چندین هزار یا حتی میلیون نفر دارن ازش استفاده میکنن چجوری کار میکنه. این باعث میشه سطح شما هم تا اون پروژه بالاتر بره، اگه چیزی رو هم متوجه نشدید میتونید یاد بگیرید و سرچ کنید، حتی اگه باز هم اطلاعات بیشتر میخواید میتونید از خود برنامه نویس های اون جا بپرسید. (این جوری هست که نتورک خوب پیدا میکنید، قبلا نمیدونستم چقدر این داستان مهمه، الانم شاید ندونم اما میدونم که از قبلا برام مهم تر شده این قضیه)دیگه بقیه چیز ها رو هم که خودتون میدونیدرزومه خوب و توانایی این که میتونید با بقیه خوب کار کنید و باز شدن موقعیت های کار جهانی و استخدام توی همون شرکت ها و گرفتن پروژه از همون نتورکی که ساختید و (منظورم ساید پراجکت اون هاست) و غیره رو که فک کنم همه جا دربارش شنیدید و خوندید (راستش منم خوندم اما فعلا هنوز اول راهم)داستانی کوتاه برای رسیدن به درس دوم منخوب دیدم بعد از همه این داستان ها چه چیز هایی برام مهمه، مثلا پروژه باید جدید باشه حتما با Kotlin باشه و آخرین پیام نوشته شده تو issue ها حد اکثر مال هفته پیش باشه و امنیت برام مهمه، پروژه باید سعی کنه امنیت کاربران رو حفظ کنه و این ها.در نهایت رسیدم به اپ اندروید DuckDuckGo من با داک داک گو رابطه خیلی خوبی داشتم و مدت ها مرورگر های مختلف استفاده میکردم و سرچ انجین های مختلف رو تست میکردم، یکی از همین ها داک داک گو بود، و چون من از Tor هم استفاده میکردم گاهی اوغات که دسترسی به اینترنت کار سختی میشد با این سرچ انجین آشنا شدم. اسم باحالش منو جذب خودش کرد و کلا دوست داشتم ببینم توی یه مرورگر که ادعای امنیت میکنه چی میگذره (بعدا که داستان اش رو خوندم خیلی بیشتر هم باهاش حال کردم) جدا از این ها پروژه خیلی cool و خفنی هم بود، طنز هستن همه چیز ها و رابط کاربری خیلی خوبی داره (آنبرینگ اش رو هم خیلی دوست داشتم) و میدونید حس یه مرورگر نرمال رو نمیده، شما چندین ابزار جالب دیگه هم توش دارید. بذار لینک اش رو هم بذارم، آها اینا پیداش کردم :) لینک گیت هاب داک داک گو یا DuckDuckGoو یه باگ خیلی ریز پیدا کردم (یه لینک تغییر کرده بود و حالا  ۴۰۴ میداد) این issue از فرصت استفاده کردم و زیرش نوشتم که منو راهنمایی کنند تا بتونم توی این پروژه مشاکت کنم،آماده درس دوم هستید؟۲− حاجی زبان خیلی مهم تر از اون چیزی هست که فکر میکنیدتوضیح لازمه؟؟ همه با هم میگویند: نععععععععععععععع نیسسسسستتتتتبرو بعدیبعدش بهم لینک داکیومنت خود گوگل رو داد.۳− آدم های معمولی دوره میخرند آدم های لجند داکیومنت میخونندرس سوم این هست که محتوای تصویری، صوتی و غیره همه زمان بر هستند و اما مقالات و مستندات همه به روز هستند و منطبق با آخرین استاندارد ها هستند. پس برای این که این همه جستجو نکنید سر دوره ها برید بخونید، خوندن خیلی مهمه. راستی نترسید. از این که متوجه نشید یا مطلب رو درک نکید یا هر فکر دیگه ای.فقط برید بخونید و همین جوری بخونید، لازم نیست همشو هم بخونید بخشیش رو بخونید که به نظرتون طبق تجربه مهمه، سینتکس ها و مقدمات، اگه بخش quick start داره حتما اون رو اول مطالعه کنید. یه چیز تو پرانتز بگم (همیشه تو طول تاریخ دو دسته آدم بودند، اون هایی که دوست داشتند همه بخونند و آگاه بشن و عده ای که دوست نداشتن بقیه بخونند، خلاصه این که با خوندن دوستی کنید، دوست خوبیه)× نکته: اگه زبانتون خوب نیست، با همین داکیومنت خوندن میتونید تقویتش کنید ( توی پادکست بایوکست، داشتم زندگی نامه جوزف استالین رو گوش میکردم، دیدم اون هم زبان های دیگه رو با خوندن کتاب ها یاد میگرفت، پس زیاد درگیر گرامر و این ها نباشید، شما که نمیخواید کنکور زبان بدید، شما فقط میخواید که منظور جمله رو متوجه شید، سعی هم کنید از دیکشنری انگلیسی به انگلیسی استفاده کنید)۴− بعضی وقت ها از یه قدم عقب تر شروع کنید، ببینید داستان چیه!!خوب، این چی میگه،من وقتی داشتم مستندات خود اندروید رو میدیدم، اسم یه پروژه زیاد میومد تو چشمم، این پروژه بعد گفتم برم این رو بخونم ببینم چه خبره،خلاصه این که شروع کردم به دیدم این پروژه و کلی چیز یاد گرفتم، مهم ترینش این که اصولی بودن چیز ها چجوری تعریف میشه تازه در کنارش هم دیدم برای خوندن یه پروژه کوچیک ساده چیکار میکنم.درس زندگیآدم ها برای انجام دادن چیز های بزرگ گارد دارند، همش دوست دارند که چیز های کوچک و ساده رو انجام بدن، وقتی نوبت به انجام کار های بزرگ میرسه سر درگم میشن که باید چیکار کنن و چیکار نکنن، پس راه حل این هست که کار های بزرگ رو به کار های کوچک تقسیم کنیم و یکی یکی انجامش بدیم. شاید معنی جمله بزرگ فکر کن و کوچک قدم بردار همین باشه، کی میدونه؟از این به بعد میخوام هر روز از کار هایی که برای این که توی DuckDuckGo میکنم تو ویرگول چیز بنویسم (امیدوارم بتونم به این داستان برسم و مثل دفعه های قبلی ول نکنم)نظر انتقاد پیشنهاد ها تون هم گوش نمیدم :))))))))شاد باشید و خرم </description>
                <category>مهدی معروف به سقراط مناطق محروم :)</category>
                <author>مهدی معروف به سقراط مناطق محروم :)</author>
                <pubDate>Sat, 16 Mar 2024 17:00:43 +0330</pubDate>
            </item>
                    <item>
                <title>پشته یا همون Stack به زبون جاوا (زبون آدمیزاد 😈)</title>
                <link>https://virgool.io/@m_11008415/%D9%BE%D8%B4%D8%AA%D9%87-%DB%8C%D8%A7-%D9%87%D9%85%D9%88%D9%86-stack-%D8%A8%D9%87-%D8%B2%D8%A8%D9%88%D9%86-%D8%AC%D8%A7%D9%88%D8%A7-%D8%B2%D8%A8%D9%88%D9%86-%D8%A2%D8%AF%D9%85%DB%8C%D8%B2%D8%A7%D8%AF-jiiewzakyoir</link>
                <description>​سلام سلام صد تا سلام به تو ستاره (مفهوم استعاری از ستاره میباشد :)خوب بریم ببینیم Stack چی میگه.‫پشته‬‫‪،‬‬ ‫ساختمان‬ ‫داده‬ ‫اي‬ ‫است‬ ‫كه‬ ‫حذف‬ ‫و‬ ‫اضافه‬ ‫از‬ ‫بالاي‬ ‫آن‬ ‫انجام‬ ‫مي‌شود‬ ‫‪.‬‬‫پشته‬‫ را FILO ‫مي‌نامند‬ ‫‪،‬‬‫چون‬ ‫آخرين‬ ‫عنصر‬ ‫وارد‬ ‫شده‬ ‫در‬ ‫آن‪،‬‬ ‫اولين‬ ‫عنصري‬ ‫است‬ ‫كه‬ ‫از‬ ‫آن‬ ‫برداشته‬ ‫مي‌شود‬ ‫‪.‬‬‫‪top‬‬‫‪‬‬ ‫مشخص‬ ‫كننده‬ ‫عنصر‬ ‫بالايي‬ ‫پشته میباشد‬:Stackمطابق شکل ابتدا ما یک پشته خالی داریم. بعد با هر عمل push (یعنی هل دادن- شما میتونید اضافه کردن به پشته در نظر بگیرید) ما یک عنصر (که جلوتر توی کد ما بهش item) اضافه میشه. و هربار به بالای قبلی اضافه میشه. و با هر بار عمل pop اون ها از بالا به ترتیب خاج میشن. راستی O این عبارت هم از مرتبه (1)O هست.برای درک بهتر میتونیم اون رو به دسته از کتاب تعمیم بدیم که روی هم قرار دارن. برای برداشتن باید چیکار کنیم؟ آفرین یکی از بالا برداریم.به شکل زیر نوجه کنید:کتاب های روی همخیلی وقت ها این مفهوم توی برنامه نویسی هم استفاده میشه. مثلا وقتی که تابع بازگشتی داریم. یا مثلا برای تبدیل postfix ، infix و prefix (شاید برای این ها هم مطلب نوشتم، مفاهیم ساده ای دارند)خوب حالا بریم با زبون برنامه نویسی جاوا (Java) ببینیم چطوری این سیستم کار میکنه (نگران این که جاوا بلد نیستید نباشید، مفهوم رو بگیرید. تازه اون رو هم ساده میگم که در کنارش جاوا هم یادبگیرید.)بریم؟؟‌ (یه لبخند، نفس عمیق، یه قلب قهوه، چایی، آب) رفتیم که بریم.خب من اولش یه interface ساده ساختم برای ساده سازی کار (interface موقعی به کار میره که ما بخوایم ایده بهتری از کارهامون داشته باشیم که مثلا میخوایم چیکار کنیم، بعدا دربارش یه وبلاگ کامل مینویسم)public interface StackInterFace {
    
        public boolean push (int item); // add item to stack
        public int pop();       // remove the last item and push back 
        public void stackIsFull();      // when stack is full call
    }کدی که نوشتم یعنی من تابع push، pop و پشته ام پر هست رو دارم و باید از این ها استفاده کنم (این ها بیشتر برای خوانایی کد هست و این که یادم نره باید چه توابعی رو پیاده سازی کنم. البته هزار تا دلیل دیگه هم داره ولی به همین بسنده میکنم)قسمت بعدی داستان ساختن یک کلاس جدید به اسم پشته هست (اما نیازی نبود، صرفا با این کار کار خودمو راحت کردم، که اگه بعدا خواستم از این مفهوم استفاده کنم راحت باشم) نترسین خیلی وارد داستان شی گرایی نمیشیم، مفهوم رو بگیرید:)public static class Stack implements StackInterFace {

        private int length = 0; // length of stack
        

        private int[] stack;
        private int top = -1;


        // constactor
        public Stack (int length ){
            this.length = length;
            stack = new int[length];
        }

        //gettr for stack
        public int[] getStack(){
            return stack;
        }
    }خوب برای این که ما بتونیم از توابعی که از پیش تو interface تعریف کزدیم استفاده کنیم باید از استفاده implements تا به کلاس مورد نظر بگیم که داداش تو حتما باید اون توابع از پیش تعریف شده رو داشته باشی. (پیاده سازی اون توابع در کد بالا نیست و جلو تر میبینیم)چیز هایی که تو این کلاس تعریف شده:تعریف متغیر اندازه، برای تایین ماکزیمم خانه هایی که پشته دارد.یک لیست که بیانگر پشته هست.متغیر top که نشون میده اندیس بالایی ترین خانه پشته چند هستتوی تابع (متد) سازنده، تعداد خانه ها رو همون زمانی که یه شی (object) جدید ساخته میشه تعیین میکنیم و لیست پشته رو با اون خانه ها تنظیم میکنیمو درنهایت با توجه به این که متغیر stack یک لیست خصوصی (private) است (به دلیل محصورسازی) برای آن متدی برای دریافت تعیین میکنمخوب حالا باید توی کلاس Stack که تعریف کردیم اون سه تا تابع رو بیاریم:این جوری (Override@ برای این هست که ما داریم اون رو دوباره مینویسمش، جون از interface داریم اون رو میاریم)خوب از چک کردن برای این که تابه پر هست یا نه شروع میکنیم:@Override
    public void stackIsFull() {
        System.err.println(&amp;quotStack is full.&amp;quot);
    }همون طور که میبینید، بسیار ساده هست، اگه پر بود ارور میده و میگه پشته پر شده.حالا نوبت متد push هست، اون هم بسیار راحته :@Override
    public boolean push(int item) {
        if (top &gt;= length-1 ){
            stackIsFull();
            return false;
        }
        top++;
        stack[top] = item;
        return true;
    }اول چک میکنه که ببینه پر شده پشته یا نه (اگه پر شده بود، تابع پر بودن تابع رو صدا میزنه) و برامون خروجی false رو برمیگردونه، اگه خونه خالی داشتیم که بتونیم آیتم رو اضافه کنیم یکی به نشون دهنده این که بالاترین عنصر (یعنی همون top) اضافه میکنیم و به پشته اون عنصر رو اضافه میکینم. و تمام.حالا آخرین مرحله مون مونده که باید بهش برسیم و اون هم چیزی نیست بجز pop :@Override
    public int pop() {
        if (top==-1){
            return -1;
        }
        int item = stack[top];
        stack[top] = 0;
        top--;
        return item;// first return the top and then  (top -1 )
    }خوب این هم بسیار ساده هستاول چک میکنه که پشته خالی هست یا نه، بعد متغیر جدیدی به اسم item میسازه و اون عنصر مورد نظر توش میذاره، جای اون عنصر تو پشته صفر میذاره، از top که نشون دهنده اندیس بالاترین خونه بود رو یکی کم میکنه و در آخر هم خروجی رو برمیگردونه.خوب من برای تست که تابع تست هم نوشتم(سعی کنید اعداد و این ها رو تغییر بدید تا ببینید چی میشه)public static int[] testStack(){

        Stack stack = new Stack(5);
        stack.push(1);
        stack.push(2);
        stack.push(3);
        stack.push(4);
        stack.pop();
        stack.push(5);
        stack.pop();
        stack.push(6);
        stack.pop();
        stack.push(7);
        stack.pop();

        return stack.getStack();
    }خوب درود بر ما که تونستیم به همین راحتی یه کد جالب و پرکاربرد توی برنامه نویسی بزنیم که توی ساختمان داده ها بسیار پرکاربرد و مهمه.نمایش کلی برنامه رو میتونید توی گیت هاب من پیدا کنید.برای دیدن کد ها کلیک کنید.​​میتونید عین همین متن رو تو وبلاگ شخصی من هم بخونید (همینه اما ژانگولرش بیشتره :)</description>
                <category>مهدی معروف به سقراط مناطق محروم :)</category>
                <author>مهدی معروف به سقراط مناطق محروم :)</author>
                <pubDate>Mon, 11 Dec 2023 08:52:50 +0330</pubDate>
            </item>
                    <item>
                <title>سنگ نخورد به سرم، سنگ و زدم به سرم!</title>
                <link>https://virgool.io/@m_11008415/%D8%B3%D9%86%DA%AF-%D9%86%D8%AE%D9%88%D8%B1%D8%AF-%D8%A8%D9%87-%D8%B3%D8%B1%D9%85-%D8%B3%D9%86%DA%AF-%D9%88-%D8%B2%D8%AF%D9%85-%D8%A8%D9%87-%D8%B3%D8%B1%D9%85-miwejzraqgyy</link>
                <description>دیروز یه نفر (انگلیسی زبان) تو لینکدین بهم پیام داد که داره دنبال یه شخص برای همکاری میگرده و منم که مدت ها بیکار بودم، اصلن همیشه خدا بیکار بودم و هیچ وقت نتونستم از حوزه برنامه نویسی کسب درامد کنم (البته بجز یه دوره پایتون که خیییلی وقت پیش منتشر کردم و یه درآمد ناچیز داشت که رایگانش کردم :)ولی همیشه عاشق تکلونوژی بودم و هستم، تقریبا توی هر زمینه ای که به نظرم جالب بود یه ناخونکی زدم و این باعث شده به قول بعضی ها اقیانوسی به عمق یک سانتی متر بشم (حالا اقیانوس و که همه میگن ولی شما تا حد یه جوب حساب کن)خیلی وقت ها پیش خودم فکر میکنم چرا نمیرم دنبال یه کاری که بتونم از کاری که ازش لذت میبرم کسب درامد کنم، شاید میترسم، که شاید اون موقع برای پول کار کنم و لذتی که حل مساله برام داره، یا شاید هم نه میترسم که وقتم و رو خیلی بگیره و نتونم کار هایی که واقعا دوست دارم رو انجام بدم (آره البته که کد زدن و حل مشکلات رو دوست دارم اما به نظرم خیلی آدم رو تک بعدی میکنه، شاید من دوست داشته باشم که یه کتاب جدید بخونم اما مشغله اجازه نده ، آره مزخرفه! ولی شاید این حسیه که دارم بعضی وقت ها که ترس برت میداره حتی نمیتونی حالت رو توصیف کنی، قلبت که منجمد میشه، اون لحظه ای که باید تصمیم رو بگیری ... )یا شاید مهم تر از همه حس نا کافی بودن بهم دست میده، حس میکنم خیلی آدم های خفن تر و بهتری هستند که میتونند همون کار رو انجام بدن (آره این مزخرفه و گفتنش مزخرفانه تر به نظر میرسه)از اون طرف هم وقتی که از خر شیطون و بلا میای پایین و دو تا رزومه این طرف و اون طرف میفرستی میبینی این قدر شرکت ها دارای ضعف هستند که نگوبعد با خودت میگی من میخوام خفن ترین و بهترین باشم، این شرکت و این جایگاه که به راحتی مصاحبه رو بدون هیچ اطلاعی برگزار نمیکنه و اصلا یادش میره، اصلا ارزشش رو داره من برم اون جا شاید من تبدیل به همون تخم عقابی باشم که قل خورده اومده تو لونه مرغا و علی رقم همه استعداد و علاقه ای که دارم نتونم تبدیل به بهترین که بخوره تو سرم، تبدیل به ماهیت و وجود &quot;خودم&quot; بشم!بذارید جمله قصار بگم : سایه های بلند ترس این قدر بلند میشند تا تبدیل به تاریکی بشن، و این تاریکی برابر با نامیدی و افسردگیست (البته من افسرده نیستم و شاد و شنگول بودن هم تا حدی معروفم )اما به قول مولانانی حدیت راه پر خون میکند/ قصه های عشق مجنون میکندمحرم این هوش جز بیهوش نیست/مر زبان را مشتری جز گوش نیستهعی حالا بماند کجا بودیم ؟؟؟ آهابه خودت میای و میبینی که که میخوای هنرت رو (به قول اسیو جابز : ما هنرمندیم!) بیای و عرصه جهانی محک بزنی و ببینی چی میشه، از شانس خوبت (و البته نیمچه تصمیم های مثبت گذشته منظورمه) برات یه پیام میاد که آقا رزومتو دیدیم و خوشمان آمد. شما هم (البته کلمه بعدی یه دور از جون داره) خر میشید و رزومه رو میفرستید. شما یه دوست خفن دارید که توصیه های اون برای شما خیلی مفید بودهرزومتون رو میفرستید و با خودتون خوشحالید ...اووو نه نهههههههههچطور ؟؟؟؟زباااااااااااااااان!!!!!!!!!بعله عزیزان داستانیهو یادتون میوفته باید یه فکری به حال زبانتون بکنیدبعد میاید کل روز رو یه دوره زبان میبینیدهی میخواید هول هولکی چیز یاد بگیرید اما این جور چیز ها رو که نمیشه یه روزه اوکی کرد!همون شب (فردای روزی که رزومه عه (رقیق تلفظ کنید) رو فرستادید ) حدس بزنید چی شد ؟(جمله مزخرف:‌خر سوار مورچه شد، خیابان ساری پیاده شد :) هه هه هه (ملو بخوانید))چی شد ؟ طرف براتون یه لینک میفرستهیه ربات خر کله میاد باهاتون صحبت میکنه (به قول یکی از معلم هامون) البته شاید با خودتون بگید که خوب شانس آوردی مهدی حرف زدن با ربات که خیلی راحت تره اما، اما، اما امان از دل غافل !من فک کردم این پروتوتایپ چیزی هست که من باید نسخه اندرویدش رو بزنم!و دیدم داره سوال میپرسه و بهم آپشن میده گفت بزن بریم رو بزن منم زدم برا تستگفت زبانت چطورهمنم نرمال مث همیشه زدم داغون که حداقل آسون تر باهام صحبت کنهبعد پیام داد: مرسی از از همکاری شما بعد تموم ای خداعهحتی اسم منو هم نپرسیده بودمن هنوز این مربعی که جای نوشتن تکست بود و نمیشد توش چیزی نوشت رو درک نکرده بودم و میخواستم باگ بگیرم از برنامه گفتم مشکلی نیستمیام ریفرش میکنم درست میشهاگه ۰٫۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۱ درصد فکر میکنید که ریفرش شد باید بهتون بگم ریفرش شداما گفت این لینک منقضی شدهآقا/ خانم من هر تریکی بلد بودم زدم از تور استفاده کردم، بروزر رو عوض کردم، نوچ، نشد که نشد که نشد که نشدبه خودم اومدم و دیدم که چقدر مسخره آهنگ Lose yourself امینم در پستو های ذهنم پلی شدبعدش هم آهنگ Numb لینکین پارک(یه ۳۰ ثانیه ای به فکر فرو میرم، انگار هیچ کلمه ای به ذهنم نمیاد)راستش نوشتن این پست یه تراپی خیلی خوب بود واسم، الان که تقریبا همه احساساتم رو به عنوان یه درون گرا و با کلی غلط املایی و تحریری نوشتم، حس میکنم الان حالم خیلی بهترهشاید اگه دل شما هم گرفته، باید بیاید و بنویسید، شاید هم بدوید، این ها خیلی خوبهیه جمله ای رو تو یکی از پادکست های سهیل علوی شندیدم که میگفت: بچه که بودیم، میخواستیم دنیا رو تغییر بدیم، بزرگ که شدیم دیدم چقدر دنیا ما رو تغییر داد :\همین امروز چشم به جمله افتاد (منبعش هم میدونم اما نمیگم، چون شاید بعضی ها بگن بابا چقدر متنت پر شده از متن های این و اون) که میگفت: میگفت شکست بخورید، زیاد هم شکست بخورید اما رو به جلو شکست بخورید (برای کور کردن چشم بسی حسود این جمله از مکسول هست (این جمله با چشم های خط شده ، زبان دراز ، بینی دراز چروک افتاده و مو های پریشان تصور کنید ))من کسی هستم که شکست رو نمیتونم قبول کنم، از یه بازی شطرنج معمولی بگیر تا والیبال (چون گروهیه)اما متاسفانه درونگرایی (یا هر چیزی که اسمشو میذاری) من سبب شده که این حس خشم برگرده خودم!! (آرررررررره عمو مزخرفه اصن آدما مزخرفن)قصد نداشتم این همه مزخرف بنویسم (حالا خیلی ها میان میگن مزخرف نیست که ... الان یه عده دیگه ای با دیدن دقیقا همین میان نظرشون رو عوض میکنند و میان میگن مزخرفه!)خلاصه حالا که دارم مینویسم بذار بنویسم، منو از چی میترسونی ؟بعد از نگاه کردن به این چشمک زن (همون گوگوری مگوری که نشون میده کجا داری تایپ میکنی رو میگم)تصمیم میگریم که برم دستشویی!! (الان اگه از بچه های خوابگاه این رو بخونن میرن هوا)منظورم این هست که به نوشتن خاتمه بدمحتی نمیخوام این رو دوباره بخونم چه برسه به این که ادیتش کنمبذار همینوجوری که کلمات به ذهنم اومدن ، بمونه (تنبلم خودتی)\ ۲۹ دقیقه بامداد ۱۱ فروردین \الان شد ۳۰ فعلاراستی حال و حوصله ندارم عکس بذارم</description>
                <category>مهدی معروف به سقراط مناطق محروم :)</category>
                <author>مهدی معروف به سقراط مناطق محروم :)</author>
                <pubDate>Fri, 31 Mar 2023 00:32:28 +0330</pubDate>
            </item>
                    <item>
                <title>پردازش غیر همزمان با RxAndroid</title>
                <link>https://virgool.io/@m_11008415/rxandroid-i5tf1egqx3gs</link>
                <description>&quot;صرفا چون عکس خیلی حس خوب میده!&quot;مقدمه جذاب !معمولا مردم مقدمه نمی خونن ! اما سعی می کنم ساده و خلاصه بگم ، پس با من همراه باشیدخوب برای توضیح دوست عزیزمون (RxJava) باید کمی براتون از سخت افزار بگم ! فرض کنید میخواهید یک فیل رو بخورید چطور این کار رو میکنید ؟! خوب جوابش سادست ! لقمه لقمه خوب چه ربطی داشت !!! ربطش این جاست که اگه کامپیوتر شما بخواد یک کار سنگین رو انجام بده باید چطور انجام بده؟ اول باید اون رو به قسمت های کوچک تر تبدیل کنه و اون ها رو انجام بده ، چرا ؟!چون منابع بهش اجازه این کار رو نمیده. بگذارید این طوری براتون توضیح بدم ، فرض کنید شما درحال گوش کردن موسیقی هستید و همزمان مرورگر شما باز هست و همزمان در حال کپی کردن یک فایل از گوشی همراه خودتون هستید . تا حالا فکر کردید که چطور این ها به صورت همزمان انجام میشن !! خوب ما توی کامپیوتر برای برنامه ای یک پردازش (process) داریم که اون ها هم واسه خودشون یک id دارن و اگه در سیستم های لینوکسی هستید همین الان برید و دستور &quot;top&quot; رو در کامند لاین تون بنویسید تا به صورت لحظه ای بتونید پراسس های کامپیوتر تون رو ببینید (حتی با گوشی های اندروید هم میشه بالاخره اون ها هم کامپیوتر هستند)  دوستای ویندوزیمون هم میتونن &quot;tasklist&quot; این رو در کامند لاین شون بزنن و حالا با یک سری چیز ها این کار ها باید پردازش بشن !اما مشکل این جاست که مثلا اگه مرور گر شما نیاز به پردازش داشته باشه بقیه باید صبر کنن تا کارش تموم شه ! اما من میخوام همزمان یک سری کار رو انجام بدم ، باید چی کار کنم !! خوب باید با یک سری روش پردازنده (cpu) رو در اختیار همه قرار بدم . که یک سری روش ها مثل راند رابین هست که میشه این کار رو کرد.این داستان ها بخشی از مشکل ما رو حل کرد . اما فرض کنید میخواهید یک تصویر رو در اینستاگرام خودتون منتشر کنید . طبق چیزی که گفتم برنامه باید منتظر بمونه تا پردازش تموم بشه و بتونه کار کنه !اما ما میبینیم که که ارسال عکس در بک گراند انجام میشه که بهش میگیم پردازش غیر همزمان !!این جا بود که نخ (Thread) به کمک ما میاد ، یعنی شما میتونید چند کار رو به صورت موازی انجام بدین !همین الان گفتم که نمیشه این کار رو کرد !!!پس این چیه ؟؟؟!! احتمالا مفهوم چند هسته ای رو شنیده اید، این یعنی چی ؟؟؟این یعنی یک پردازنده خودش رو تبدیل میکنه به چند پردازنده گوچکتر و البته ضعیف تر !یعنی میتونه روی رو چیز به صورت همزمان کار کنه بعد یک کلاس سیستم عامل میریم سراغ اندروید ! خوب رسیدم به این ها : RxJava &amp; RxAndroidما با این دو کتاب خونه میتونیم پردازش غیر همزمان رو برای اندروید پیاده سازی کنیم که در حالت عادی کمی سخت تر هست ، همین! که شرکت reactivex  یک کتاب خانه ای معرفی کرد که برای اندروید Rxjava بهتر کار کند و یک سری قابلیت ها بهش داد برای اطلاعات بیشتر میتونید به گیت هاب اون ها سر بزنید.کار کردن با اون بسیار آسون هست .و ما کلا دو چیز خیلی مهم داریم Observable , Observer  یک نونوایی رو در نظر بگیرید ، یک سری آدم هستند که نون میخواهند و یک نانوا هست که نون رو میده دست مشتری ، فرض کنید رابطه Observable شبیه به نانوا هست و Observer شبیه به مشتریان که باید کارشان تک تک راه بیفتد پیاده سازی یک پروژه کوچیکقدم اول ، اضافه کردن Rxjava و RxAndroid
dependencies {
    implementation &#039;io.reactivex.rxjava3:rxandroid:3.0.0&#039;
    // Because RxAndroid releases are few and far between, it is recommended you also
    // explicitly depend on RxJava&#039;s latest version for bug fixes and new features.
    // (see https://github.com/ReactiveX/RxJava/releases for latest 3.x.x version)
    implementation &#039;io.reactivex.rxjava3:rxjava:3.0.0&#039;
یا allprojects {   
    repositories {
         maven { url &amp;quothttps://oss.jfrog.org/libs-snapshot&amp;quot }    
    }
 }قدم دوم ، اصل داستانObservable.just(&amp;quotone&amp;quot,&amp;quottow&amp;quot,&amp;quottree&amp;quot,&amp;quotfor&amp;quot,&amp;quotfive&amp;quot,&amp;quotsix&amp;quot)                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Observer&lt;String&gt;() {                    @Overridde
                    public void onSubscribe(@NonNull Disposable d) {
                    }
                    @Override
                    public void onNext(@NonNull String s) {
                     }
                    @Override
                    public void (@NonNull Throwable e) {
                    }
                    @Override
                    public void onComplete() {
                }
});کد بالا یعنی یه نونوایی دارم با شش مشتر !برو عملیات پردازش رو توی یک نخ دیگه انجام بدم بعد برگرد به نخ اصلی و وقتی که کارت تموم شد اون نخ رو حذف کن !وقتی که onSubscribe شد یعنی ما مشترک شدیم روی اون و یک متغیر  Disposable رو برمیگردونه که باید اونو بعدا dispose کنیم . چچوری ؟ مثلا توی onDestroy if (disposable != null) disposable.dispose(); این قطعه کد رو اضافه کنیم.تابع onNext یعنی با هر مشتری چیکار کنم !تابع  هم اگر مشکلی پیش اومده باشه اون رو اطلاع میدهو onComplete که زمانی اجرا میشه که کارمون تموم شده باشه </description>
                <category>مهدی معروف به سقراط مناطق محروم :)</category>
                <author>مهدی معروف به سقراط مناطق محروم :)</author>
                <pubDate>Fri, 28 Jan 2022 12:22:52 +0330</pubDate>
            </item>
                    <item>
                <title>Hello World !</title>
                <link>https://virgool.io/@m_11008415/hello-world-wu75gxo9x4kh</link>
                <description>سلام بچه ها.  امروز میخوام براتون درباره کامپیوتر ها حرف بزنم . یک داستان پر از جذابیت. با من همراه باشیدآروم به سمت کامپیوترت میری و با ترس اون دکمه رو میزنی و یهو کلی چیز میز سبز رنگ که احتمالا صفر و یک هستن از جلو چشت میگذره !! و این آغازه ، آغاز یک دنیای دیگر ...می خوام بهت یاد بدم که چطوری میتونی برنامه نویسی کنی ، پایه ای ؟خوب مرسومه که با عبارت &quot; سلام دنیا &quot;  زبان های برنامه نویسی رو شروع میکنن . نمیدونم چرا . شاید همون طور که گفتم سرآغاز دنیای جدیده !یک کوچولو از فواید و مزایای برنامه نویسی :‌   برنامه نویسی آدم ها رو به فکر وامیداره و به آدم یاد میده که مسائل رو از دیدگاه های دیگه نگاه کنه و یه جور هایی آدم رو منطقی میکنه . (این نظر شخصی بود) برای فواید بیشتر و کامل تر به این لینک سر بزنید.خوب بیاین بزنیم تو کارش .ما چند تا زبون برنامه نویسی داریم که همشون با حالن و این که خیلی از تازه کار ها میپرسن ما از کدوم شروع کنیم ؟ جواب درست اینه که واقعا هیچ فرقی نداره . پیشنهاد من اینه که ببین اسم چند تا شون رو شنیدی احتمالا پایتون یا سی پلاس پلاس رو شنیدی اگه هم نه که الان شنیدی :) برو یه تحقیق (سرچ) کن ببین کدوم بیشتر به کارت میاد . مثلا پایتون برای هوش مصنوعی معرکست . این به این معنا نیست که نمیشه سایت ساخت منظورم اینه که هر چیز را بهر کاری ساخته اند و برو ببین که کدوم بیشتر به کارت میاد. یا علاقته بعد برو یادش بگیر . و در جواب این که بگی بهترین زبان برنامه نویسی چیه هم باید بگم دقیقا مثل اینه که بگی بهترین شیرینی  چیه !!حالا بریم یه &quot;! Hello word&quot; جذاب بگیریم !!خوب ، کجا بودیم ؟؟ اها میخواستیم یه عبارت رو چاپ کنیم . برای این که برنامه ما بتونه یه چیز رو برای ما بنویسه ما به دستور چاپ احتیاج داریم . که مثلا تو بعضی جا ها مثل پایتون  &quot;print&quot; هست.یه سایت خیلی جالب هست که شما بدون دردسر میتونید همین قضیه رو آنلاین و بدون این که درگیر نصب و این جور مسائل بشید ببینید به این سایت  سر بزنید . به صورت پیشفرض توش همین عبارت نوشته شده که با دکه اجرا (Run) ، اجرا میشه. print (&amp;quot Hello World ! &amp;quot)  یا به طور کلی :‌print (&amp;quot write your text &amp;quot)یه همچین چیزی در سی پلاس پلاس میشه تو مایه های :#include&lt;iostream&gt;

using namespace std;
int main()
{
    cout&lt;&lt;&amp;quotHello World&amp;quot
    return 0;
}یه سایت خوب برای این که کد های سی پلاس پلاس تون رو اجرا بگیرید ، onlinegdb  هست .خوب اگه براتون جالبه که Hello world رو با زبان های برنامه نویسی دیگه ببینین و تست کنید و البته لذت ببیرید . به این لینک یه سر بزنید . توش 824 تا زبان رو سلام دنیا گرفته .خیلی خوشحالم که تا این جای این پست همراه من بودی . اگه نظر یا پیشنهادی داری من این پاین مایین ها درست در قسمت کامنت ها منتظرتم . اوج مرام تون اینه که لایک هم بکنید . مهدی رحمانی  1400/06/17شاد باشید و بخندین :)</description>
                <category>مهدی معروف به سقراط مناطق محروم :)</category>
                <author>مهدی معروف به سقراط مناطق محروم :)</author>
                <pubDate>Wed, 08 Sep 2021 10:54:15 +0430</pubDate>
            </item>
            </channel>
</rss>