ฟังก์ชั่นเล็ก ๆ เทียบกับการรักษาหน้าที่การใช้งานในฟังก์ชั่นเดียวกัน


15

ฉันมีคลาสที่ตั้งค่าอาร์เรย์ของโหนดและเชื่อมต่อกับแต่ละอื่น ๆ ในโครงสร้างคล้ายกราฟ เป็นการดีที่สุดที่จะ:

  1. คงฟังก์ชันการทำงานเพื่อเริ่มต้นและเชื่อมต่อโหนดในฟังก์ชันเดียว
  2. มีฟังก์ชั่นการเริ่มต้นและการเชื่อมต่อในฟังก์ชั่นที่แตกต่างกันสองฟังก์ชั่น (และมีลำดับที่ต้องพึ่งพาซึ่งต้องเรียกใช้ฟังก์ชั่น - แม้ว่าโปรดทราบว่าฟังก์ชั่นเหล่านี้เป็นแบบส่วนตัว)

วิธีที่ 1: (แย่ในฟังก์ชั่นหนึ่งที่ทำสองสิ่ง แต่มันเก็บหน้าที่การทำงานที่พึ่งพากันไว้ด้วยกัน - โหนดไม่ควรเชื่อมต่อโดยไม่ต้องเริ่มต้นก่อน)

init() {
    setupNodes()
}

private func setupNodes() {
    // 1. Create array of nodes
    // 2. Go through array, connecting each node to its neighbors 
    //    according to some predefined constants
}

วิธีที่ 2: (ในแง่ที่ดีกว่าว่าเป็นการจัดทำเอกสารด้วยตนเอง แต่ไม่ควรเรียกใช้ connectNodes () ก่อนที่จะติดตั้ง setupNodes () ดังนั้นผู้ที่ทำงานกับ internals ในชั้นเรียนจำเป็นต้องทราบเกี่ยวกับคำสั่งนี้)

init() {
    setupNodes()
}

private func setupNodes() {
    createNodes()
    connectNodes()
}

private func createNodes() {
    // 1. Create array of nodes
}

private func connectNodes() {
    // 2. Go through array, connecting each node to its neighbors 
    //    according to some predefined constants
}

รู้สึกตื่นเต้นที่ได้ยินความคิดใด ๆ



วิธีหนึ่งในการแก้ไขปัญหานี้โดยการกำหนดวัตถุกลางที่สามารถใช้เพื่อสร้างวัตถุสุดท้ายเท่านั้น นี่ไม่ใช่วิธีที่ถูกต้องเสมอไป แต่จะมีประโยชน์หากผู้ใช้อินเตอร์เฟสต้องการจัดการสถานะตัวกลางในบางวิธี
Joel Cornett

คำตอบ:


23

ปัญหาที่คุณกำลังเผชิญอยู่เรียกว่าการมีเพศสัมพันธ์ชั่วคราว

คุณมีสิทธิ์ที่จะกังวลเกี่ยวกับความเข้าใจของโค้ดนี้

private func setupNodes() {
    createNodes();
    connectNodes();
}

ฉันสามารถเดาได้ว่าเกิดอะไรขึ้นที่นั่น แต่บอกฉันว่าสิ่งนี้จะทำให้เกิดอะไรขึ้นที่ชัดเจนขึ้น:

private func setupNodes() {
    self.nodes = connectNodes( createNodes() );
}

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

สิ่งนี้ทำให้การconnectNodes()พึ่งพาโหนดชัดเจน


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

1
รหัสที่คุณเพิ่มนั้นสามารถอ่านได้มากขึ้นดังนั้นฉันจะสร้างโครงสร้างใหม่ในลักษณะนั้น
mcfroob

10

ฟังก์ชั่นแยกกันด้วยเหตุผลสองประการ:

1. ฟังก์ชั่นส่วนตัวมีความเป็นส่วนตัวสำหรับสถานการณ์นี้อย่างแน่นอน

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

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

2. การเชื่อมต่อโหนดซึ่งกันและกันอาจไม่ใช่ฟังก์ชันส่วนตัว

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

อาจเป็นไปconnectNodesได้ที่จะมีการตอบสนองอย่างมีสติถ้ายังไม่ได้สร้างอาร์เรย์ของโหนด (ส่งข้อยกเว้นกลับชุดว่างเปล่าคุณต้องตัดสินใจว่าอะไรเหมาะสมกับสถานการณ์ของคุณ)


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

4

คุณอาจพบ (ขึ้นอยู่กับความซับซ้อนของแต่ละงาน) ว่านี่เป็นรอยต่อที่ดีสำหรับการแยกชั้นเรียนอื่น

(ไม่แน่ใจว่า Swift ใช้งานได้หรือไม่ แต่ใช้รหัสปลอม :)

class YourClass {
    init(generator: NodesGenerator) {
        self.nodes = connectNodes(generator.make())
    }
    private func connectNodes() {

    }
}

class NodesGenerator {
    public func make() {
        // Return some nodes from storage or make new ones
    }
}

สิ่งนี้แยกความรับผิดชอบในการสร้างและแก้ไขโหนดเพื่อแยกคลาส: NodeGeneratorใส่ใจเฉพาะการสร้าง / ดึงข้อมูลโหนดในขณะที่ให้YourClassความสำคัญกับการเชื่อมต่อโหนดที่ได้รับเท่านั้น


2

นอกเหนือจากนี้เป็นวัตถุประสงค์ที่แท้จริงของวิธีการส่วนตัวสวิฟท์ให้ความสามารถในการใช้ฟังก์ชั่นภายใน

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

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

นี่คือตัวอย่างของลักษณะที่อาจมีในกรณีนี้:

init() {
    self.nodes = setupNodes()

    func setupNodes() {
        var nodes = createNodes()
        connect(Nodes: nodes)
    }

    private func createNodes() -> [Node]{
        // 1. Create array of nodes
    }

    func connect(Nodes: [Node]) {
        // 2. Go through array, connecting each node to its neighbors 
        //    according to some predefined constants
    }
}

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


0

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

หากส่วนอื่น ๆ ของโปรแกรมของคุณไม่ได้ใช้งานฉันจะไม่แสดงเป็นฟังก์ชันแยกต่างหาก

หากภาษาของคุณรองรับคุณยังสามารถมีฟังก์ชั่นแบบทำหน้าที่เดียวโดยใช้ฟังก์ชั่นที่ซ้อนกัน

function setupNodes ()  {
  function createNodes ()  {...} 
  function connectNodes ()  {...}
  createNodes() 
  connectNodes() 
} 

สถานที่ของการประกาศมีความสำคัญมากและในตัวอย่างข้างต้นมีความชัดเจนโดยไม่จำเป็นต้องมีเงื่อนงำใด ๆ เพิ่มเติมว่าฟังก์ชั่นด้านในมีความหมายที่จะใช้ภายในร่างกายของฟังก์ชันด้านนอกเท่านั้น

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

ฉันไม่คิดว่าคุณต้องทำอย่างใดอย่างหนึ่ง สิ่งที่ดีที่สุดที่ต้องทำแตกต่างกันไปในแต่ละกรณี

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


ตัวเลือกที่น่าสนใจ อย่างที่คุณพูดฉันคิดว่ามันอาจจะงงนิดหน่อยว่าทำไมฟังก์ชั่นถูกประกาศแบบนี้
mcfroob

ในการตอบความไม่แน่นอนบางอย่างในคำถามนี้: 1) ใช่ Swift สนับสนุนฟังก์ชั่นภายในและ 2) มันมี "ส่วนตัว" สองระดับ privateอนุญาตให้เข้าถึงภายในประเภทการปิดล้อม (struct / class / enum) เท่านั้นในขณะที่fileprivateอนุญาตให้เข้าถึงได้ตลอดทั้งไฟล์
Alexander - Reinstate Monica
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.