Philocode
همیشه آبجکتهای immutable را ترجیح دهید؛ یعنی آنهایی که بعد از ساختهشدن نمیتوانند تغییر کنند. اگر واقعاً در شرایطی نیاز داشتید که چیزی را تغییر دهید، ابتدا یک کپی بسازید و بعد تغییرات را روی کپی اعمال کنید. #ObjectDesignStyleGuide
🧐 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
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 (عوارض جانبی) نداشته باشه.
به متد شمارۀ دو میگیم query method که برای برگردوندن اطلاعات ازش استفاده کردیم؛ واضحه که return type مشخصی باید داشته باشه، و لازمه که side effect (عوارض جانبی) نداشته باشه.
🔥1
Philocode
به متد شمارۀ یک میگیم command method که قراره کاری انجام بده، که اینجا تغییردادن پراپرتیه. به متد شمارۀ دو میگیم query method که برای برگردوندن اطلاعات ازش استفاده کردیم؛ واضحه که return type مشخصی باید داشته باشه، و لازمه که side effect (عوارض جانبی) نداشته…
بشنویم از مارتین فولر که راجع به جداسازی Query/Command methodها افاضه فرموده:
https://martinfowler.com/bliki/CommandQuerySeparation.html
https://martinfowler.com/bliki/CommandQuerySeparation.html
martinfowler.com
bliki: Command Query Separation
a bliki entry for Command Query Separation
چند تست برای متد زیر لازم داریم تا Code Coverage صد در صد داشته باشیم؟ و چند تست نیاز داریم تا تمام حالتهای ممکن را پوشش دهیم؟
❤️ 1 - 2
🤩 2 - 3
😁 3 - 3
public function pay($amount): bool👍 2 - 2
{
if ($amount <= 0) {
throw new SomeException('Are you serious?');
}
return PaymentService::pay($amount);
}
❤️ 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.
به مثال زیر دقت کنید:
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 هم برمیگردونیم.
میگه گاهی واقعاً نیاز داریم که این قاعده رو بشکنیم؛ مثلاً متدهای یک کنترلر بالأخره باید یه 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
چند تست برای متد زیر لازم داریم تا 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 داخل اونها، چیزی رو خراب نمیکنه.
اما command methodها میتونند از query methodها استفاده کنند، چون برعکس اون قاعده اینجا صادق نیست: command methodها side-effect دارند و کالشدن یک query method داخل اونها، چیزی رو خراب نمیکنه.
توی نسخۀ فعلی TeleBot میشه کیبورد رو chunk کرد تا هر ردیف، به تعدادی که ذکر شده دکمه داشته باشه؛ مثلاً ششتا دکمه اضافه کرده و بعد هر ردیف رو سهدکمهای میکنه:
1️⃣2️⃣3️⃣
4️⃣5️⃣6️⃣
اما ان شا الله توی نسخۀ بعدی، میشه هم int و هم array رو به متد chunk پاس داد:
1️⃣2️⃣
3️⃣4️⃣5️⃣6️⃣
لطف کنید به ریپازیتوری ⭐️ بدید که انگیزۀ بیشتری برای اوپنسورس داشته باشم.
->chunk(3)نتیجه:
1️⃣2️⃣3️⃣
4️⃣5️⃣6️⃣
اما ان شا الله توی نسخۀ بعدی، میشه هم int و هم array رو به متد chunk پاس داد:
->chunk([2, 4])نتیجه:
1️⃣2️⃣
3️⃣4️⃣5️⃣6️⃣
لطف کنید به ریپازیتوری ⭐️ بدید که انگیزۀ بیشتری برای اوپنسورس داشته باشم.
👍5
تعریفکردن کلاسها به صورت final، چرا؟
اگه کلمۀ کلیدی final رو به کلاسی اضافه کنیم، امکان extendکردن اون کلاس وجود نخواهد داشت.
مثال:
PHP Fatal error: Class B may not inherit from final class (A)
میفرمایند که composition راه بهتر و منعطفتری از inheritance ــــه (همیشه؟)، برای همین وقتی ترجیحمون استفاده از final باشه، راه inheritance رو میبندیم و کسانی که با کلاس کار دارند میفهمند که باید به جای inheritance و overrideکردن، دنبال راه دیگهای برای تغییر رفتار کلاس باشند و به نحوی اونها رو به اجرای اصل Composition over inheritance سوق میدیم.
برای مطالعۀ بیشتر، این لینکهارو داشته باشید:
https://matthiasnoback.nl/2018/09/final-classes-by-default-why
https://en.wikipedia.org/wiki/Composition_over_inheritance
https://medium.com/@orhanobut/favor-composition-over-inheritance-a3e5462d01f5
https://stackoverflow.com/questions/32308276/when-to-favor-inheritance-over-composition
اگه کلمۀ کلیدی final رو به کلاسی اضافه کنیم، امکان extendکردن اون کلاس وجود نخواهد داشت.
مثال:
<?phpخروجی:
final class A {}
class B extends A {}
PHP Fatal error: Class B may not inherit from final class (A)
میفرمایند که composition راه بهتر و منعطفتری از inheritance ــــه (همیشه؟)، برای همین وقتی ترجیحمون استفاده از final باشه، راه inheritance رو میبندیم و کسانی که با کلاس کار دارند میفهمند که باید به جای inheritance و overrideکردن، دنبال راه دیگهای برای تغییر رفتار کلاس باشند و به نحوی اونها رو به اجرای اصل Composition over inheritance سوق میدیم.
برای مطالعۀ بیشتر، این لینکهارو داشته باشید:
https://matthiasnoback.nl/2018/09/final-classes-by-default-why
https://en.wikipedia.org/wiki/Composition_over_inheritance
https://medium.com/@orhanobut/favor-composition-over-inheritance-a3e5462d01f5
https://stackoverflow.com/questions/32308276/when-to-favor-inheritance-over-composition
👍3
کد بالا، اصطلاحاً tight coupling داره و کد ما شدیداً به کلاس Facebook درهمتنیده شده. اگه مثل کد پایین از dependency injection استفاده کنیم، به loose coupling میرسیم و خیلی راحت میشه در هنگام تستنوشتن، یه mock رو به جای اون سرویس واقعی پاس داد و بقیۀ منطق کلاس رو تست کرد. از طرفی اصل دوم و پنجم SOLID رو رعایت کردیم و میتونیم هر سرویس دیگهای مثل Twitter رو بدون تغییردادن این کلاس وارد ماجرا کنیم و انعطاف کلاس Publisher رو حفظ کنیم.
حملهای؟ نظری؟ سوالی؟
حملهای؟ نظری؟ سوالی؟
👍1
از زیباییهای Typenoscript:
interface Todo {
noscript: string;
}
const todo: Readonly<Todo> = {
noscript: "Delete inactive users",
};
todo.noscript = "Hello";
⛔️ Cannot assign to 'noscript' because it is a read-only property.👍1
Philocode
از زیباییهای Typenoscript: interface Todo { noscript: string; } const todo: Readonly<Todo> = { noscript: "Delete inactive users", }; todo.noscript = "Hello"; ⛔️ Cannot assign to 'noscript' because it is a read-only property.
البته توی PHP هم میتونیم مثل این رو داشته باشیم، ولی از Utility Typeها خبری نیست و باید داخل خود کلاس تعریف بشه:
اینطوری بیرون از آبجکت نمیشه پراپرتیهارو تغییر داد.
class Developer⛔️ Error: Cannot modify readonly property Developer::$name...
{
public readonly name;
}
$developer = new Developer();
$developer->name = 'John';
اینطوری بیرون از آبجکت نمیشه پراپرتیهارو تغییر داد.
👍1
Philocode
تعریفکردن کلاسها به صورت final، چرا؟ اگه کلمۀ کلیدی final رو به کلاسی اضافه کنیم، امکان extendکردن اون کلاس وجود نخواهد داشت. مثال: <?php final class A {} class B extends A {} خروجی: PHP Fatal error: Class B may not inherit from final class (A) میفرمایند…
استثنایی که برای این میتونه وجود داشته باشه، دیزاین پترن Template ــــه.
این مثال رو بیبنید:
اینجا overrideکردن جزئی از ذات این پترنه و معنی نداره با final از overrideشدنش جلوگیری کنیم!
این مثال رو بیبنید:
class SomeJobمثلاً اون چهارتا متد، روی همون کلاس SomeJob تعریف شدند و هر کدوم از اونها، مرحلهای از انجام کاره؛ مثلاً اولی روغن میریزه، دومی تخم مرغ رو اضافه میکنه، سومی نمک رو اضافه میکنه و چهارمی هم یه چیز دیگه. Template Pattern میگه بذارید که این مراحل قابل overrideکردن باشند، تا بشه بعضی از این مراحل رو توی کلاسی که از این کلاس ارث میبره، تغییر داد؛ مثلاً به جای مرحلۀ سوم که نمک میریزه، مرگ موش اضافه کنه. (چه مثال دارکی)
{
public function handle()
{
$this->firstStep();
$this->secondStep();
$this->thirdStep();
$this->fourthStep();
}
}
اینجا overrideکردن جزئی از ذات این پترنه و معنی نداره با final از overrideشدنش جلوگیری کنیم!