อะไรที่ทำให้ Iterator เป็นรูปแบบการออกแบบ?


9

ฉันสงสัยว่ามันคืออะไรที่ทำให้ Iterator เป็นพิเศษเมื่อเทียบกับสิ่งก่อสร้างที่คล้ายกันอื่น ๆ และทำให้Gang of Fourแสดงว่าเป็นรูปแบบการออกแบบ

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

แต่จะเกิดอะไรขึ้นถ้าเราแทนที่ลำดับชั้นของคอลเลกชันด้วยเช่นลำดับชั้นของวัตถุทางคณิตศาสตร์ (จำนวนเต็ม, ลอย, ซับซ้อน, เมทริกซ์ ฯลฯ ) และตัววนซ้ำโดยคลาสที่แสดงถึงการดำเนินการบางอย่างที่เกี่ยวข้องกับวัตถุเหล่านี้ แผนภาพคลาสจะเหมือนกัน

เราอาจพบตัวอย่างที่คล้ายกันอื่น ๆ อีกมากมายเช่น Writer, Painter, Encoder และคนที่ดีกว่าซึ่งทำงานในลักษณะเดียวกัน อย่างไรก็ตามฉันไม่เคยได้ยินชื่อเหล่านี้เรียกว่ารูปแบบการออกแบบ

แล้วอะไรที่ทำให้ Iterator มีความพิเศษ?

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


เพื่อชี้แจงประเด็นของฉันให้ฉันยกตัวอย่างรายละเอียดเพิ่มเติม

นี่คือปัญหาการออกแบบของเรา:

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

นี่คือทางออกที่สมเหตุสมผลสำหรับปัญหาการออกแบบของเรา (ในทางปฏิบัติทั่วไปของรูปแบบตัววนซ้ำ):

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

ตัวอย่าง:

มีชั้นฐานหรืออินเตอร์เฟซที่เป็นMathObject(ชื่อโง่ฉันรู้ว่าอาจจะมีคนที่มีความคิดที่ดี.) กับการเรียนมาและMyInteger MyMatrixสำหรับMathObjectการดำเนินการแต่ละครั้งPowerควรมีการกำหนดที่อนุญาตให้คำนวณสแควร์คิวบ์และอื่น ๆ ดังนั้นเราสามารถเขียน (ใน Java):

MathObject i = new MyInteger(5);
Power powerOfFive = i.getPower();
MyInteger square = powerOfFive.calculate(2); // should return 25
MyInteger cube = powerOfFive.calculate(3); // should return 125

5
"แผนภาพคลาสจะเหมือนกัน" - แล้วอะไรล่ะ รูปแบบการออกแบบไม่ใช่แผนภาพคลาส มันเป็นสิ่งที่เป็นนามธรรมในระดับสูงสำหรับคลาสของการแก้ปัญหาที่เกิดขึ้นซ้ำ
Doc Brown

@DocBrown: ใช่ แต่ไม่ใช่การดำเนินการทางคณิตศาสตร์การเขียนออบเจ็กต์ไปยังไฟล์เอาต์พุตกราฟิกหรือการเข้ารหัสข้อมูลปัญหาที่เกิดซ้ำเหมือนการทำซ้ำ?
Frank Puffer

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

@FrankPuffer หากคุณร่างวิธีแก้ไขปัญหาร่วมกันสำหรับการเขียนวัตถุลงในไฟล์คุณสามารถเขียนวิธีการแก้ปัญหาของคุณและเรียกว่ารูปแบบการเขียนวัตถุหากทำเช่นนั้นจะเป็นประโยชน์
Brandin

3
คุณคิดมากเรื่องนี้ รูปแบบการออกแบบเป็นโซลูชันที่รู้จักกันดีสำหรับปัญหาการคำนวณทั่วไปและนั่นคือทั้งหมดที่มันเป็น คุณใช้รูปแบบเมื่อคุณสามารถจดจำและใช้ประโยชน์ที่ได้รับ
Robert Harvey

คำตอบ:


9

รูปแบบส่วนใหญ่จากหนังสือ GoF มีสิ่งต่อไปนี้ร่วมกัน:

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

ปัญหาที่แก้ไขโดยรูปแบบเหล่านี้เป็นพื้นฐานที่นักพัฒนาหลายคนเข้าใจว่าส่วนใหญ่เป็นวิธีการแก้ปัญหาสำหรับคุณสมบัติภาษาโปรแกรมที่ขาดหายไปซึ่งเป็น IMHO มุมมองที่ถูกต้อง (โปรดทราบว่าหนังสือ GoF มาจาก 1995 ที่ Java และ C ++ คุณสมบัติเป็นวันนี้)

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

เปรียบเทียบสิ่งนี้กับปัญหาที่คุณเลือก:

  • การเขียนไปยังไฟล์ - นั่นคือ IMHO เพียง "พื้นฐาน" ไม่เพียงพอ มันเป็นปัญหาที่เฉพาะเจาะจงมาก หรือมีวิธีแก้ปัญหาที่ดีและมีความสามารถในการพูด - มีหลายวิธีที่แตกต่างกันวิธีการเขียนไปยังไฟล์และไม่มี "แนวปฏิบัติที่ดีที่สุด" ที่ชัดเจน
  • Painter, Encoder: อะไรก็ตามที่คุณมีอยู่ในใจกับปัญหาเหล่านั้นดูพื้นฐานน้อยกว่าสำหรับฉันและไม่ได้แยกออกจากกัน
  • มีฟังก์ชั่น "กำลัง" สำหรับวัตถุชนิดต่าง ๆ : ในแวบแรกที่อาจคุ้มค่ากับรูปแบบ แต่โซลูชันที่คุณเสนอไม่ได้โน้มน้าวฉัน - ดูเหมือนความพยายามที่จะใช้ฟังก์ชั่นพลังงานเป็นสิ่งที่คล้ายกับ รูปแบบตัววนซ้ำ ฉันใช้รหัสจำนวนมากกับการคำนวณทางวิศวกรรม แต่ฉันจำไม่ได้ว่าสถานการณ์ที่วิธีการคล้ายกับวัตถุฟังก์ชันพลังงานของคุณจะช่วยฉันได้อย่างไร (ตัววนซ้ำเป็นสิ่งที่ฉันต้องจัดการในชีวิตประจำวัน)

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


The problems solved by these patterns are so basic that many developers think their main purpose is to be workarounds for missing programming language features- การประชดประชันที่นักพัฒนาซอฟต์แวร์มักใช้รูปแบบการออกแบบซอฟต์แวร์ที่มีอายุ 20 ปีในขณะที่ยังคงเชื่อว่าพวกเขากำลังเขียนโค้ดที่ล้ำสมัย
Robert Harvey

@ RobertHarvey: ฉันไม่คิดว่า devs จำนวนมากจะใช้รูปแบบตัววนซ้ำในวันนี้ใน "OO way" ที่ GoF แนะนำ พวกเขาใช้งานได้ตามปกติโดยวิธีการที่มีให้โดยภาษาหรือเป็น lib มาตรฐาน (ตัวอย่างเช่นใน C # โดยใช้IEnumerableและyield) แต่สำหรับรูปแบบ GoF อื่น ๆ สิ่งที่คุณเขียนอาจเป็นจริง
Doc Brown

1
เกี่ยวข้องกับworkarounds for missing programming language features: blog.plover.com/prog/johnson.html
jrw32982 รองรับโมนิก้า

8

The Gang of Four อ้างคำนิยามรูปแบบของ Christopher Alexander:

แต่ละรูปแบบอธิบายถึงปัญหาที่เกิดขึ้นซ้ำแล้วซ้ำอีกในสภาพแวดล้อมของเราแล้วอธิบายหลักของการแก้ไขปัญหานั้น […]

ปัญหานี้แก้ไขได้อย่างไรโดยผู้ทำซ้ำ

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

...

การบังคับใช้:ใช้รูปแบบ Iterator

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

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

และมันก็เป็นเรื่องดีที่มีรูปแบบของ Iterator สิ่งเลวร้ายเกิดขึ้นหากคุณไม่ได้ใช้ ต่อต้านตัวอย่างที่ฉันชอบคือ Perl ที่นี่แต่ละคอลเลกชัน (อาร์เรย์หรือแฮช) รวมถึงสถานะตัววนซ้ำเป็นส่วนหนึ่งของคอลเลกชัน ทำไมสิ่งนี้ถึงไม่ดี? เราสามารถวนซ้ำแบบแฮชได้ง่ายๆในขณะที่ - แต่ละลูป:

while (my ($key, $value) = each %$hash) {
  say "$key => $value";
}

แต่ถ้าเราเรียกฟังก์ชั่นในเนื้อความลูป

while (my ($key, $value) = each %$hash) {
  do_something_with($key, $value, $hash);
}

ตอนนี้ฟังก์ชั่นนี้อาจทำสิ่งที่มันต้องการได้ยกเว้น:

  • เพิ่มหรือลบรายการแฮชเนื่องจากสิ่งเหล่านี้จะเปลี่ยนลำดับการวนซ้ำอย่างไม่คาดคิด (ใน C ++ - พูดสิ่งเหล่านี้จะทำให้ตัววนซ้ำใช้การไม่ได้)
  • วนซ้ำตารางแฮชเดียวกันโดยไม่ทำการคัดลอกเนื่องจากจะใช้สถานะการวนซ้ำเดียวกัน อุ่ย

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

ใช่แน่นอนรูปแบบตัววนซ้ำเกี่ยวข้องกับรูปแบบอื่น ๆ ตัวอย่างเช่น iterator สร้างอินสแตนซ์ได้อย่างไร ใน Java เรามี generic Iterable<T>และIterator<T>interface iterable ที่เป็นรูปธรรมเช่นArrayList<T>สร้าง iterator ชนิดใดชนิดหนึ่งในขณะที่ชนิดของ iterator HashSet<T>อาจแตกต่างกันโดยสิ้นเชิง นั่นทำให้ฉันนึกถึงรูปแบบโรงงานที่Iterable<T>เป็นนามธรรมซึ่งเป็นโรงงานที่เป็นนามธรรมและIteratorเป็นผลิตภัณฑ์

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

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

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


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