Philocode – Telegram
وقتی کسی پروفایلم رو چک می‌کنه... 😔😂
#fun
😁4
Philocode
مقاله‌ای از مارتین فولر دربارۀ Value Objectها: https://martinfowler.com/bliki/ValueObject.html
می‌فرماید که Value Objectها ویژگی‌هایی دارن، از جمله این دو ویژگی:
1. اینکه immutable هستند، قرار نیست تغییرشون بدیم.
2. اینکه یکتا نیستند. بر خلاف entity که برای ذخیره‌سازی و... نیاز داریم identifiable باشه، اینها نیاز نیست که identifiable باشند.
👍2
Philocode
می‌فرماید که Value Objectها ویژگی‌هایی دارن، از جمله این دو ویژگی: 1. اینکه immutable هستند، قرار نیست تغییرشون بدیم. 2. اینکه یکتا نیستند. بر خلاف entity که برای ذخیره‌سازی و... نیاز داریم identifiable باشه، اینها نیاز نیست که identifiable باشند.
یکی از دلایلی که نویسنده برای این آورده که value objectها بايد immutable باشند.

به اتفاق غیر منتظره‌ای که با تغییردادن پراپرتی acc1 (که یک value object ــه) افتاده توجه کنید.
👍3
Philocode
یکی از دلایلی که نویسنده برای این آورده که value objectها بايد immutable باشند. به اتفاق غیر منتظره‌ای که با تغییردادن پراپرتی acc1 (که یک value object ــه) افتاده توجه کنید.
توی Javanoscript می‎شه این‌طوری آبجکت‌هارو clone کرد. اگه به جای این کار (بهش می‌گیم spread) می‌اومدیم obj1 رو به obj2 اساین می‌کردیم، تغییر obj1 روی obj2 هم اعمال می‌شد چون بهش reference داشت.
👍2
Philocode
حاصل کد زیر کدام است؟
console.log({ name: "John" } == { name: "John" })
جواب درست: false

چرا؟ چون جناب Javanoscript آبجکت‌هارو بر اساس اون value که دارن مقایسه نمی‌کنه، به جای اون به reference دقت می‌کنه؛ برای همین هیچ‌وقت دو آبجکت جدا مساوی نیستند، حتی اگه value یکسان داشته باشند.

به این مثال هم توجه کنید:
const obj1 = { age: 24 }
const obj2 = obj1
console.log(obj1 == obj2) // true

const obj3 = { ...obj1 }
console.log(obj3 == obj1) // false
👍2
Philocode
Inside an immutable object: public withPassword (string plainTextPassword): User Inside a mutable object: public setPassword (string plainTextPassword): void
توی لاراول یه کلاس به اسم Builder داریم که کار ساختن کوئری‌های دیتابیس رو انجام می‌ده.

متد select این کلاس رو ببینیم:
public function select($columns = ['*'])
{
// 13 lines removed
return $this;
}

این متد داره this$ رو برمی‌گردونه، و از اینجا حدس می‌زنیم: آها، این یه immutable object ـــه! اما پیاده‌سازی متدهای این کلاس از جمله خود select یه چیز دیگه به ما می‌گن؛ مثلاً روی این کلاس یه پراپرتی به اسم columns داریم که متد select تغییرش می‌ده!

چیزی که باعث گیجی ما شده، اینه که خروجی متدها شبیه immutableهاست (پست ریپلای‌شده رو ببینید)، ولی ساختار آبجکت و رفتار این متد به mutableها شباهت داره! چیزی که باعث گیجی ما شده، اینه که این کلاس می‌خواسته یه راه سرراست در اختیار ما بذاره که هر بار instance این آبجکت رو صدا نزنیم و به صورت زنجیره‌وار از متدها استفاده کنیم.
👍2🔥2
Philocode
توی لاراول یه کلاس به اسم Builder داریم که کار ساختن کوئری‌های دیتابیس رو انجام می‌ده. متد select این کلاس رو ببینیم: public function select($columns = ['*']) { // 13 lines removed return $this; } این متد داره this$ رو برمی‌گردونه، و از اینجا حدس…
همیشه آبجکت‌های immutable را ترجیح دهید؛ یعنی آن‌هایی که بعد از ساخته‌‎شدن نمی‌توانند تغییر کنند. اگر واقعاً در شرایطی نیاز داشتید که چیزی را تغییر دهید، ابتدا یک کپی بسازید و بعد تغییرات را روی کپی اعمال کنید.
#ObjectDesignStyleGuide
Philocode
🧐 A template for methods [scope] function methodName(type name, ...): void | [return-type] { [preconditions checks] [failure scenarios] [happy path] [postcondition checks] [return void|specific-return-type] } #ObjectDesignStyleGuide
به متد شمارۀ یک می‌گیم command method که قراره کاری انجام بده، که اینجا تغییردادن پراپرتیه.
به متد شمارۀ دو می‌گیم query method که برای برگردوندن اطلاعات ازش استفاده کردیم؛ واضحه که return type مشخصی باید داشته باشه، و لازمه که side effect (عوارض جانبی) نداشته باشه.
🔥1
چند تست برای متد زیر لازم داریم تا Code Coverage صد در صد داشته باشیم؟ و چند تست نیاز داریم تا تمام حالت‌های ممکن را پوشش دهیم؟
public function pay($amount): bool
{
if ($amount <= 0) {
throw new SomeException('Are you serious?');
}

return PaymentService::pay($amount);
}

👍 2 - 2
❤️ 1 - 2
🤩 2 - 3
😁 3 - 3
2👍2🤩2
Philocode
بشنویم از مارتین فولر که راجع به جداسازی Query/Command methodها افاضه فرموده: https://martinfowler.com/bliki/CommandQuerySeparation.html
گفتیم که query methodها نباید side-effect (عوارض جانبی) داشته باشند، برای همین نویسنده یک قاعدۀ دیگه رو معرفی می‌کنه:
Query methods should use other query methods, not command methods.

به مثال زیر دقت کنید:
public function payable(): number {
return this.subTotal() + this.deliveryCost()
}
متد payable به وضوح یک query method ـــــه، و داره از دوتا query method دیگه به نام‌های subTotal و deliveryCost استفاده می‌کنه. اما مهم اینه که نباید داخل query methodها از command methodها که عوارض جانبی دارند استفاده بشه، چون در اون صورت query method ما هم عوارض جانبی خواهد داشت درحالیکه نباید داشته باشه!
👍2
Philocode
گفتیم که query methodها نباید side-effect (عوارض جانبی) داشته باشند، برای همین نویسنده یک قاعدۀ دیگه رو معرفی می‌کنه: Query methods should use other query methods, not command methods. به مثال زیر دقت کنید: public function payable(): number { return this.subTotal()…
ماتیاس نوباک که خوشبختانه اهل ساختن وب‌اپلیکیشن‌هاست برای این قاعده یه استثنا میاره:
می‌گه گاهی واقعاً نیاز داریم که این قاعده رو بشکنیم؛ مثلاً متدهای یک کنترلر بالأخره باید یه HTTP response برگردونند هرچند که side-effect هم داشته باشند؛ ما کاربر رو از طریق متد کنترلر ثبت نام می‌کنیم و اینجا side-effect مشهوده (تغییر دیتابیس و...) و با این حال یه response هم برمی‌گردونیم.
👍1
Philocode
چند تست برای متد زیر لازم داریم تا Code Coverage صد در صد داشته باشیم؟ و چند تست نیاز داریم تا تمام حالت‌های ممکن را پوشش دهیم؟ public function pay($amount): bool { if ($amount <= 0) { throw new SomeException('Are you serious?'); } return…
وقتشه که این سوال رو جواب بدیم.

چند تست برای متد زیر لازم داریم تا Code Coverage صد در صد داشته باشیم؟
اول باید بفهمیم که code coverage چیه. code coverage به زبان ساده یعنی اینکه وقتی تست رو اجرا کردید، چند خط از کدهای اپلیکیشن از کل خطوط اجرا شدند؟ مثلاً کدی که 100 خط باشه، و با اجرای تست، 50 خطش اجرا بشه، یعنی 50% کدها پوشش داده شده. 50% کدهای ما در حین اجرای تست به کلی اجرا نشدند.
توی نمونۀ ما، دو تست برای اینکه code coverage ما 100% بشه نیازه؛ توی یکی باید داخل if اجرا بشه و توی دومی قسمت مربوط به اجرای متد pay از اون سرویس و جوابی که برمی‌گرده. اگه مقدار amount مثبت باشه، هیچ‌وقت به داخل if نمی‌رسیم و در نتیجه اون قسمت پوشش داده نخواهد شد.

چند تست نیاز داریم تا تمام حالت‌های ممکن را پوشش دهیم؟
ممکنه که code coverage کد ما 100% باشه، ولی لزوماً معنیش این نیست که برای کل حالت‌هایی که ممکنه پیش بیان، تست نوشتیم. توی این مورد، با دو تست تونستیم 100% از code coverage رو به دست بیاریم، ولی توی این کد نه دو حالت، سه حالت وجود داره:
1. وقتی amount مساوی یا کوچک‌تر از صفر باشه.
2. وقتی که متد pay از اون سرویس، true برگردونه.
3. وقتی که اون متد، false برگردونه.
اگه به return type متد pay خودمون دقت کنیم، می‌بینیم که bool هست و این خودش یعنی دو حالت؛ پس جواب این سوال، می‌شه سه تست.
#testing
👍2
Philocode
گفتیم که query methodها نباید side-effect (عوارض جانبی) داشته باشند، برای همین نویسنده یک قاعدۀ دیگه رو معرفی می‌کنه: Query methods should use other query methods, not command methods. به مثال زیر دقت کنید: public function payable(): number { return this.subTotal()…
گفتیم که query methodها نباید از command methodها استفاده کنند. این مصداق واضح سوء استفاده‌ست؛ چون command methodها side-effect دارند و این‌طوری query method رو از چیزی که باید باشه (اینکه ده‌بار کال کنیم و باز هم چیزی تغییر نکنه) منحرف می‌کنند.

اما command methodها می‌تونند از query methodها استفاده کنند، چون برعکس اون قاعده اینجا صادق نیست: command methodها side-effect دارند و کال‌شدن یک query method داخل اونها، چیزی رو خراب نمی‌کنه.
توی نسخۀ فعلی TeleBot می‌شه کیبورد رو chunk کرد تا هر ردیف، به تعدادی که ذکر شده دکمه داشته باشه؛ مثلاً شش‌تا دکمه اضافه کرده و بعد هر ردیف رو سه‌دکمه‌ای می‌کنه:
->chunk(3)
نتیجه:
1️⃣2️⃣3️⃣
4️⃣5️⃣6️⃣

اما ان شا الله توی نسخۀ بعدی، میشه هم int و هم array رو به متد chunk پاس داد:
->chunk([2, 4])
نتیجه:
1️⃣2️⃣
3️⃣4️⃣5️⃣6️⃣

لطف کنید به ریپازیتوری ⭐️ بدید که انگیزۀ بیشتری برای اوپن‌سورس داشته باشم.
👍5
خُب خـــــــــیـــــــــلی بهتر شد! 🔥
#refactoring #telebot
🔥2