تخيل كتابًا لا يوجد فيه تقسيم إلى فصول ، وكل شيء غني عن الانهيار المنطقي والدلالي ، كتاب لا توجد فيه فقرات ، لا نقاط وفواصل ، كتاب يخبر فيه السطر الأول عن شيء ، والثاني عن الآخر ، والثالث مرة أخرى حول أول شيء.
قدمت؟
هل يمكن أن تفهم ما هو الكتاب؟
كيف يمكن أن تجد بسرعة المقطع الذي تهتم به؟
يجب تنظيم الكود الخاص بك ، وكذلك محتويات الكتاب ، بحيث يسهل قراءة الكود وتنقل المعنى فيه.
في هذه المقالة ، سأعرض أمثلة على التعليمات البرمجية المنظمة والتي سيكون للفصول فيها نفس تسلسل الكتل الرئيسية وتفاصيلها.
للراحة ، سأستخدم فئة الكلمة (فئة) ، ولكن ضمني أي نوع من أنواع (فئة ، هيكل ، التعداد).
بفضل تطبيق هذه النصائح ، ستصبح شفرتك قابلة للقراءة ، والتي ستوفر في المستقبل الراحة وسرعة العمل معها.
بالطبع ، يمكن ترقية النصائح الموصوفة حسب رغبتك ، مع مراعاة المبادئ الأساسية.
أولاً ، دعنا نقارن نفس الكود بطريقتين.
مثال على فئة فوضوي:
| 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) |
| } |
| |
| } |
سطر فارغ 38 - المسافة البادئة لسطر واحد من الطريقة الأخيرة بحيث يمكنك معرفة أين تنتهي شريحة الإغلاق الأخيرة للطريقة وأين تنتهي الفئة.
تظهر نفس الوظيفة في كلا المثالين ، لكن الفرق هو أن الخيار الثاني له بنية واضحة ، نظرًا لأن المنطق أكثر وضوحًا ، والرمز سهل القراءة ، يمكنك العثور بسرعة على ما تبحث عنه فيه ، وإلى جانب ذلك ، من الجيد أن ننظر إليه.
المبادئ الأساسية لتشكيل هيكل طبقي خالص:
- استخدم دائمًا // MARK: -
- إعطاء أسماء التسميات وتحديد أولوياتها
- وضع المنطق خارج طرق دورة الحياة في طرق منفصلة
- نحن نستخدم التمديد لتنفيذ البروتوكولات
- حدد العناصر المرتبطة منطقيا
- نزيل غير المستخدمة
- أتمتة الروتين
1. استخدم دائمًا // MARK: -
لسهولة القراءة ، يتم تقسيم الكتاب إلى فصول ، وسيكون أكثر راحة لنا في العمل إذا أنشأنا جدول محتويات فئة باستخدام
// MARK: - .
لا تبرز هذه التسمية جيدًا فقط من الشفرة بأكملها ، ولكنها أيضًا تنشئ جدول محتويات تلقائيًا - فهي تسلط الضوء على الأقسام في الشفرة بالخط العريض في قائمة عناصر هذا الملف.
يمكنك عرض جدول محتويات الملف عن طريق النقر فوق الزر بعد السهم الأيمن (>) أعلى الملف بعد اسم هذا الملف أو 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 رمز النمط