- بواسطة x32x01 ||
كتير من مطوري Back-End بيقعوا في نفس الغلطة أثناء كتابة Business Logic، خصوصًا لما بيكون عندهم شوية شروط لازم تتحقق قبل تنفيذ عملية معينة.
خلينا نفترض مثال بسيط في نظام بنكي:
السيناريو ده طبيعي جدًا في أي نظام فيه Validation أو Business Rules.
لكن المشكلة بتبدأ من طريقة كتابة الكود 👀
في البداية الكود ممكن يبان بسيط…
لكن مع الوقت ومع زيادة الشروط، الكود بيتحول لشكل السهم 👇
⚠️ وده اللي معروف باسم Arrow Anti-Pattern
المشكلة هنا إن:
✔ Single Responsibility Principle (SRP)
الفانكشن بقت مسؤولة عن كل حاجة.
✔ Open/Closed Principle (OCP)
لو البنك طلب شرط جديد… لازم تعدل الكود الأساسي.
وده ممكن يكسر Unit Tests الموجودة.
يعني يعملوا Methods منفصلة:
ده حل أفضل شوية 👌
لكن المشكلة لسه موجودة.
لأن الكلاسات مازالت معتمدة على بعض.
وده اسمه: ⚠ Tight Coupling
الفكرة ببساطة إنك بتقسم كل شرط في Class مستقل.
وكل Class بيكون مسؤول عن خطوة واحدة فقط.
كل Handler بعد ما يخلص شغله يسلم المهمة للـ Handler اللي بعده.
هنا كل Handler بيعمل مهمة واحدة بس
وبعدها يسلم المهمة للي بعده.
خلينا نشرحها بمثال بسيط 👇
تخيل المشترك الكهربائي.
المشترك مش فارق معاه:
لو غيرت الجهاز… المشترك مش هيكسر.
نفس الفكرة لازم تكون في تصميم الكود.
كل Class يبقى مستقل.
مثلاً:
لو الحساب:
بدل ما نعمل If Condition تاني…
نخلي النظام يختار Strategy المناسبة وقت التشغيل.
لو المشروع:
لكن لو النظام:
خلينا نفترض مثال بسيط في نظام بنكي:
1️⃣ لو الشخص عنده حساب في البنك
2️⃣ لو الحساب Active
3️⃣ لو نوع الحساب جاري أو توفير
4️⃣ لو الحساب فيه رصيد كافي
لو كل الشروط دي اتحققت… نقدر نبدأ عملية السحب.2️⃣ لو الحساب Active
3️⃣ لو نوع الحساب جاري أو توفير
4️⃣ لو الحساب فيه رصيد كافي
السيناريو ده طبيعي جدًا في أي نظام فيه Validation أو Business Rules.
لكن المشكلة بتبدأ من طريقة كتابة الكود 👀
المشكلة: استخدام Nested If Conditions بكثرة ⚠️
كتير من المبرمجين بيكتبوا الكود بالشكل ده: Java:
if (hasBankAccount(user)) {
if (isAccountActive(account)) {
if (isAccountTypeValid(account)) {
if (hasBalance(account)) {
withdraw(account);
}
}
}
} لكن مع الوقت ومع زيادة الشروط، الكود بيتحول لشكل السهم 👇
⚠️ وده اللي معروف باسم Arrow Anti-Pattern
المشكلة هنا إن:
- الكود بيبقى صعب القراءة
- Flow البرنامج معقد
- أي مطور يجي يشتغل بعدك هيعاني معاه 😅
- بيكسر مبادئ SOLID Principles
✔ Single Responsibility Principle (SRP)
الفانكشن بقت مسؤولة عن كل حاجة.
✔ Open/Closed Principle (OCP)
لو البنك طلب شرط جديد… لازم تعدل الكود الأساسي.
وده ممكن يكسر Unit Tests الموجودة.
الحل الأول: تقسيم الكود إلى Methods
بعض المطورين بيحاولوا يحلوا المشكلة عن طريق Refactoring.يعني يعملوا Methods منفصلة:
Java:
if (!hasBankAccount(user)) return;
if (!isAccountActive(account)) return;
if (!isAccountTypeValid(account)) return;
if (!hasBalance(account)) return;
withdraw(account); لكن المشكلة لسه موجودة.
لأن الكلاسات مازالت معتمدة على بعض.
وده اسمه: ⚠ Tight Coupling
الحل الاحترافي: استخدام Chain of Responsibility Pattern 🔗
الحل الأقوى في الحالة دي هو استخدام Design Pattern مشهور اسمه: Chain of Responsibilityالفكرة ببساطة إنك بتقسم كل شرط في Class مستقل.
وكل Class بيكون مسؤول عن خطوة واحدة فقط.
كل Handler بعد ما يخلص شغله يسلم المهمة للـ Handler اللي بعده.
مثال عملي على Chain of Responsibility 💻
أولاً: تعريف الـ Interface
Java:
public interface Handler {
void setNext(Handler next);
void handle(Account account);
} Handler لفحص الحساب
Java:
public class AccountExistsHandler implements Handler {
private Handler next;
public void setNext(Handler next) {
this.next = next;
}
public void handle(Account account) {
if(account == null) {
throw new RuntimeException("Account not found");
}
if(next != null) {
next.handle(account);
}
}
} Handler لفحص الرصيد
Java:
public class BalanceCheckHandler implements Handler {
private Handler next;
public void setNext(Handler next) {
this.next = next;
}
public void handle(Account account) {
if(account.getBalance() <= 0) {
throw new RuntimeException("No balance");
}
if(next != null) {
next.handle(account);
}
}
} تشغيل السلسلة
Java:
Handler h1 = new AccountExistsHandler();
Handler h2 = new BalanceCheckHandler();
h1.setNext(h2);
h1.handle(account); وبعدها يسلم المهمة للي بعده.
يعني إيه Loose Coupling؟ 🤔
المفهوم ببساطة: إن الكلاسات متبقاش لازقة في بعض.خلينا نشرحها بمثال بسيط 👇
تخيل المشترك الكهربائي.
المشترك مش فارق معاه:
- تلاجة
- شاحن موبايل
- كمبيوتر
لو غيرت الجهاز… المشترك مش هيكسر.
نفس الفكرة لازم تكون في تصميم الكود.
كل Class يبقى مستقل.
دمج Chain of Responsibility مع Strategy Pattern 🧠
في بعض الحالات بنستخدم معاه Pattern تاني اسمه: Strategy Patternمثلاً:
لو الحساب:
- جاري
- توفير
بدل ما نعمل If Condition تاني…
نخلي النظام يختار Strategy المناسبة وقت التشغيل.
هل ده Over-Engineering؟ 🤨
الإجابة تعتمد على حجم المشروع.لو المشروع:
✔ صغير
✔ Prototype
✔ مش هيكبر
ممكن الحل البسيط يكفي.✔ Prototype
✔ مش هيكبر
لكن لو النظام:
- نظام بنكي
- Enterprise System
- Business Logic بيتغير كتير
- Chain of Responsibility
- Strategy Pattern
الخلاصة 💡
أكبر مشكلة بتقع فيها مشاريع Back-End هي:❌ Nested If Conditions
❌ Arrow Anti-Pattern
❌ Tight Coupling
والحل الاحترافي بيكون باستخدام:❌ Arrow Anti-Pattern
❌ Tight Coupling
✔ Chain of Responsibility Pattern
✔ Strategy Pattern
✔ Loose Coupling Design
وده بيخلي الكود:✔ Strategy Pattern
✔ Loose Coupling Design
- أسهل في القراءة
- أسهل في الصيانة
- قابل للتوسع بسهولة