خلاصه کتاب Pragmatic Programmer. درس 28

مقدمه فصل 5: خم شدن یا شکستن Bend, or Breakزندگی ساکن نمیمونه، کد ماهم منفک از این قضیه نیست. اونم با تغییرات فوق سریع حوزه تکنولوژی، یا تغییرات بنیادی در نیازهای پروژه و ... این خیلی مهمه که محصولات و کدامونو به سبکی بنویسیم که منعطف و راحت قابل تغییر باشه وگرنه خیلی راحت از رده خارج میشه.تو درس11 در مورد بازگشت پذیری(Reversibility) ی کلیاتی گفتیم، تو این فصل میخایم روشهایی که کمک میکنه تا کد منعطف و برگشت پذیر بنویسیمو بگیم.درس 28: جداسازی (Decoupling)تو درس 8 جوهره طراحی خوب، راجع به اصولی که منجر میشه به اینکه کدمون منعطف تر و راحت تر باشه در مقابل تغییرات، گفتیم. وابستگی، دشمن تغییراته. بخاطر اینکه چیزای مختلفو بهم وصل میکنه و برای تغییر دادن یک بخش باید قسمت های دیگه هم در نظر داشته باشیم و کار سخت و سخت تر میشه. ممکنه وقتی ی چیزی رو تغییر میدین ی جای دیگه بترکه و ...وقتی میخایم یک چیز محکم بسازیم مثل ستون یا پل، معمولا کامپوننت های درگیر در اون رو خیلی بهم میچسبونن و هر چی وابستگی بیشتری داشته باشن خروجی محکم تری پدیدار میشه. اما توی نرم افزار دقیقا برعکسه داستان، هر چی کامپوننت ها وابستگی شون بهم به حداقل ترین حالت ممکن برسه، و خروجی منعطف تر باشه، برامون مطلوب تر و راحت تر خاهد بود اعمال تغییرات و توسعه ها.منظورمون از جداسازی کد چیه؟ در اینجا میخایم در مورد:• زنجیره فراخوانی متودها• خطر ابجکت و متغیرهای گلوبال• خطرهای کلاسهای زیرین در ارث بریصحبت کنیم. البته که این لیست شاید ی مقدار فانتزی باشه، چون هرجایی که تو دو کد چیزی رو به اشتراک استفاده کنن، اونا وارد وابستگی(coupling) شدن و باید حین کد زدن همیشه شاخکاتونو تیز کنید و حین دیتکت سریعا در طراحی تجدید نظر کنید.• زنجیره فراخانی متودهاممکنه کدی شبیه به زیرو یا دیده باشید یا خودتون زده باشید:public void applyDiscount(customer, order_id, discount) { totals = customer .orders .find(order_id) .getTotals(); totals.grandTotal = totals.grandTotal - discount; totals.discount = discount;}توی این کد رفرنسی از چندین سفارش یک آبجکت مشتری گرفته شده، برای اینکه سفارش خاصی رو پیدا کنیم و از اون مبلغ کلش رو بگیریم، از اون مبلغ کل استفاده کنیم و تخفیفو ازش کم کنیم و مقدار تخفیفم ست کنیم. این قطعه کد داره 5 سطح انتزاعی رو پیمایش میکنه از مشتری تا ابجکت مبلغ کل. قطعا باید کد سطح اول بدونه که مشتری ابجکتی داره که لیست سفارشهاشو نگه میداره، ابجکت سفارش تابعی داره که کار جستجو رو انجام میده، یک سفارش یک مبلغ کل داره که خواندنی و نوشتنیه setter and getter داره. یکسری دانستنی وجود داره که باید بدونیم. بدتر اینکه خیلی راحت نمیشه این کدو توی ادامه پروژه تغییر داد. فرض کنید بیزنس این پروژه تصمیم میگیره که سفارشی نتونه تخفیف بیشتر از 40 درصد داشته باشه. کجای کد اینو هندل کنیم؟ممکنه بگید توی همون تابع applyDiscount، قطعا این قسمتی از جوابه، اما با این مدل کد ما مطمئن نیستیم که این فیلد مبلغ کد سفارش در دیگر قسمت های پروژه کجاها در حال مقداردهی شدنه، و اگر خاطرتون نباشه سراغش هم نمیرید.برای حل این مشکل از یک روش به اسم زیر کمک میگیریم:Tell, Don’t Askاین اصل میگه شما نباید براساس وضعیت داخلی یک آبجکت تصمیم به آپدیتش بگیرید، با این کار مزایای انکپسولیشن رو از بین میبرید و بیزنس پیاده سازی هم پخش میشه. (اون باید خودش خودشو اپدیت کنه ینی فانکشنی داشته باشه براش). در قدم اول برای اعمال تخفیف به این شکل عمل میکنیم:public void applyDiscount(customer, order_id, discount) { customer .orders .find(order_id) .getTotals() .applyDiscount(discount);}در اینجا هنوز یک TDA (تل دونت اسک) دیگه داریم، در مورد سفارش های یک مشتری:public void applyDiscount(customer, order_id, discount) { customer .findOrder(order_id) .getTotals() .applyDiscount(discount);}همین داستان برای یک سفارش و مبلغ کلشم وجود داره در نتیجه:public void applyDiscount(customer, order_id, discount) { customer .findOrder(order_id) .applyDiscount(discount);}اینجا، جاییه که متوقف میشیم. شاید بگید طبق اصل TDA یک فانکشن برای کلاس مشتری مینوشتیم که خودش مستقیم حرکتو بزنه، خوب اگر برده طور بهش نگا کنیم این فانکشن هم جواب میده (نفهمیدم کاربرد این کلمه رو اینجا برده وار slavish یا شایدم چیز دیگه ای توی این جایگاه معنی میشه)این TDA وحی الهی نیست، فقط یک پترنه که بهمون کمک میکنه مشکلاتو شناسایی کنیم، در خیلی از سیستم های یکسری از ابجکت ها افشا شدنشون لازمه مثل مشتری یا سفارش بعضی ها هم ممکنه این مدل طراحی رو بهینه تر پیدا کنند که سفارش های مشتری رو توی مشتری هاید کنن.قانون Demeterممکنه در مورد coupling شنیده باشید که قانون Demeter هم بیان میشه LoD، این قانون اواخر دهه 80 میلادی توسط Ian Holland مطرح شد، به برنامه نویسا کمک میکنه تا توابعشون رو تمیزتر و اصل جداسازی راحت تر پیاده بشه. این قانون میگه هر تابعی که در یک کلاس تعریف میشه باید فقط بتونه:- متودهای دیگه همون کلاس رو کال کنه- به پارامترهای خودش دسترسی داشته باشه- توابع آبجکتی که خودش ساخته رو ببینه- به متغیرهای گلوبال دسترسی داشته باشه(متغیرهای گلوبال چیز خوبی نیست تو ادامه همین درس بهشون میپردازیم)Don’t Chain Method Callsسعی کنید بیش تر از یک دات "." نداشته باشید، وقتی میخاید به چیزی دسترسی پیدا کنید مثلا کد زیر خوب نیست:# This is pretty poor styleamount = customer.orders.last().totals().amount;# and so is this…orders = customer.orders;last = orders.last();totals = last.totals();amount = totals.amount;یک استثناء بزرگ وجود داره توی عدم استفاده از دات های پیاپی، اونم برای دسترسی به چیزهایی که خیلی خیلی بعیده تغییر کنند. عموما هرچیزی توی اپلیکیشن شما ممکنه تغییر بکنه، هرچیزی توی کتابخونه های تردپارتی، یا حتی کتابخونه های خوده زبون برنامه نویسی، هرچند کتابخونه ها معمولا استیبل نوشته و اپدیت میشن و ما خیلی خوشحال اینجوری استفاده میکنیم:people.sort_by {|person| person.age }.first(10).map {| person | person.name }در حدی که کد روبی که ما توی ادیشن اول این کتاب نوشتیم 20 سال پیش، همچنان کار میکنه که هیچ، تا وقتی که بریم خونه سالمندانم کار خاهد کرد یحتمل.Chains and pipelinesتوی درس 30 Transforming Programming در مورد توابعی صحبت خاهیم کرد که به صورت پایپ لاین کار میکنن، و دیتا بینشون پاس داده میشه از تابعی به تابع دیگه، این مثل زنجیره ای کال کردن متودهایی که مثال زدیم نیست، هرچند که به صورت کامل از هم دیکاپل نیستند فانکشن ها ولی قطعا از مدل زنجیره ای کمتره وابستگی شون و معمولا فرمت دیتایی که بین شون منتقل میشه یک دسته.• THE EVILS OF GLOBALIZATIONداده های گلوبال به شکل موزیانه ای وابستگی رو در کدتون میبرن بالا، یک پارامتر همیشه در دسترس برای همه توابعن. چرا میگیم وابستگی کدو زیاد میکنه، در وهله اول فرض کنید نحوه پیاده سازی و مقداردهی متغیرهای گلوبال رو تغییر میدید، حالا ممکنه صدجای برنامه ازشون استفاده کرده باشید، بیفت دنبال دستکاری همه جا.... همچنین باعث میشن که نتونید قطعه های کدو به شکل مستقل تست بکنید(اون کدهایی که از گلوبالا استفاده میکنن).- گلوبال دیتاها شامل Singleton ها هم میشن، شاید ی عده بگن خوب متغیر گلوبال تعریف نکردم، کلاس نوشتم براش و یک اینستنس سینگلتون ازش ساختم، این کار هم دقیقا گلوبال دیتا ایجاد میکنه. حالا ممکنه یک عده این دیتاها رو پشت توابع اون کلاس سینگلتون و یا پشت اینترفیس ها بپوشونن، باز این بهتره چون باعث میشه به راحتی تغییرات مخرب نتونید روش بدید و صدها جایی که استفاده شده خراب نشه.- گلوبال دیتا شامل منابع خارجی هم میشه(External Resource)، هر منبع خارجی قابل تغییر مثل دیتابیس، دیتااستور، فایل سیستم، وب ای پی آی و ... گلوبال دیتا هستن و فقط برای بهتر و امن تر کار کردن باهاشون باید رفتارشونو توی کد wrap کنیم و تحت کنترل داشته باشیم.• ارث بری وابستگی رو بیشتر میکنه Inheritance adds couplingدر مورد اینکه وقتی یک کلاس استیت و رفتار یک کلاس دیگه رو به ارث میبره، به صورت دیتیل در فصل 31 Inheritance tax صحبت خاهیم کرد. منتهی بازم مسئله حول محور تغییراته، کدهای وابسته رو سخت تر میشه تغییر داد، تغییر یک جا باعث ساید افکت توی جای دیگه بشه که در بدترین حالت توی پروداکشن بزنه بیرون. کدو خجالتی نگه داری، فقط در مورد چیزهایی که میدونه و بهش مربوط میشه عمل کنه، این کمک میکنه به جداسازی در پروژه و تغییر و توسعه رو راحت تر میکنه.دروس مرتبط: 8, 9, 10, 11, 29, 30, 31, 32, 33, 34, 35, 36منبع کانال تلگرامی: https://t.me/pragmaticprogrammer_fa