ด้วยเหตุผลใดบ้างที่คุณจะใช้ส่วนขยายคลาสแยกต่างหากสำหรับตัวแทนแต่ละคนใน Swift


13

ฉันทำงานผ่านการสอนของ Ray Wenderlich และสังเกตว่าผู้เขียนใช้ส่วนขยายคลาสเพื่อระงับการโทรกลับแทนที่จะมอบหมายให้จัดการในคลาสเองเช่น:

มอบหมาย callback ภายในส่วนขยายของชั้นเรียน:

extension LogsViewController : UIPopoverPresentationControllerDelegate {
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        ...
    }
}

ตรงข้ามกับการมีอยู่ในชั้นเรียน:

มอบหมายการเรียกกลับภายในชั้นเรียน:

class LogsViewController : UITableViewController, UIPopoverPresentationControllerDelegate {
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        ...
    }
}

ฉันพบสิ่งนี้แปลกและน่าสนใจในเวลาเดียวกัน เขามีไฟล์เฉพาะสำหรับการขยายในคลาส LogsViewController ชื่อ "LogsViewControllerExtension.swift" และมีส่วนขยายที่แตกต่างกันสำหรับแต่ละโพรโทคอลของผู้แทน: UITableViewDataSource, UISplitViewDelegate ฯลฯ เช่น:

มีหลายนามสกุลคลาสแต่ละตัวเรียกกลับพร้อมตัวแทนภายในไฟล์ของตัวเอง:

extension LogsViewController: UISplitViewControllerDelegate {
    ... callbacks
}

extension LogsViewController : UIPopoverPresentationControllerDelegate {
    ... callbacks
}

ทำไม?

มีข้อดีอะไรในการทำสิ่งนี้? ฉันสามารถดูว่ามันอาจจะสามารถอ่านได้มากกว่าที่จะแยกออก แต่ในเวลาเดียวกันมันเป็นระดับของการอ้อม มีหลักการ OO ที่สนับสนุนหรือต่อต้านการทำเช่นนี้หรือไม่?


1
คุณสามารถเขียนโครงสร้างจำนวนมากเช่นนี้ที่สนับสนุนหลักการพื้นฐานของ OO แต่ก็ยังมีความผิดในเรื่องการเอาชนะ
Robert Harvey

1
@RobertHarvey จริง แต่คุณหมายถึงว่าโค้ดตัวอย่างที่ฉันแสดงที่นี่เป็นรูปแบบของการครอบงำ การมอบหมายเป็นรูปแบบทั่วไปในการพัฒนา iOS นอกจากนี้ไม่ว่าคุณจะใช้ส่วนขยายของชั้นเรียนหรือไม่ไม่เปลี่ยนรหัสภายในรหัสนั้นจะเป็นเหมือนการจัดโครงสร้างรหัสใหม่มากกว่า (/ มากกว่า)
morbidhawk

1
ฉันเห็นรูปแบบนี้ของการขยายส่วนขยายลงในไฟล์เดียวกันและฉันไม่รู้ว่าสิ่งนี้มาจากไหน ดูเหมือนว่าบางคนกำลังดูถูกเหยียดหยามอยู่แล้วและเพียงแค่ส่งรหัสเล็ก ๆ น้อย ๆ ไปยังส่วนขยายภายในไฟล์เดียวกันโดยพลการ ฉันแน่ใจว่ามีเหตุผลที่ดีที่จะทำเช่นนี้ในบางโอกาส แต่ฉันได้รับความประทับใจโปรแกรมเมอร์บางคนก็ทำโดยไม่เข้าใจว่าทำไม ฉันกำลังมองหาข้อได้เปรียบในเรื่องนี้มากกว่าการใช้ MARK: - และชอบที่จะหาแหล่งที่มาที่ชัดเจนว่าทำไมส่วนขยายควรใช้ในลักษณะนี้
David Lari

คำตอบ:


15

ฉันไม่รู้ว่าทำไมคุณถึงบอกว่ามันเพิ่มระดับความอ้อม บางทีคุณอาจหมายถึงบางสิ่งที่แตกต่างจากความหมายดั้งเดิมเพราะไม่มีการสร้างทางอ้อมโดยการทำสิ่งนี้ แต่ทำไมล่ะ

ฉันทำเพราะมันเป็นแบบแยกส่วน รหัสทั้งหมดที่อินเตอร์เฟสต้องการนั้นถูกจัดกลุ่มไว้ในที่เดียว (ยกเว้นคุณสมบัติจริง) ถ้าฉันเลือกที่จะสร้างคลาสที่แยกต่างหากเพื่อนำโพรโทคอลนั้นมาใช้ (และแนะนำระดับของการอ้อมจริง) ทั้งหมดที่ฉันต้องการ do คือเปลี่ยนนามสกุลให้เป็นคลาสของมันเอง (ผ่านคุณสมบัติที่จำเป็นในการผ่านฟังก์ชัน init) และสร้างคุณสมบัติบน ViewController เพื่อยกตัวอย่างวัตถุให้เป็น

ฉันยังใส่ฟังก์ชั่นส่วนตัวที่ใช้โดยฟังก์ชั่นของโปรโตคอลนั้นในส่วนขยาย ฉันยังไม่ได้สร้างไฟล์แยกต่างหากสำหรับส่วนขยาย แต่การทำเช่นนี้ทำให้ชัดเจนว่าฟังก์ชั่นส่วนตัวเหล่านั้นมีไว้สำหรับโปรโตคอลนั้นเท่านั้น

และในทุก ๆ กรณีผู้คนมักบ่นเกี่ยวกับตัวควบคุมมุมมองไขมันและการแยกตัวควบคุมมุมมองออกด้วยวิธีนี้จะช่วยให้จัดระเบียบได้ดีขึ้นแม้ว่ามันจะไม่ได้ทำให้ตัวควบคุมมุมมองบางลง


ฉันเห็นสิ่งที่คุณหมายถึงโดย modularity เมื่อฉันเข้าใจสิ่งที่เกิดขึ้นมันก็ดูเหมือนจะเป็นวิธีที่สะอาดในการแยกข้อกังวล ระดับทางอ้อมที่ฉันคิดว่ามีไว้สำหรับดวงตาใหม่ (และฉันมีอคติมาจากทาง Obj-c) ฉันรู้สึกถึงความอ้อมระดับเดียวกันเมื่อทำการซับคลาสที่โค้ดถูกอ้างอิงซึ่งถูกนิยามไว้ที่อื่นในคลาสฐานและยากที่จะหามันอีก ผมยอมรับว่าองค์กรจะเพิ่มผลประโยชน์ (มีค่าใช้จ่ายเล็ก ๆ น้อย ๆ ตรง)
morbidhawk

ฉันคิดว่าสิ่งนี้เคยทำมาแล้วกับประเภทคลาสใน Obj-C ฉันไม่เคยเป็นแฟนของคนเหล่านั้น แต่ฉันคิดว่าพวกเขาต้องการไฟล์แยกต่างหาก ตั้งแต่ตอนนี้ใน Swift คุณสามารถเก็บนามสกุลไว้ในไฟล์เดียวกันนั่นคือสิ่งที่ฉันจะทำถ้าฉันตัดสินใจแยกมันออกเป็นอย่างนั้น
morbidhawk

2

อย่างที่ดาเนียลกล่าวไว้เกี่ยวกับการอ้อมไม่มีระดับของมัน
ฉันเห็นด้วยเขาและฉันต้องการเพิ่มคุณสมบัติที่มีประสิทธิภาพพิเศษของส่วนขยายโปรโตคอลที่ฉันรู้เมื่อเร็ว ๆ นี้

สมมติว่าคุณมีโปรโตคอลdidCopyTextเช่น คุณจะใช้สิ่งนั้นเป็น:

protocol didCopyText {
  var charachtersCount: Int {get}
  func addToClipboardWith(text: String)
}

ใน Swift คุณสมบัติและวิธีการไม่ได้นำมาใช้ในการประกาศพิธีสารคุณต้องการเขียนการนำไปใช้ในทุก ๆ คลาสที่สอดคล้องdidCopyTextกับจำนวนคลาสที่เพิ่มขึ้นซึ่งสอดคล้องกับโปรโตคอลนี้ด้วยการใช้งานเดียวกันมันจะจบลงด้วยความยุ่งเหยิง รหัสซ้ำแล้วซ้ำอีก นั่นคือสิ่งที่ส่วนขยายของโปรโตคอลมีประโยชน์

protocol didCopyText {
var charachtersCount: Int {
    get {
     // implementation
    }
}
func addToClipboardWith(text: String) {
      // implementation
 }
}

ด้วยการดำเนินการตามคุณสมบัติและวิธีการของโพรโทคอล ตอนนี้คลาสใด ๆ ที่สอดคล้องกับโปรโตคอลนี้จะใช้การดำเนินการเดียวกัน


ขอบคุณที่แบ่งปันสิ่งนี้ ในขณะที่ฉันถามคำถามนี้ฉันไม่ได้ตระหนักถึงข้อเสียเปรียบบางประการของการสืบทอด OO และสิ่งที่คุณกำลังอธิบายที่นี่เป็นวิธีที่ดีในการรับฟังก์ชั่นเดียวกันนี้กลับมาใช้ซ้ำโดยการใช้คลาสทั้งหมดที่สอดคล้องกับอินเตอร์เฟสนั้น สืบทอดทุกสิ่งจากวัตถุ และถ้าคุณทำตามหลักการของการแยกอินเทอร์เฟซคุณสามารถแยกโพรโทคอลตามต้องการเพื่อให้แน่ใจว่าคลาสที่ใช้งานไม่เคยมีคุณสมบัติ / วิธีการจากอินเทอร์เฟซที่พวกเขาไม่ต้องการ
morbidhawk

1

สมมติว่าคลาสของคุณรองรับสามโปรโตคอลดังนั้นคุณต้องเพิ่มชุดฟังก์ชั่นสามชุด จุดประสงค์ของฟังก์ชั่นเหล่านี้คือเพื่อรองรับโปรโตคอลดังนั้นคุณต้องมีเอกสารประกอบ

อย่างไรก็ตามหากคุณเพิ่มส่วนขยายสำหรับแต่ละโปรโตคอลและในแต่ละส่วนขยายคุณจะใช้ฟังก์ชั่นที่จำเป็นสำหรับโปรโตคอลนั้นเท่านั้นซึ่งจะทำให้โค้ดของคุณอ่านได้มากขึ้น

ฉันมักจะไม่ใส่ไว้ในไฟล์แยกต่างหากเว้นแต่ส่วนขยายเหล่านี้จะมีขนาดใหญ่อย่างแท้จริง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.