ฟังก์ชั่นชั้นหนึ่งเป็นสิ่งทดแทนรูปแบบกลยุทธ์หรือไม่?


15

รูปแบบการออกแบบกลยุทธ์มักถูกมองว่าเป็นตัวแทนสำหรับฟังก์ชั่นชั้นแรกในภาษาที่ขาดพวกเขา

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

อย่างไรก็ตามฉันคิดเกี่ยวกับมันและตัดสินใจว่าบางทีกลยุทธ์อาจมีมากกว่าฟังก์ชั่นที่ไม่ระบุชื่อธรรมดา

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

ในภาษาเชิงวัตถุที่สนับสนุนฟังก์ชั่นชั้นหนึ่งรูปแบบกลยุทธ์มีความได้เปรียบมากกว่าการใช้ฟังก์ชั่นหรือไม่?


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

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

5
ที่เกี่ยวข้อง: c2.com/cgi/wiki?ClosuresAndObjectsAreEquivalent
Euphoric

คำตอบ:


13

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

ส่วนต่อประสานกลยุทธ์สามารถมีได้หลายวิธี ลองใช้อินเทอร์เฟซRouteFindingStragegyเป็นตัวอย่างซึ่งสรุปเส้นทางการค้นหาเส้นทางที่แตกต่าง มันสามารถประกาศวิธีการเช่น

  • Route findShortestRoute(Node start, Node destination)
  • boolean doesRouteExist(Node start, Node destination)
  • Route[] findAllPossibleRoutes(Node start, Node destination)
  • Route findShortestRouteToClosestDestination(Node start, Node[] destinations)
  • Route findTravelingSalesmanRoute(Node[] stations)

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

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


2
"เมื่อสถานะภายในนี้ซับซ้อนมากมันมักจะเป็นประโยชน์ในการส่งเสริมการปิดการเรียนเต็มเปี่ยม": ทำไม? หากสถานะภายในซับซ้อนขึ้นคุณสามารถใส่วัตถุ / บันทึกที่จัดเก็บไว้ในส่วนปิดได้
Giorgio

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

ดูเหมือนว่าฉันจะมีเหตุผล
Giorgio

5

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

ใช้ตัวอย่างต่อไปนี้ใน Common LISP:

(defun number-strings (ss)
  (let ((counter 0))
    (mapcar #'(lambda (s) (format nil "~a: ~a" (incf counter) s)) ss)))

ฟังก์ชั่นนี้ใช้รายการของสตริงและเติมตัวนับให้กับแต่ละองค์ประกอบของรายการ ตัวอย่างเช่นการเรียกใช้

(number-strings '("a" "b" "c"))

จะช่วยให้

("1: a" "2: b" "3: c")

ฟังก์ชั่นnumber-stringsภายในใช้ฟังก์ชั่นที่ไม่ระบุชื่อกับตัวแปรcounterที่เก็บสถานะ (มูลค่าปัจจุบันของตัวนับ) ซึ่งจะถูกนำมาใช้ใหม่ทุกครั้งที่มีการเรียกใช้ฟังก์ชั่น

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

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


ดังนั้นในภาษาที่รองรับฟังก์ชั่นชั้นหนึ่งเป็นแบบปิดคุณจะยังคงใช้กลยุทธ์ 'คลาสสิค' หรือไม่?
Aviv Cohn

1
ฉันมักจะเห็นด้วยกับ Philipp: มันขึ้นอยู่กับภาษาและความชอบส่วนตัว ฉันมักจะเลือกวิธีที่ทำให้สัญกรณ์ง่ายที่สุดเท่าที่จะทำได้ ตัวอย่างเช่นใน Lisp ฉันสามารถกำหนดสถานะของฉันเป็นรายการของตัวแปรผ่าน a letแล้วกำหนดการปิดของฉันภายใน โดยทั่วไปฉันจะกำหนดวัตถุด้วยวิธีการหนึ่งทันที ในภาษาอื่น (เช่น Java) มันอาจจะสะดวกกว่า (syntactically ง่ายกว่า) เพื่อกำหนดวัตถุที่เหมาะสมเพื่อรักษาสถานะ ดังนั้นฉันจะตัดสินใจจากกรณีที่กรณี
Giorgio

1

เพียงเพราะสองการออกแบบสามารถแก้ปัญหาเดียวกันไม่ได้หมายความว่ามันเป็นการทดแทนโดยตรงสำหรับกันและกัน

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

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


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

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

1
ประเด็นก็คือถ้าคุณใช้ส่วนหนึ่งของกระบวนทัศน์การทำงานเช่นฟังก์ชั่นชั้นหนึ่งไม่ต้องแปลกใจถ้าคุณจำเป็นต้องดึงส่วนอื่น ๆ ของกระบวนทัศน์เพื่อให้ทำงานได้อย่างราบรื่น
Karl Bielefeldt

1

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

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


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

ฉันอยากเห็นด้วยกับ @Snowman คุณแน่ใจว่าคุณกำลังพูดถึงรูปแบบกลยุทธ์ ?
Rowan Freeman

1
@Snowman แม้หน้าเว็บที่คุณเชื่อมโยงไปจะไม่ระบุว่าควรนำรูปแบบนี้ไปใช้อย่างไร แต่ให้ตัวอย่างในภาษาเฉพาะ แต่ไม่มีอะไรในแผนภาพ UML ที่บอกว่าฉันต้องใช้การสืบทอด C ++, อินเตอร์เฟส Java หรือบล็อก Ruby . ดังนั้นฉันไม่เห็นด้วยกับการวิเคราะห์ของคุณ
idoby
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.