تخيل كتابًا لا يوجد فيه تقسيم إلى فصول ، وكل شيء غني عن الانهيار المنطقي والدلالي ، كتاب لا توجد فيه فقرات ، لا نقاط وفواصل ، كتاب يخبر فيه السطر الأول عن شيء ، والثاني عن الآخر ، والثالث مرة أخرى حول أول شيء.
قدمت؟
هل يمكن أن تفهم ما هو الكتاب؟
كيف يمكن أن تجد بسرعة المقطع الذي تهتم به؟
يجب تنظيم الكود الخاص بك ، وكذلك محتويات الكتاب ، بحيث يسهل قراءة الكود وتنقل المعنى فيه.
في هذه المقالة ، سأعرض أمثلة على التعليمات البرمجية المنظمة والتي سيكون للفصول فيها نفس تسلسل الكتل الرئيسية وتفاصيلها.
للراحة ، سأستخدم فئة الكلمة (فئة) ، ولكن ضمني أي نوع من أنواع (فئة ، هيكل ، التعداد).
بفضل تطبيق هذه النصائح ، ستصبح شفرتك قابلة للقراءة ، والتي ستوفر في المستقبل الراحة وسرعة العمل معها.
بالطبع ، يمكن ترقية النصائح الموصوفة حسب رغبتك ، مع مراعاة المبادئ الأساسية.
أولاً ، دعنا نقارن نفس الكود بطريقتين.
مثال على فئة فوضوي:
|  | final class MessyViewController: UIViewController { | 
|  |  | 
|  | private let userService = UserService() | 
|  | var userID: String? | 
|  | private var userList: [User]? | 
|  |  | 
|  | @IBOutlet private weak var searchBar: UISearchBar! | 
|  |  | 
|  | weak var delegate: SomeDelegate? | 
|  |  | 
|  | @IBAction private func cancelButtonPressed(_ sender: UIBarButtonItem) { | 
|  | dismiss(animated: true, completion: nil) | 
|  | } | 
|  |  | 
|  | override func viewDidLoad() { | 
|  | super.viewDidLoad() | 
|  | // Do any additional setup after loading the view. | 
|  | navigationController?.navigationBar.backgroundColor = .red | 
|  | navigationItem.title = "Some" | 
|  | } | 
|  | @IBOutlet private weak var tableView: UITableView! | 
|  | } | 
يشبه هذا الكود تفريغ الأساليب والمتغيرات والمنافذ ، حيث يدمج كل شيء معًا ، يصعب فهم ما يشير إليه وفي أي مكان يجب البحث عنه.
مثال على فئة نقية:
|  | final class CleanViewController: UIViewController { | 
|  |  | 
|  | // MARK: - IBOutlets | 
|  |  | 
|  | @IBOutlet private weak var searchBar: UISearchBar! | 
|  | @IBOutlet private weak var tableView: UITableView! | 
|  |  | 
|  | // MARK: - Public Properties | 
|  |  | 
|  | var userID: String? | 
|  | weak var delegate: SomeDelegate? | 
|  |  | 
|  | // MARK: - Private Properties | 
|  |  | 
|  | private let userService = UserService() | 
|  | private var userList: [User]? | 
|  |  | 
|  | // MARK: - Lifecycle | 
|  |  | 
|  | override func viewDidLoad() { | 
|  | super.viewDidLoad() | 
|  |  | 
|  | setupNavigationBar() | 
|  | } | 
|  |  | 
|  | // MARK: - Private Methods | 
|  |  | 
|  | private func setupNavigationBar() { | 
|  | navigationController?.navigationBar.backgroundColor = .red | 
|  | navigationItem.title = "Some" | 
|  | } | 
|  |  | 
|  | // MARK: - IBActions | 
|  |  | 
|  | @IBAction private func cancelButtonPressed(_ sender: UIBarButtonItem) { | 
|  | dismiss(animated: true, completion: nil) | 
|  | } | 
|  |  | 
|  | } | 
تظهر نفس الوظيفة في كلا المثالين ، لكن الفرق هو أن الخيار الثاني له بنية واضحة ، نظرًا لأن المنطق أكثر وضوحًا ، والرمز سهل القراءة ، يمكنك العثور بسرعة على ما تبحث عنه فيه ، وإلى جانب ذلك ، من الجيد أن ننظر إليه.
المبادئ الأساسية لتشكيل هيكل طبقي خالص:
- استخدم دائمًا // MARK: -
- إعطاء أسماء التسميات وتحديد أولوياتها
- وضع المنطق خارج طرق دورة الحياة في طرق منفصلة
- نحن نستخدم التمديد لتنفيذ البروتوكولات
- حدد العناصر المرتبطة منطقيا
- نزيل غير المستخدمة
- أتمتة الروتين
1. استخدم دائمًا // MARK: -
لسهولة القراءة ، يتم تقسيم الكتاب إلى فصول ، وسيكون أكثر راحة لنا في العمل إذا أنشأنا جدول محتويات فئة باستخدام 
// MARK: - .
لا تبرز هذه التسمية جيدًا فقط من الشفرة بأكملها ، ولكنها أيضًا تنشئ جدول محتويات تلقائيًا - فهي تسلط الضوء على الأقسام في الشفرة بالخط العريض في قائمة عناصر هذا الملف.
 يمكنك عرض جدول محتويات الملف عن طريق النقر فوق الزر بعد السهم الأيمن (>) أعلى الملف بعد اسم هذا الملف أو ctr + 6 (قائمة عناصر المستند).
يمكنك عرض جدول محتويات الملف عن طريق النقر فوق الزر بعد السهم الأيمن (>) أعلى الملف بعد اسم هذا الملف أو ctr + 6 (قائمة عناصر المستند).2. نعطي أسماء التسميات ونؤسس تسلسلها
فيما يلي التصنيفات الرئيسية لتقسيم الرمز إلى كتل متصلة منطقياً وتسلسلها:
|  | // MARK: - IBOutlets | 
|  |  | 
|  | // MARK: - Public Properties | 
|  |  | 
|  | // MARK: - Private Properties | 
|  |  | 
|  | // MARK: - Initializers | 
|  |  | 
|  | // MARK: - Lifecycle | 
|  |  | 
|  | // MARK: - Public Methods | 
|  |  | 
|  | // MARK: - Private Methods | 
|  |  | 
|  | // MARK: - IBActions | 
عند استخدام طريقة التجميع هذه ، يمكن للمرء التنقل بسهولة في كود أي فئة.
3. سحب المنطق من أساليب دورة الحياة إلى طرق منفصلة
يجب وضع المنطق داخل أساليب دورة حياة ViewController في طرق منفصلة ، حتى لو كان عليك إنشاء طريقة ذات سطر واحد من التعليمات البرمجية. اليوم واحد ، وغدا هو العاشرة.
|  | ❌ NOT Preferred | 
|  |  | 
|  | override func viewDidLoad() { | 
|  | super.viewDidLoad() | 
|  |  | 
|  | navigationController?.navigationBar.backgroundColor = .red | 
|  | someButton.layer.cornerRadius = 10 | 
|  | someButton.layer.masksToBounds = true | 
|  | navigationItem.title = "Some" | 
|  | print("Some") | 
|  | } | 
|  |  | 
|  |  | 
|  | ✅ Preferred | 
|  |  | 
|  | // MARK: - Lifecycle | 
|  |  | 
|  | override func viewDidLoad() { | 
|  | super.viewDidLoad() | 
|  |  | 
|  | setupNavigationBar() | 
|  | setupSomeButton() | 
|  | printSome() | 
|  | } | 
|  |  | 
|  |  | 
|  | // MARK: - Private Methods | 
|  |  | 
|  | private func setupNavigationBar() { | 
|  | navigationController?.navigationBar.backgroundColor = .red | 
|  | navigationItem.title = "Some" | 
|  | } | 
|  |  | 
|  | private func setupSomeButton() { | 
|  | someButton.layer.cornerRadius = 10 | 
|  | someButton.layer.masksToBounds = true | 
|  | } | 
|  |  | 
|  | private func printSome() { | 
|  | print("Some") | 
|  | } | 
نظرًا لحقيقة أن تفاصيل التنفيذ يتم الاستعانة بمصادر خارجية لأساليب الطرف الثالث بها ، يصبح منطق دورة الحياة أكثر وضوحًا.
4. استخدام التمديد لتنفيذ البروتوكولات
تأخذ تنفيذ البروتوكول في ملحق ملحوظ 
// MARK: - SomeProtocol :
|  | ❌ NOT Preferred | 
|  |  | 
|  | final class CleanViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { | 
|  |  | 
|  | // all methods | 
|  | } | 
|  |  | 
|  |  | 
|  | ✅ Preferred | 
|  |  | 
|  | final class CleanViewController: UIViewController { | 
|  |  | 
|  | // class stuff here | 
|  |  | 
|  | } | 
|  |  | 
|  |  | 
|  | // MARK: - Table View Data Source | 
|  | extension CleanViewController: UITableViewDataSource { | 
|  |  | 
|  | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | 
|  |  | 
|  | return userList?.count ?? 0 | 
|  | } | 
|  |  | 
|  | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | 
|  |  | 
|  | let cell = UITableViewCell() | 
|  | return cell | 
|  | } | 
|  |  | 
|  | } | 
ستحتوي هذه التسمية على كل ما يتعلق بهذا البروتوكول - كل ما هو موجود هنا فقط ولا حاجة للذهاب إلى أي مكان آخر ، وإلا ستنتشر أساليب وخصائص البروتوكول في جميع أنحاء الفصل.
5. حدد العناصر المرتبطة منطقيا
لزيادة مستوى الرؤية ، من الضروري تحديد العناصر المرتبطة منطقياً باستخدام سطر فارغ:
|  | ❌ NOT Preferred | 
|  |  | 
|  | private func showActivityIndicator(on viewController: UIViewController) { | 
|  | activityIndicator.center = viewController.view.center | 
|  | loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) | 
|  | loadingView.alpha = 0.5 | 
|  | activityIndicator.hidesWhenStopped = true | 
|  | activityIndicator.style = .whiteLarge | 
|  | loadingView.center = viewController.view.center | 
|  | loadingView.clipsToBounds = true | 
|  | loadingView.layer.cornerRadius = 15 | 
|  | viewController.view.addSubview(loadingView) | 
|  | viewController.view.addSubview(activityIndicator) | 
|  | activityIndicator.startAnimating() | 
|  | } | 
|  |  | 
|  |  | 
|  | ✅ Preferred | 
|  |  | 
|  | private func showActivityIndicator(on viewController: UIViewController) { | 
|  | activityIndicator.center = viewController.view.center | 
|  | activityIndicator.hidesWhenStopped = true | 
|  | activityIndicator.style = .whiteLarge | 
|  |  | 
|  | loadingView.center = viewController.view.center | 
|  | loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) | 
|  | loadingView.alpha = 0.5 | 
|  | loadingView.clipsToBounds = true | 
|  | loadingView.layer.cornerRadius = 15 | 
|  |  | 
|  | viewController.view.addSubview(loadingView) | 
|  | viewController.view.addSubview(activityIndicator) | 
|  |  | 
|  | activityIndicator.startAnimating() | 
|  | } | 
6. نزيل غير المستخدمة
لا تترك التعليقات غير الضرورية (افتراضيًا) أو الطرق الفارغة أو الوظائف الناقصة - فهذا يؤدي إلى انسداد الكود. انتبه إلى فئة AppDelegate ، على الأرجح ستجد طرق فارغة هناك مع تعليقات في الداخل.
|  | ❌ NOT Preferred | 
|  |  | 
|  | @UIApplicationMain | 
|  | class AppDelegate: UIResponder, UIApplicationDelegate { | 
|  |  | 
|  | var window: UIWindow? | 
|  |  | 
|  |  | 
|  | func application( | 
|  | _ application: UIApplication, | 
|  | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { | 
|  | // Override point for customization after application launch. | 
|  | return true | 
|  | } | 
|  | // | 
|  | // func someFunc() { | 
|  | // print("Some") | 
|  | // } | 
|  |  | 
|  | func applicationWillResignActive(_ application: UIApplication) { | 
|  | // Sent when the application is about to move from active to inactive state. This can occur for certain | 
|  | //types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits | 
|  | //the application and it begins the transition to the background state. | 
|  | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. | 
|  | } | 
|  |  | 
|  |  | 
|  | ✅ Preferred | 
|  |  | 
|  | @UIApplicationMain | 
|  | class AppDelegate: UIResponder, UIApplicationDelegate { | 
|  |  | 
|  | var window: UIWindow? | 
|  |  | 
|  |  | 
|  | func application( | 
|  | _ application: UIApplication, | 
|  | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { | 
|  |  | 
|  | return true | 
|  | } | 
|  |  | 
|  | } | 
7. أتمتة الروتينية
لتجنب الكتابة يدويًا في كل فئة 
// MARK: - SomeMark ، استخدم 
Code Snippet .
 نكتب تسمية ، حددها ، ثم محرر -> إنشاء رمز مقتطف ، ونطلق عليه اسم ونطلق عليه اختصار.
نكتب تسمية ، حددها ، ثم محرر -> إنشاء رمز مقتطف ، ونطلق عليه اسم ونطلق عليه اختصار.// مارك: - مكافأة
- قم بتمييز الفصل الدراسي باستخدام الكلمة الأساسية النهائية إذا لم يكن لهذه الفئة أطفال - يتم تجميع المشروع بشكل أسرع ويتم تشغيل الرمز بشكل أسرع.
- قم بتمييز الخصائص والمنافذ والأساليب باستخدام الكلمة الأساسية الخاصة - ستكون متاحة فقط داخل الفصل ولن تكون في قائمة الخصائص والأساليب العامة إذا لم تكن هناك حاجة إليها.
أتمنى لك كل النجاح في تطوير التطبيقات ودع فصلك يصبح أنظف!
// مارك: - مساعدة في كتابة مقالسيرجي بتشياكوف
أليكسي بليشكوف 
AlekseyPleshkov// مارك: - الروابطراي wenderlich رمز النمط