“ ทุกอย่างคือแผนที่” ฉันทำถูกไหม?


69

ฉันดูคำพูดของสจ็วตเซียร์เซียร์ " คิดในข้อมูล " และนำหนึ่งในแนวคิดจากนั้นเป็นหลักการออกแบบในเกมนี้ที่ฉันทำ ความแตกต่างคือเขาทำงานใน Clojure และฉันทำงานใน JavaScript ฉันเห็นความแตกต่างที่สำคัญระหว่างภาษาของเราในเรื่องนั้น:

  • Clojure เป็นการเขียนโปรแกรมที่ใช้งานง่าย
  • รัฐส่วนใหญ่ไม่เปลี่ยนรูป

ฉันใช้ความคิดจากสไลด์ "ทุกอย่างคือแผนที่" (จาก 11 นาที, 6 วินาทีถึง> 29 นาที) บางสิ่งที่เขาพูดคือ:

  1. เมื่อใดก็ตามที่คุณเห็นฟังก์ชั่นที่ใช้อาร์กิวเมนต์ 2-3 ข้อคุณสามารถทำให้เป็นกรณีสำหรับเปลี่ยนเป็นแผนที่และเพิ่งผ่านแผนที่มามีข้อดีมากมายดังนี้:
    1. คุณไม่ต้องกังวลกับคำสั่งโต้แย้ง
    2. คุณไม่ต้องกังวลกับข้อมูลเพิ่มเติมใด ๆ หากมีกุญแจพิเศษนั่นไม่ใช่ความกังวลของเรา พวกเขาไหลผ่านพวกเขาไม่ได้ยุ่ง
    3. คุณไม่จำเป็นต้องกำหนดสคีมา
  2. ตรงข้ามกับการส่งผ่านวัตถุไม่มีการซ่อนข้อมูล แต่เขาทำให้กรณีที่การซ่อนข้อมูลสามารถทำให้เกิดปัญหาและ overrated:
    1. ประสิทธิภาพ
    2. ใช้งานง่าย
    3. ทันทีที่คุณสื่อสารผ่านเครือข่ายหรือข้ามกระบวนการคุณจะต้องให้ทั้งสองฝ่ายเห็นพ้องกับการแสดงข้อมูลต่อไป นั่นเป็นงานพิเศษที่คุณสามารถข้ามได้ถ้าคุณแค่ทำงานกับข้อมูล
  3. ส่วนใหญ่เกี่ยวข้องกับคำถามของฉัน นี่คือ 29 นาทีใน: "ทำให้ฟังก์ชั่นของคุณประกอบได้" นี่คือตัวอย่างโค้ดที่เขาใช้อธิบายแนวคิด:

    ;; Bad
    (defn complex-process []
      (let [a (get-component @global-state)
            b (subprocess-one a) 
            c (subprocess-two a b)
            d (subprocess-three a b c)]
        (reset! global-state d)))
    
    ;; Good
    (defn complex-process [state]
      (-> state
        subprocess-one
        subprocess-two
        subprocess-three))
    

    ฉันเข้าใจว่าโปรแกรมเมอร์ส่วนใหญ่ไม่คุ้นเคยกับ Clojure ดังนั้นฉันจะเขียนมันใหม่ในลักษณะที่จำเป็น:

    ;; Good
    def complex-process(State state)
      state = subprocess-one(state)
      state = subprocess-two(state)
      state = subprocess-three(state)
      return state
    

    นี่คือข้อดี:

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

ตอนนี้กลับไปที่สถานการณ์ของฉัน: ฉันใช้บทเรียนนี้และนำไปใช้กับเกมของฉัน นั่นคือฟังก์ชั่นระดับสูงเกือบทั้งหมดของฉันจะรับและส่งคืนgameStateวัตถุ วัตถุนี้มีข้อมูลทั้งหมดของเกม EG: รายการ badGuys รายการเมนูของขวัญบนพื้น ฯลฯ นี่คือตัวอย่างของฟังก์ชั่นอัพเดทของฉัน:

update(gameState)
  ...
  gameState = handleUnitCollision(gameState)
  ...
  gameState = handleLoot(gameState)
  ...

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

ผมกังวลว่าวันหนึ่งผมจะตื่นขึ้นและตระหนักถึงการออกแบบทั้งหมดนี้คือการเสแสร้งและฉันเพิ่งจริงๆรับการดำเนินการลูกบิ๊กโคลนต่อต้านรูปแบบ


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

ปรับปรุง

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

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

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

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

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

มีที่สำหรับค้นหาการใช้งานอ้างอิง (เช่น: schema) การใช้การอ้างอิงนี้เป็นรหัสที่เกมใช้เพื่อไม่ให้ล้าสมัย

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

หากสถาปัตยกรรมนี้พังลงในที่สุดภายใต้น้ำหนักของมันเองฉันจะเพิ่มการอัปเดตครั้งที่สอง มิฉะนั้นสมมติว่าทุกอย่างเป็นไปด้วยดี :)


2
คำถามยอดเยี่ยม (+1)! ฉันพบว่ามันเป็นแบบฝึกหัดที่มีประโยชน์มากในการลองใช้สำนวนที่ใช้งานได้ในภาษาที่ไม่ใช้การได้
Giorgio

15
ใครก็ตามที่กำลังจะบอกคุณว่าการซ่อนข้อมูลในลักษณะ OO (ด้วยคุณสมบัติและฟังก์ชั่นการเข้าถึง) เป็นสิ่งที่ไม่ดีเนื่องจากการเข้าชม (มักจะเล็กน้อย) ประสิทธิภาพการทำงานแล้วบอกให้คุณเปลี่ยนพารามิเตอร์ทั้งหมดของคุณลงในแผนที่ ค่าใช้จ่าย (มากขึ้น) ของการค้นหาแฮชทุกครั้งที่คุณพยายามดึงค่าสามารถเพิกเฉยได้อย่างปลอดภัย
Mason Wheeler

4
@MasonWheeler ให้บอกว่าคุณพูดถูก คุณจะโมฆะทุกจุดที่เขาทำเพราะสิ่งนี้ผิดหรือเปล่า?
Daniel Kaplan

9
ใน Python (และฉันเชื่อว่าภาษาแบบไดนามิกส่วนใหญ่รวมถึง Javascript) วัตถุเป็นเพียงแค่ไวยากรณ์น้ำตาลสำหรับ dict / แผนที่อย่างไรก็ตาม
โกหก

6
@EvanPlaice: สัญกรณ์ Big-O สามารถหลอกลวงได้ ความจริงง่ายๆคืออะไรที่ช้าเมื่อเทียบกับการเข้าถึงโดยตรงกับสองหรือสามคำแนะนำรหัสเครื่องแต่ละและสิ่งที่เกิดขึ้นบ่อยครั้งเป็นฟังก์ชั่นการโทร, ค่าใช้จ่ายที่จะเพิ่มขึ้นอย่างรวดเร็ว
Mason Wheeler

คำตอบ:


42

ฉันสนับสนุนแอปพลิเคชันที่ 'ทุกอย่างเป็นแผนที่' มาก่อน มันเป็นความคิดที่แย่มาก โปรดอย่าทำมัน!

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

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

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

แก้ไข - ตัวอย่าง

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

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

ฟังก์ชั่นถัดไปจะดึงข้อมูลจากฐานข้อมูล หรือว่าพวกเขาต้องการส่งแผนที่ไปยังชั้นการเข้าถึงข้อมูล DAL จะตรวจสอบว่าแผนที่มีค่าบางอย่างเพื่อควบคุมวิธีการดำเนินการค้นหาหรือไม่ หาก 'justcount' เป็นคีย์ดังนั้นข้อความค้นหาจะเป็น 'count select foo from bar' ฟังก์ชั่นใด ๆ ที่เคยเรียกว่าอาจมีฟังก์ชั่นที่เพิ่ม 'justcount' ลงในแผนที่ ผลลัพธ์ของแบบสอบถามจะถูกเพิ่มลงในแผนที่เดียวกัน

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

รหัสนี้มีความยุ่งเหยิงอย่างมีประสิทธิภาพซึ่งคุณต้องเข้าใจว่ามันสมบูรณ์ที่จะรู้ว่ามีรายการใดรายการหนึ่งในแผนที่มาจากไหน


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

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

28

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

ตัวอย่างเช่นลองตอบคำถามต่อไปนี้เกี่ยวกับแต่ละฟังก์ชั่นกระบวนการย่อย:

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

ด้วยรูปแบบนี้คุณไม่สามารถตอบคำถามเหล่านั้นได้โดยไม่ต้องอ่านฟังก์ชั่นทั้งหมด

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


2
"ประโยชน์ของการเปลี่ยนแปลงไม่ได้ลงไปทางวัตถุที่ไม่เปลี่ยนรูปของคุณจะใหญ่ขึ้น" ทำไม? นั่นคือความคิดเห็นเกี่ยวกับประสิทธิภาพหรือการบำรุงรักษา? โปรดอธิบายอย่างละเอียดในประโยคนั้น
Daniel Kaplan

8
@tieTYT Immutables ทำงานได้ดีเมื่อมีบางสิ่งเล็ก ๆ (เช่นประเภทตัวเลข) คุณสามารถคัดลอกสร้างสร้างทิ้งพวกเขาลดน้ำหนักพวกเขาด้วยราคาที่ค่อนข้างต่ำ เมื่อคุณเริ่มจัดการกับสถานะเกมทั้งหมดที่ประกอบไปด้วยแผนที่ขนาดใหญ่และขนาดใหญ่ต้นไม้รายชื่อและหลายสิบหากไม่ใช่ตัวแปรหลายร้อยค่าใช้จ่ายในการคัดลอกหรือลบค่าใช้จ่ายจะเพิ่มขึ้น

3
ฉันเห็น. นั่นเป็นปัญหา "ข้อมูลที่ไม่เปลี่ยนรูปแบบในภาษาที่มีความจำเป็น" หรือปัญหา "ข้อมูลที่ไม่เปลี่ยนรูปแบบ" หรือไม่? IE: อาจจะไม่เป็นปัญหาในรหัส Clojure แต่ฉันเห็นได้ว่ามันเป็นอย่างไรใน JS นอกจากนี้ยังเป็นความเจ็บปวดในการเขียนรหัสสำเร็จรูปทั้งหมดเพื่อทำ
Daniel Kaplan

3
@MichaelT และ Karl: เพื่อความยุติธรรมคุณควรพูดถึงอีกด้านหนึ่งของเรื่องราวที่เปลี่ยนแปลงไม่ได้ / ประสิทธิภาพ ใช่การใช้งานที่ไร้เดียงสาอาจไม่มีประสิทธิภาพอย่างน่ากลัวนั่นคือสาเหตุที่ผู้คนเข้าใกล้ด้วยวิธีที่ดีกว่า ดูผลงานของ Chris Okasaki สำหรับข้อมูลเพิ่มเติม

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

12

สิ่งที่คุณดูเหมือนจะทำคือรัฐ monad ด้วยตนเองได้อย่างมีประสิทธิภาพ; สิ่งที่ฉันจะทำคือสร้างคอมบิเนเตอร์แบบย่อ (ผูกมัด) และแสดงการเชื่อมต่อระหว่างขั้นตอนตรรกะของคุณอีกครั้งโดยใช้:

function stateBind() {
    var computation = function (state) { return state; };
    for ( var i = 0 ; i < arguments.length ; i++ ) {
        var oldComp = computation;
        var newComp = arguments[i];
        computation = function (state) { return newComp(oldComp(state)); };
    }
    return computation;
}

...

stateBind(
  subprocessOne,
  subprocessTwo,
  subprocessThree,
);

คุณยังสามารถใช้stateBindเพื่อสร้างกระบวนการย่อยต่าง ๆ จากกระบวนการย่อยและดำเนินการกับแผนผังของ combinators รวมเพื่อจัดโครงสร้างการคำนวณของคุณอย่างเหมาะสม

สำหรับคำอธิบายของรัฐ monad ที่ไม่ได้ระบุและการแนะนำที่ยอดเยี่ยมแก่พระมหากษัตริย์โดยทั่วไปใน JavaScript ดูโพสต์บล็อกนี้


1
ตกลงฉันจะดูว่า (และแสดงความคิดเห็นในภายหลัง) แต่คุณคิดอย่างไรเกี่ยวกับแนวคิดของการใช้รูปแบบนี้?
Daniel Kaplan

1
@tietyT ฉันคิดว่ารูปแบบตัวเองเป็นความคิดที่ดีมาก Monad ของรัฐโดยทั่วไปเป็นเครื่องมือสร้างโครงสร้างโค้ดที่มีประโยชน์สำหรับอัลกอริธึมหลอกที่ไม่แน่นอน (อัลกอริทึมที่ไม่เปลี่ยนรูป แต่เลียนแบบความไม่แน่นอน)
Flame ของ Ptharien

2
+1 สำหรับการสังเกตว่ารูปแบบนี้เป็นหลัก Monad อย่างไรก็ตามฉันไม่เห็นด้วยว่ามันเป็นความคิดที่ดีในภาษาที่มีความไม่แน่นอน Monad เป็นวิธีที่จะให้ความสามารถของ globals / สถานะที่ไม่แน่นอนในภาษาที่ไม่อนุญาตการกลายพันธุ์ IMO ในภาษาที่ไม่บังคับใช้รูปแบบ Monad นั้นเป็นเพียงแค่การช่วยตัวเองทางจิต
โกหก

6
@LieRyan Monads โดยทั่วไปแล้วไม่มีอะไรเกี่ยวข้องกับความไม่แน่นอนหรือกลมกลืน; เฉพาะรัฐ monad เท่านั้นทำ (เพราะนั่นคือสิ่งที่มันโดยเฉพาะถูกออกแบบมาเพื่อทำ) ฉันไม่เห็นด้วยที่รัฐ monad ไม่มีประโยชน์ในภาษาที่มีความไม่แน่นอนแม้ว่าการดำเนินการโดยอาศัยความไม่แน่นอนภายใต้อาจมีประสิทธิภาพมากกว่าที่ฉันเปลี่ยนไม่ได้ (แม้ว่าฉันจะไม่แน่ใจในเรื่องนั้นก็ตาม) อินเทอร์เฟซแบบ monadic สามารถมอบความสามารถระดับสูงที่ไม่สามารถเข้าถึงได้อย่างง่ายดายผู้stateBindประสานงานที่ฉันให้เป็นตัวอย่างที่ง่ายมาก
เปลวไฟของ Ptharien

1
@LieRyan ฉันสองความเห็นของ Ptharien - monads ส่วนใหญ่ไม่เกี่ยวกับสถานะหรือความไม่แน่นอนและแม้แต่ที่กล่าวคือไม่ได้เกี่ยวกับสถานะของโลกโดยเฉพาะ Monads ใช้งานได้ดีในภาษา OO / ภาษาบังคับ / ภาษาที่ไม่แน่นอน

11

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

Fogus:ดังนั้นเมื่อความซับซ้อนของเหตุการณ์ลดลง Clojure จะช่วยแก้ปัญหาได้อย่างไร ตัวอย่างเช่นกระบวนทัศน์เชิงวัตถุในอุดมคตินั้นมีวัตถุประสงค์เพื่อส่งเสริมการใช้ซ้ำ แต่ Clojure ไม่เชิงวัตถุแบบคลาสสิก - เราจะวางโครงสร้างโค้ดของเราเพื่อนำมาใช้ใหม่ได้อย่างไร

Hickey:ฉันจะโต้แย้งเกี่ยวกับ OO และนำกลับมาใช้ใหม่ แต่แน่นอนว่าการสามารถนำสิ่งต่าง ๆ กลับมาใช้ใหม่ได้ทำให้ปัญหาในมือง่ายขึ้นเนื่องจากคุณไม่ได้สร้างนวัตกรรมล้อแทนที่จะสร้างรถยนต์ และ Clojure อยู่ใน JVM ทำให้มีล้อจำนวนมาก - ห้องสมุด - พร้อมใช้งาน อะไรทำให้ห้องสมุดใช้ซ้ำได้? ควรทำสิ่งหนึ่งหรือสองสามอย่างดีพอเพียงพอสมควรและสร้างความต้องการเล็กน้อยเกี่ยวกับรหัสลูกค้า ไม่มีสิ่งใดที่หลุดออกจาก OO และไม่ใช่ไลบรารี Java ทั้งหมดที่ตรงตามเกณฑ์นี้ แต่มีหลายอย่างที่ทำ

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

รูปแบบการเชื่อมโยงนี้เป็นเพียงหนึ่งในหลาย abstractions ที่มาพร้อมกับ Clojure และสิ่งเหล่านี้เป็นรากฐานที่แท้จริงของวิธีการที่จะนำมาใช้ใหม่: ฟังก์ชั่นเกี่ยวกับ abstractions การเปิดฟังก์ชั่นชุดใหญ่และเปิดใช้งานได้กับชุดเปิดที่มีขนาดเล็กและขยายได้นั้นเป็นกุญแจสำคัญในการนำอัลกอริทึมกลับมาใช้ใหม่และการทำงานร่วมกันของไลบรารี ฟังก์ชั่นส่วนใหญ่ของ Clojure ถูกกำหนดในรูปแบบของนามธรรมและผู้เขียนห้องสมุดออกแบบรูปแบบอินพุตและเอาท์พุตในแง่ของฟังก์ชั่นเหล่านั้นเช่นกันทำให้ตระหนักถึงการทำงานร่วมกันอย่างมากระหว่างห้องสมุดที่พัฒนาขึ้นอย่างอิสระ สิ่งนี้ตรงกันข้ามกับ DOM และสิ่งต่าง ๆ ที่คุณเห็นใน OO แน่นอนคุณสามารถทำนามธรรมที่คล้ายกันใน OO ด้วยอินเทอร์เฟซตัวอย่างเช่นคอลเลกชัน java.util แต่คุณสามารถทำได้อย่างง่ายดายเช่นเดียวกับใน java.io

Fogus ย้ำประเด็นเหล่านี้ในหนังสือของเขาจาวาสคริปต์การทำงาน :

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

หากการดำเนินการเดียวที่เราสามารถดำเนินการกับวัตถุหนังสือหรืออินสแตนซ์ของประเภทพนักงานคือ setTitle หรือ getSSN เราจะล็อคข้อมูลของเราเป็นภาษาไมโครต่อชิ้นข้อมูล (Hickey 2011) วิธีการที่ยืดหยุ่นมากขึ้นในการสร้างแบบจำลองข้อมูลเป็นเทคนิคการเชื่อมโยงข้อมูล วัตถุจาวาสคริปต์แม้ลบด้วยเครื่องจักรต้นแบบเป็นยานพาหนะในอุดมคติสำหรับการสร้างแบบจำลองข้อมูลที่เชื่อมโยงซึ่งค่าที่ตั้งชื่อสามารถจัดโครงสร้างเพื่อสร้างแบบจำลองข้อมูลระดับสูงกว่าเข้าถึงได้ในรูปแบบที่เหมือนกัน

แม้ว่าเครื่องมือสำหรับการจัดการและการเข้าถึงวัตถุ JavaScript ในขณะที่แมปข้อมูลนั้นเบาบางภายใน JavaScript เอง แต่ Underscore ยังให้ประโยชน์ในการใช้งานที่เป็นประโยชน์ ในบรรดาฟังก์ชั่นที่เข้าใจง่ายที่สุดคือ _.keys, _.values ​​และ _.pluck ทั้ง _.keys และ _.values ​​ตั้งชื่อตามหน้าที่การใช้งานของพวกเขาซึ่งจะนำวัตถุและส่งกลับอาร์เรย์ของคีย์หรือค่าของมัน ...


2
ฉันอ่านบทสัมภาษณ์ Fogus / Hickey นี้ก่อนหน้านี้ แต่ฉันไม่สามารถเข้าใจสิ่งที่เขาพูดถึงจนถึงตอนนี้ ขอบคุณสำหรับคำตอบ. ยังไม่แน่ใจว่า Hickey / Fogus จะให้การออกแบบแก่พวกเขาหรือไม่ ฉันกังวลฉันเอาวิญญาณของคำแนะนำของพวกเขาไปมาก
Daniel Kaplan

9

ผู้สนับสนุนของปีศาจ

ฉันคิดว่าคำถามนี้ควรได้รับการสนับสนุนจากปีศาจ (แต่แน่นอนว่าฉันมีอคติ) ฉันคิดว่า @KarlBielefeldt ทำคะแนนได้ดีมากและฉันต้องการพูดถึงพวกเขา ก่อนอื่นฉันอยากจะบอกว่าคะแนนของเขายอดเยี่ยม

เนื่องจากเขากล่าวว่านี่ไม่ใช่รูปแบบที่ดีแม้ในการเขียนโปรแกรมใช้งานฉันจะพิจารณา JavaScript และ / หรือ Clojure ในคำตอบของฉัน ความคล้ายคลึงกันที่สำคัญอย่างหนึ่งระหว่างสองภาษานี้คือพวกเขาพิมพ์แบบไดนามิก ฉันเห็นด้วยกับประเด็นของเขามากขึ้นถ้าฉันใช้ภาษานี้ในรูปแบบคงที่เช่น Java หรือ Haskell แต่ฉันจะพิจารณาทางเลือกของรูปแบบ "Everything is a Map" เพื่อเป็น OOP แบบดั้งเดิมใน JavaScriptและไม่ใช่ภาษาที่พิมพ์แบบคงที่ (ฉันหวังว่าฉันไม่ได้ตั้งอาร์กิวเมนต์แบบชาวฟางด้วยการทำสิ่งนี้ โปรดแจ้งให้เราทราบ)

ตัวอย่างเช่นลองตอบคำถามต่อไปนี้เกี่ยวกับแต่ละฟังก์ชั่นกระบวนการย่อย:

  • เขตข้อมูลใดของรัฐที่ต้องใช้

  • ฟิลด์ใดที่แก้ไข

  • ฟิลด์ใดไม่เปลี่ยนแปลง

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

  1. อ่านเอกสาร
  2. ดูที่ฟังก์ชั่นร่างกาย
  3. ดูการทดสอบ
  4. คาดเดาและเรียกใช้โปรแกรมเพื่อดูว่ามันทำงาน

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

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

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

แต่ถ้าคุณอนุญาตให้ฉันใช้การแบ่งขั้วที่ผิด: เปรียบเทียบกับการเขียน JavaScript ในแบบ OOP แบบดั้งเดิมดูเหมือนว่า "ทุกอย่างคือแผนที่" ดีกว่า ในวิธี OOP แบบดั้งเดิมฟังก์ชั่นอาจต้องการแก้ไขหรือเพิกเฉยต่อสถานะที่คุณส่งผ่านหรือระบุว่าคุณไม่ผ่านด้วยรูปแบบ "ทุกอย่างเป็นแผนที่" คุณเพียงต้องการแก้ไขหรือเพิกเฉยต่อสถานะที่คุณผ่าน ใน.

  • คุณสามารถจัดลำดับของฟังก์ชั่นใหม่ได้อย่างปลอดภัยหรือไม่?

ในรหัสของฉันใช่ ดูความคิดเห็นที่สองของฉันต่อคำตอบของ @ Evicatos บางทีนี่อาจเป็นเพราะฉันทำเกม แต่ฉันพูดไม่ได้ ในเกมที่อัปเดต 60x ต่อวินาทีมันไม่สำคัญว่าจะเป็นเช่นdead guys drop lootนั้นgood guys pick up lootหรือในทางกลับกัน แต่ละฟังก์ชั่นยังคงทำสิ่งที่ควรทำโดยไม่คำนึงถึงลำดับที่พวกเขาทำงาน ข้อมูลเดียวกันจะถูกป้อนเข้าในการupdateโทรที่ต่างกันหากคุณสลับการสั่งซื้อ หากคุณมีgood guys pick up lootแล้วdead guys drop lootคนดีจะหยิบปล้นในครั้งต่อไปupdateและมันก็ไม่ใช่เรื่องใหญ่ มนุษย์จะไม่สามารถสังเกตเห็นความแตกต่าง

อย่างน้อยนี่เป็นประสบการณ์โดยทั่วไปของฉัน ฉันรู้สึกอ่อนแอมากที่ยอมรับสิ่งนี้แบบสาธารณะ อาจจะพิจารณานี้จะโอเคเป็นมาก , มากสิ่งที่ไม่ดีที่จะทำ แจ้งให้เราทราบหากฉันทำผิดพลาดอย่างร้ายแรงที่นี่ แต่ถ้าผมมีมันเป็นเรื่องง่ายมากที่จะจัดเรียงฟังก์ชั่นเพื่อการสั่งซื้อdead guys drop lootแล้วgood guys pick up lootอีกครั้ง มันจะใช้เวลาน้อยกว่าเวลาที่ใช้ในการเขียนย่อหน้านี้: P

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

เป็นเรื่องปกติที่จะเขียนขั้นตอนแบบแยกคู่ด้วยรูปแบบนี้ แต่ก็ยากที่จะสังเกตเห็นขั้นตอนคู่ของคุณใน OOP แบบดั้งเดิม ถ้าผมเขียน OOP แบบดั้งเดิมธรรมชาติวิธีไร้เดียงสาของความคิดคือการทำให้dead guys drop lootผลตอบแทนวัตถุที่ฉันมีที่จะผ่านเข้ามาLoot good guys pick up lootฉันจะไม่สามารถเรียงลำดับการดำเนินการเหล่านั้นได้ใหม่ตั้งแต่ครั้งแรกที่คืนค่าอินพุตของวินาที

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

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

นอกจากนี้ประโยชน์ของการเปลี่ยนไม่ได้จะลดลงเมื่อวัตถุที่ไม่เปลี่ยนรูปของคุณมีขนาดใหญ่ขึ้น

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


4
"พารามิเตอร์ตัวแรกของฟังก์ชันอาจชื่อ foo แต่นั่นคืออะไร" นั่นเป็นเหตุผลที่คุณไม่ตั้งชื่อพารามิเตอร์ของคุณ "foo" แต่ "repetitions", "parent" และชื่ออื่น ๆ ที่ทำให้เห็นสิ่งที่คาดหวังเมื่อรวมกับชื่อฟังก์ชัน
เซบาสเตียนเรดล

1
ฉันต้องเห็นด้วยกับคุณในทุกจุด ปัญหาเดียวที่จาวาสคริปต์ใช้กับรูปแบบนี้คือคุณกำลังทำงานกับข้อมูลที่ไม่แน่นอนและดังนั้นคุณจึงมีแนวโน้มที่จะกลายพันธุ์ อย่างไรก็ตามมีห้องสมุดที่ช่วยให้คุณเข้าถึงโครงสร้างข้อมูล clojure ในจาวาสคริปต์ธรรมดา ๆ ได้ แต่ฉันลืมสิ่งที่เรียกว่า การส่งผ่านข้อโต้แย้งเป็นวัตถุไม่เคยได้ยินมาก่อน jQuery ทำหลาย ๆ ที่นี้ แต่บันทึกว่าส่วนใดของวัตถุที่พวกเขาใช้ โดยส่วนตัวแล้วฉันจะแยก UI- ฟิลด์และ GameLogic-fields แต่อะไรก็ตามที่เหมาะกับคุณ :)
Robin Heggelund Hansen

@SebastianRedl ฉันควรทำparentอย่างไร คือrepetitionsและอาเรย์ของตัวเลขหรือสตริงหรือไม่ก็ไม่สำคัญ? หรือบางทีการทำซ้ำเป็นเพียงตัวเลขที่แสดงถึงจำนวนการตอบโต้ที่ฉันต้องการ มีมากมายของ API ของออกมีที่เพียงแค่ใช้ตัวเลือกวัตถุ โลกเป็นสถานที่ที่ดีกว่าถ้าคุณตั้งชื่ออย่างถูกต้อง แต่ไม่รับประกันว่าคุณจะรู้วิธีใช้ api ไม่มีคำถามที่ถาม
Daniel Kaplan

8

ฉันพบว่ารหัสของฉันมีแนวโน้มที่จะจบลงด้วยโครงสร้างเช่น:

  • ฟังก์ชั่นที่ใช้แผนที่มักจะมีขนาดใหญ่ขึ้นและมีผลข้างเคียง
  • ฟังก์ชั่นที่รับการโต้แย้งมีแนวโน้มที่จะเล็กลงและบริสุทธิ์

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

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

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

ตัวอย่างเช่น,

function foo (post) {
  check(post, {
    text: String,
    timestamp: Date,
    // Optional, but if present must be an array of strings
    tags: Match.Optional([String])
    });

  // do stuff
}

ดูhttp://docs.meteor.com/#matchเพิ่มเติม

:: อัพเดท ::

การบันทึกวิดีโอของ Stuart Sierra ของ Clojure / West "Clojure in the Large"ก็มีผลต่อเรื่องนี้เช่นกัน เช่นเดียวกับ OP เขาควบคุมผลข้างเคียงซึ่งเป็นส่วนหนึ่งของแผนที่ดังนั้นการทดสอบจึงง่ายขึ้นมาก นอกจากนี้เขายังมีโพสต์บล็อกโดยสรุปขั้นตอนการทำงานปัจจุบันของ Clojure ซึ่งดูเหมือนว่าเกี่ยวข้องกัน


1
ฉันคิดว่าความคิดเห็นของฉันต่อ @Evicatos จะอธิบายตำแหน่งของฉันอย่างละเอียดที่นี่ ใช่ฉันกลายพันธุ์และฟังก์ชั่นไม่บริสุทธิ์ แต่ฟังก์ชั่นของฉันมันง่ายต่อการทดสอบโดยเฉพาะอย่างยิ่งในการเข้าใจถึงปัญหาสำหรับข้อบกพร่องถดถอยผมไม่ได้วางแผนในการทดสอบ เครดิตครึ่งหนึ่งไปที่ JS: มันง่ายมากที่จะสร้าง "แผนที่" / วัตถุด้วยข้อมูลที่ฉันต้องการสำหรับการทดสอบ จากนั้นก็ง่ายพอ ๆ กับการผ่านเข้าไปและตรวจสอบการกลายพันธุ์ ผลข้างเคียงมักจะแสดงอยู่ในแผนที่ดังนั้นจึงง่ายต่อการทดสอบ
Daniel Kaplan

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

5

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

สิ่งที่หมายถึงคือโปรแกรมเมอร์ในอนาคตใน codebase จะต้องรู้ว่าฟังก์ชั่นที่ถูกเรียกใช้ทำงานภายใน - และการเรียกใช้ฟังก์ชั่นที่ซ้อนกัน - เพื่อที่จะเรียกมันว่า

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


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

1
BTW: อีก: อาร์กิวเมนต์หลักที่ต่อต้านมัน: ดูเหมือนว่าเป็นจริงไม่ว่าจะเป็นในการปิดบังหรือจาวาสคริปต์ แต่มันเป็นจุดที่มีค่าที่จะทำให้ บางทีผลประโยชน์ที่ได้รับการจดทะเบียนอาจมีค่ามากกว่าเชิงลบของสิ่งนั้น
Daniel Kaplan

2
ตอนนี้ฉันรู้แล้วว่าทำไมฉันถึงรบกวนมันไปรอบ ๆ แม้ว่ามันจะเป็นตัวแปรทั่วโลก: มันทำให้ฉันสามารถเขียนฟังก์ชันที่บริสุทธิ์ได้ ถ้าฉันเปลี่ยนฉันgameState = f(gameState)ไปมันยากมากที่จะทดสอบ f() อาจกลับมาเป็นสิ่งที่แตกต่างกันทุกครั้งที่ฉันเรียกมันว่า แต่มันง่ายที่จะคืนสิ่งเดียวกันทุกครั้งที่ได้รับอินพุทเดียวกัน ff()f(gameState)
Daniel Kaplan

3

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

update(gameState)
  ...
  gameState = handleUnitCollision(gameState)
  ...
  gameState = handleLoot(gameState)
  ...

และ

{
  ...
  handleUnitCollision: function() {
    ...
  },
  ...
  handleLoot: function() {
    ...
  },
  ...
  update: function() {
    ...
    this.handleUnitCollision()
    ...
    this.handleLoot()
    ...
  },
  ...
};

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


คำถามทั่วไปที่เกี่ยวข้องเพิ่มเติม [ programmers.stackexchange.com/questions/260309/…การสร้างแบบจำลองข้อมูลกับคลาสดั้งเดิม)
7610

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

@tietyT คุณไม่ได้ทำการเขียนโปรแกรมเชิงวัตถุดังนั้นมันก็โอเค
user7610

คุณมาถึงข้อสรุปอย่างไร (หนึ่ง "มันก็โอเค")?
Daniel Kaplan

ปัญหากับพระเจ้าใน OO คือ "วัตถุนั้นตระหนักถึงทุกอย่างหรือวัตถุทั้งหมดนั้นขึ้นอยู่กับวัตถุของพระเจ้าว่าเมื่อมีการเปลี่ยนแปลงหรือแก้ไขข้อผิดพลาด แหล่งที่มาในรหัสของคุณมีวัตถุอื่น ๆ นอกเหนือจากวัตถุพระเจ้าดังนั้นส่วนที่สองไม่เป็นปัญหา ในส่วนแรกวัตถุของพระเจ้าคือโปรแกรมและโปรแกรมของคุณควรตระหนักถึงทุกสิ่ง นั่นก็โอเค
user7610

2

ฉันเพิ่ง sorta ต้องเผชิญกับหัวข้อนี้ก่อนหน้านี้ในขณะที่เล่นกับโครงการใหม่ ฉันกำลังทำงานใน Clojure เพื่อสร้างเกมโป๊กเกอร์ ฉันแสดงค่าใบหน้าและความเหมาะสมเป็นคำหลักและตัดสินใจที่จะแสดงบัตรเป็นแผนที่เช่น

{ :face :queen :suit :hearts }

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

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

(defn face [card] (card :face))
(defn suit [card] (card :suit))

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

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

class State
  def complex-process()
    state = clone(this) ; or just use 'this' below if mutation is fine
    state.subprocess-one()
    state.subprocess-two()
    state.subprocess-three()
    return state

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


2

จากสิ่งที่ (น้อย) ฉันเคยเห็นการใช้แผนที่หรือโครงสร้างที่ซ้อนกันอื่น ๆ เพื่อทำให้วัตถุสถานะไม่เปลี่ยนรูปแบบระดับโลกเดียวเช่นนี้เป็นเรื่องปกติในภาษาที่ใช้งานได้อย่างน้อยก็บริสุทธิ์โดยเฉพาะอย่างยิ่งเมื่อใช้สถานะ Monad เป็น@ Ptharien'sFlame mentioend

สองสิ่งกีดขวางบนถนนเพื่อใช้สิ่งนี้อย่างมีประสิทธิภาพที่ฉันเคยเห็น / อ่านเกี่ยวกับ (และคำตอบอื่น ๆ ที่นี่ได้กล่าวถึง) คือ:

  • การกลายเป็นค่าที่ซ้อนกัน (ลึก) ในสถานะ (ไม่เปลี่ยนรูป)
  • การซ่อนสถานะส่วนใหญ่จากฟังก์ชั่นที่ไม่ต้องการและเพียงให้สิ่งเล็ก ๆ น้อย ๆ ที่พวกเขาต้องการในการทำงาน / กลายพันธุ์

มีเทคนิค / รูปแบบทั่วไปสองสามแบบที่สามารถช่วยบรรเทาปัญหาเหล่านี้ได้:

ประการแรกคือZippers : สิ่งนี้ยอมให้การสำรวจและการกลายพันธุ์ของรัฐลึกลงไปภายในลำดับชั้นซ้อนที่ไม่เปลี่ยนรูป

อีกอย่างคือเลนส์ : สิ่งเหล่านี้ช่วยให้คุณมุ่งเน้นไปที่โครงสร้างไปยังสถานที่เฉพาะและอ่าน / เปลี่ยนแปลงค่าที่นั่น คุณสามารถรวมเลนส์ที่แตกต่างกันเข้าด้วยกันเพื่อมุ่งเน้นไปที่สิ่งต่าง ๆ เช่นห่วงโซ่คุณสมบัติที่ปรับได้ใน OOP (ซึ่งคุณสามารถแทนที่ตัวแปรสำหรับชื่อคุณสมบัติจริง!)

Prismatic เพิ่งโพสต์บล็อกเกี่ยวกับการใช้เทคนิคประเภทนี้เหนือสิ่งอื่นใดใน JavaScript / ClojureScript ซึ่งคุณควรตรวจสอบ พวกเขาใช้เคอร์เซอร์ (ซึ่งเปรียบเทียบกับรูดซิป) กับสถานะหน้าต่างสำหรับฟังก์ชั่น:

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

IIRC พวกเขายังสัมผัสกับความไม่สามารถเปลี่ยนแปลงได้ใน JavaScript ในโพสต์นั้น


Talk OP ที่กล่าวถึงยังกล่าวถึงการใช้ฟังก์ชั่นอัพเดทเพื่อ จำกัด ขอบเขตที่ฟังก์ชั่นสามารถอัปเดตเป็นแผนผังย่อยของแผนที่รัฐ ฉันคิดว่ายังไม่มีใครนำสิ่งนี้มาใช้
user7610

@ user7610 จับได้ดีฉันไม่อยากจะเชื่อว่าฉันลืมพูดถึงสิ่งนั้น - ฉันชอบฟังก์ชั่นนั้น (และassoc-inอื่น ๆ ) เดาฉันเพิ่งมี Haskell ในสมอง ฉันสงสัยว่ามีใครทำพอร์ต JavaScript ของมันหรือไม่ ผู้คนอาจไม่ได้นำมันมาเพราะ (อย่างฉัน) พวกเขาไม่ได้ดูคำปราศรัย :)
พอล

@ พอลในแง่ที่พวกเขามีเพราะมันมีอยู่ใน ClojureScript แต่ฉันไม่แน่ใจว่า "นับ" ในใจของคุณ อาจมีอยู่ใน PureScript และฉันเชื่อว่ามีอย่างน้อยหนึ่งไลบรารีที่ให้โครงสร้างข้อมูลที่ไม่เปลี่ยนรูปแบบใน JavaScript ฉันหวังว่าอย่างน้อยหนึ่งในนั้นจะมีสิ่งเหล่านี้มิฉะนั้นพวกเขาจะใช้งานไม่สะดวก
Daniel Kaplan

@tietyT ฉันคิดถึงการใช้งาน JS ดั้งเดิมเมื่อฉันแสดงความคิดเห็น แต่คุณได้ทำสิ่งที่ดีเกี่ยวกับ ClojureScript / PureScript ฉันควรตรวจสอบ JS ที่ไม่เปลี่ยนแปลงและดูว่ามีอะไรบ้างฉันไม่เคยทำงานมาก่อน
paul

1

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

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

หากคุณกำลังทำสำเนาของรัฐอย่างชัดเจนให้แก้ไขจากนั้นส่งคืนสำเนาที่แก้ไขแล้วดำเนินการต่อ ฉันไม่แน่ใจว่าเป็นวิธีที่ดีที่สุดในการบรรลุสิ่งที่คุณพยายามทำใน Javascript แต่อาจเป็น "ใกล้" กับตัวอย่าง Clojure ที่กำลังทำอยู่


1
ในที่สุดก็เป็นจริง "มากแตกต่างกันมาก"? ในตัวอย่าง Clojure เขาเขียนทับสถานะเก่าของเขาด้วยสถานะใหม่ ใช่ไม่มีการกลายพันธุ์ที่แท้จริงเกิดขึ้นตัวตนกำลังเปลี่ยนไป แต่ในตัวอย่าง "ดี" ของเขาตามที่เขียนไว้เขาไม่มีวิธีรับสำเนาที่ส่งผ่านไปยังกระบวนการย่อยสอง การระบุค่านั้นถูกเขียนทับ ดังนั้นฉันคิดว่าสิ่งที่ "แตกต่างกันมาก" เป็นเพียงรายละเอียดการใช้ภาษา อย่างน้อยในบริบทของสิ่งที่คุณนำมา
Daniel Kaplan

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

1
คุณจะต้องแสดงตัวอย่างให้ฉันเพราะในประสบการณ์ของฉันกับเรื่องนี้ฉันสามารถย้ายสิ่งต่าง ๆ ตามความประสงค์และมันมีผลน้อยมาก เพียงเพื่อพิสูจน์ด้วยตัวเองฉันย้ายการเรียก subprocess แบบสุ่มสองครั้งในระหว่างการupdate()ทำงานของฉัน ฉันเลื่อนไปด้านบนและอีกอันไปด้านล่าง การทดสอบทั้งหมดของฉันยังคงผ่านไปและเมื่อฉันเล่นเกมฉันสังเกตว่าไม่มีผลร้ายใด ๆ ผมรู้สึกว่าฟังก์ชั่นของฉันมีเพียงแค่เป็น composable เป็นตัวอย่าง Clojure เราทั้งคู่กำลังทิ้งข้อมูลเก่าของเราหลังจากแต่ละขั้นตอน
Daniel Kaplan

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

1
@Evicatos - บริสุทธิ์และมีลายเซ็นเดียวกันไม่ได้หมายความว่าลำดับของฟังก์ชั่นไม่สำคัญ ลองนึกภาพการคำนวณราคาด้วยการใช้ส่วนลดคงที่และเปอร์เซนต์ (-> 10 (- 5) (/ 2))ผลตอบแทน 2.5 (-> 10 (/ 2) (- 5))ส่งคืน 0.
แซค

1

หากคุณมีวัตถุสถานะส่วนกลางบางครั้งเรียกว่า "พระเจ้าวัตถุ" ซึ่งถูกส่งผ่านไปยังแต่ละขั้นตอนคุณจะทำให้ปัจจัยหลายอย่างสับสนซึ่งทั้งหมดเพิ่มการมีเพศสัมพันธ์ในขณะที่ลดการทำงานร่วมกันลง ปัจจัยเหล่านี้ล้วนส่งผลกระทบต่อการบำรุงรักษาในระยะยาวในทางลบ

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

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

หากคุณผ่าน "ซิป", "เลนส์" หรือ "เคอร์เซอร์" ตามที่อธิบายไว้ในโพสต์โดย @paul นั่นเป็นสิ่งหนึ่ง คุณจะต้องมีการเข้าถึงและอนุญาตให้ซิป ฯลฯ เพื่อควบคุมการอ่านและการเขียนข้อมูล

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

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

ผลกระทบของระบบ

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

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

  1. เส้นโค้งการเรียนรู้สำหรับวัตถุพระเจ้านั้นเรียบ (กล่าวคือใช้เวลานานมากในการที่จะมีความสามารถ) โปรแกรมเมอร์เพิ่มเติมแต่ละคนจะต้องเรียนรู้ทุกสิ่งที่คุณรู้และเก็บไว้ในหัวของพวกเขา คุณจะสามารถว่าจ้างโปรแกรมเมอร์ที่ดีกว่าคุณได้โดยสมมติว่าคุณสามารถจ่ายเงินให้พวกเขามากพอที่จะประสบกับการรักษาวัตถุขนาดใหญ่
  2. การจัดเตรียมการทดสอบในคำอธิบายของคุณเป็นกล่องสีขาวเท่านั้น คุณจำเป็นต้องรู้ทุกรายละเอียดของวัตถุเทพเจ้ารวมถึงโมดูลที่อยู่ใต้การทดสอบเพื่อตั้งค่าการทดสอบเรียกใช้และกำหนดว่าก) มันทำถูกและข) มันไม่ได้ทำอะไรเลย จาก 10,000 สิ่งที่ผิด อัตราเดิมพันจะซ้อนกับคุณอย่างมาก
  3. การเพิ่มฟีเจอร์ใหม่ต้องการให้คุณ a) ผ่านทุก subprocess และพิจารณาว่าฟีเจอร์นั้นมีผลกับโค้ดใด ๆ หรือไม่และในทางกลับกัน b) ต้องผ่านสภาวะทั่วโลกของคุณและออกแบบการเพิ่มเติมและ c) ผ่านการทดสอบทุกหน่วย เพื่อตรวจสอบว่าหน่วยภายใต้การทดสอบไม่ได้รับผลกระทบในทางลบคุณลักษณะใหม่

ในที่สุด

  1. วัตถุเทพเจ้าที่ไม่แน่นอนนั้นเป็นคำสาปของการมีอยู่ของฉันบางอย่างของฉันเองและบางอย่างที่ฉันติดอยู่
  2. รัฐ monad ไม่ได้ปรับขนาด รัฐเติบโตชี้แจงกับผลกระทบทั้งหมดสำหรับการทดสอบและการดำเนินงานที่หมายถึง วิธีที่เราควบคุมสถานะในระบบที่ทันสมัยคือโดยการมอบหมาย (การแบ่งหน้าที่) และการกำหนดขอบเขต (จำกัด การเข้าถึงเฉพาะส่วนย่อยของรัฐ) วิธีการ "ทุกอย่างเป็นแผนที่" ตรงกันข้ามกับการควบคุมของรัฐ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.