مقدمة: لماذا تعتبر مبادئ SOLID مهمة لمطور Swift
كل مطور iOS يواجه عاجلاً أم آجلاً مشكلة "كود السباغيتي": وحدات التحكم تتضخم إلى آلاف الأسطر، تغيير كيان واحد يكسر ثلاثة آخرين، ويصبح الاختبار كابوساً. مبادئ SOLID هي خمس قواعد أساسية للتصميم الشيئي تساعد في إنشاء كود مرن وقابل للصيانة والاختبار بلغة Swift.
في هذه المقالة، سنشرح كل مبدأ من مبادئ SOLID بأمثلة محددة من تطوير iOS. ستتعلم كيفية تطبيق هذه المبادئ في المشاريع الحقيقية، وتجنب الأخطاء الشائعة. ستكون المادة مفيدة لمطوري Junior وكذلك للمتخصصين ذوي الخبرة الذين يرغبون في تنظيم معرفتهم.
1. Single Responsibility Principle (SRP) — مبدأ المسؤولية الواحدة
الجوهر: يجب أن يكون لكل class سبب واحد فقط للتغيير. بمعنى آخر، يجب أن يكون class مسؤولاً عن مهمة واحدة فقط.
المشكلة: ViewController "الإلهي"
الخطأ النموذجي هو وضع كل المنطق في UIViewController. لنلق نظرة على مثال سيء:
class UserProfileViewController: UIViewController {
@IBOutlet weak var nameLabel: UILabel!
func loadUser() {
// 1. منطق الشبكة
let url = URL(string: "https://api.example.com/user")!
URLSession.shared.dataTask(with: url) { data, _, _ in
// 2. تحليل JSON
let user = try! JSONDecoder().decode(User.self, from: data!)
// 3. الحفظ في Core Data
self.saveToCoreData(user)
// 4. تحديث واجهة المستخدم
DispatchQueue.main.async {
self.nameLabel.text = user.name
}
}.resume()
}
private func saveToCoreData(_ user: User) { /* ... */ }
}
الحل: فصل المسؤوليات
سنقسم الكود إلى مكونات منفصلة:
// 1. خدمة للعمل مع الشبكة
class UserNetworkService {
func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {
let url = URL(string: "https://api.example.com/user")!
URLSession.shared.dataTask(with: url) { data, _, error in
if let data = data {
do {
let user = try JSONDecoder().decode(User.self, from: data)
completion(.success(user))
} catch {
completion(.failure(error))
}
} else if let error = error {
completion(.failure(error))
}
}.resume()
}
}
// 2. مدير تخزين البيانات
class UserStorageManager {
func save(_ user: User) {
// منطق الحفظ في Core Data / Realm
}
}
// 3. ViewModel (أو Presenter)
class UserViewModel {
private let networkService = UserNetworkService()
private let storageManager = UserStorageManager()
var onUserUpdated: ((User) -> Void)?
func loadUser() {
networkService.fetchUser { [weak self] result in
switch result {
case .success(let user):
self?.storageManager.save(user)
self?.onUserUpdated?(user)
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
// 4. ViewController الآن يدير واجهة المستخدم فقط
class UserProfileViewController: UIViewController {
@IBOutlet weak var nameLabel: UILabel!
private let viewModel = UserViewModel()
override func viewDidLoad() {
super.viewDidLoad()
viewModel.onUserUpdated = { [weak self] user in
DispatchQueue.main.async {
self?.nameLabel.text = user.name
}
}
viewModel.loadUser()
}
}
النتيجة: كل class لديه سبب واحد للتغيير. اختبار وصيانة هذا الكود أصبحت أسهل بكثير.