مقدمة في مبادئ SOLID في Swift
\nكل مطور iOS يواجه عاجلاً أم آجلاً مشكلة "كود السباغيتي": تتضخم الفئات، التغييرات في مكان واحد تكسر التطبيق بأكمله، ويتحول الاختبار إلى كابوس. يكمن حل هذه المشكلات في أساس التصميم الشيئي — مبادئ SOLID. هذه القواعد الخمس، التي صاغها روبرت مارتن، تساعد في إنشاء كود مرن وقابل للصيانة والتوسع بلغة Swift. في هذه المقالة، سنشرح كل مبدأ من النظرية إلى الأمثلة العملية لتطبيقات iOS/macOS.
\nبغض النظر عما إذا كنت تكتب باستخدام UIKit أو SwiftUI أو AppKit، فإن SOLID هو أساس عالمي. دعنا نتعمق في التفاصيل.
\n\n1. مبدأ المسؤولية الواحدة (SRP)
\nالنظرية
\nيجب أن يكون للفئة سبب واحد فقط للتغيير. بمعنى آخر، كل وحدة مسؤولة عن وظيفة واحدة بالضبط. إذا كان ViewController الخاص بك يدير واجهة المستخدم، ويعالج طلبات الشبكة، ويحلل JSON، فهو ينتهك مبدأ SRP.
\nالتطبيق في Swift
\nلننظر إلى مثال نموذجي — تحميل وعرض بيانات المستخدم.
\n// ❌ سيء: انتهاك مبدأ SRP\nclass UserViewController: UIViewController {\n func loadUser() {\n // طلب شبكة\n let url = URL(string: "https://api.example.com/user")!\n URLSession.shared.dataTask(with: url) { data, _, _ in\n let user = try? JSONDecoder().decode(User.self, from: data!)\n DispatchQueue.main.async {\n self.updateUI(with: user)\n }\n }.resume()\n }\n \n func updateUI(with user: User?) {\n // تحديث واجهة المستخدم\n }\n}\n// ✅ جيد: فصل المسؤوليات\nclass UserService {\n func fetchUser(completion: @escaping (User?) -> Void) {\n let url = URL(string: "https://api.example.com/user")!\n URLSession.shared.dataTask(with: url) { data, _, _ in\n let user = try? JSONDecoder().decode(User.self, from: data!)\n completion(user)\n }.resume()\n }\n}\n\nclass UserViewController: UIViewController {\n let userService = UserService()\n \n func loadUser() {\n userService.fetchUser { [weak self] user in\n DispatchQueue.main.async {\n self?.updateUI(with: user)\n }\n }\n }\n \n private func updateUI(with user: User?) {\n // تحديث واجهة المستخدم\n }\n}\nالخلاصة: الآن UserService مسؤول فقط عن الشبكة، و UserViewController مسؤول عن واجهة المستخدم. التغييرات في منطق الشبكة لا تؤثر على المتحكم.
2. مبدأ الفتح/الإغلاق (OCP)
\nالنظرية
\nيجب أن تكون الفئات مفتوحة للتمديد ولكن مغلقة للتعديل. بدلاً من تغيير الكود الموجود، نضيف كوداً جديداً من خلال الوراثة أو البروتوكولات. في Swift، الأداة المثالية لذلك هي البروتوكولات والإضافات.
\nالتطبيق في Swift
\nتخيل أننا نكتب وحدة لحساب الخصومات في متجر إلكتروني.
\n// ❌ سيء: عند إضافة خصم جديد، يجب تغيير الفئة\nclass DiscountCalculator {\n func calculate(price: Double, type: String) -> Double {\n if type == "seasonal" {\n return price * 0.9\n } else if type == "loyalty" {\n return price * 0.85\n }\n return price\n }\n}\n// ✅ جيد: استخدام بروتوكول للتمديد\nprotocol DiscountStrategy {\n func applyDiscount(to price: Double) -> Double\n}\n\nstruct SeasonalDiscount: DiscountStrategy {\n func applyDiscount(to price: Double) -> Double {\n price * 0.9\n }\n}\n\nstruct LoyaltyDiscount: DiscountStrategy {\n func applyDiscount(to price: Double) -> Double {\n price * 0.85\n }\n}\n\nclass DiscountCalculator {\n func calculate(price: Double, strategy: DiscountStrategy) -> Double {\n strategy.