เหตุใดโมเดลโดเมน anemic จึงถือว่าไม่ดีใน C # / OOP แต่สำคัญมากใน F # / FP


46

ในโพสต์บล็อกใน F # เพื่อความสนุกสนานและผลกำไรกล่าวว่า:

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

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

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

ระบุว่าใน C # เราดูเหมือนจะยืมต่อจาก F # และพยายามเขียนโค้ดสไตล์การทำงานมากขึ้น ทำไมเราไม่ยืมความคิดในการแยกข้อมูล / พฤติกรรมและคิดว่ามันไม่ดี? มันเป็นเพียงแค่ว่าคำจำกัดความไม่ได้มาพร้อมกับ OOP หรือมีเหตุผลที่เป็นรูปธรรมว่ามันไม่ดีใน C # ที่ด้วยเหตุผลบางอย่างไม่ได้ใช้ใน F # (และในความเป็นจริงจะกลับรายการ)?

(หมายเหตุ: ฉันสนใจเป็นพิเศษในความแตกต่างใน C # / F # ที่สามารถเปลี่ยนความคิดเห็นของสิ่งที่ดี / ไม่ดีแทนที่จะเป็นบุคคลที่อาจไม่เห็นด้วยกับความเห็นทั้งในโพสต์บล็อก)


1
สวัสดีแดน! ความแข็งแกร่งของคุณคือแรงบันดาลใจ นอกจากแพลตฟอร์ม. NET (และ Haskell) ฉันขอแนะนำให้คุณดูที่สกาล่า กอช Debashish ได้เขียนคู่ของบล็อกเกี่ยวกับการสร้างแบบจำลองโดเมนด้วยเครื่องมือการทำงานมันเป็นที่ชาญฉลาดสำหรับฉันหวังว่าสำหรับคุณเกินไปนี่คุณจะไป: debasishg.blogspot.com/2012/01/...
AndreasScheinert

2
ฉันถูกส่งไปโพสต์บล็อกที่น่าสนใจโดยเพื่อนร่วมงานวันนี้: blog.inf.ed.ac.uk/sapm/2014/02/04/… ดูเหมือนว่าผู้คนเริ่มที่จะท้าทายแนวคิดที่ว่าโมเดลโดเมนโลหิตจางไม่ดีอย่างสิ้นเชิง ซึ่งฉันคิดว่าอาจเป็นสิ่งที่ดี!
Danny Tuppeny

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

4
@Doval ให้ความเป็นธรรมเหมือนกับบอกว่าคุณไม่สามารถเปิดเผยตัวเขียนระบบไฟล์ได้เพราะมีบางคนอาจเติมดิสก์ของคุณ ใครบางคนที่สร้างทรีของโหนดแดงเท่านั้นจะไม่สร้างความเสียหายให้กับทรีสีแดง - ดำที่พวกมันถูกโคลนนิ่งหรือรหัสใด ๆ ในระบบซึ่งเกิดขึ้นกับการใช้อินสแตนซ์ที่เกิดขึ้นอย่างดี ถ้าคุณเขียนโค้ดที่แข็งขันสร้างอินสแตนซ์ใหม่ของขยะหรือสิ่งที่ไม่เป็นอันตรายไม่เปลี่ยนรูปจะไม่ช่วยให้คุณประหยัด, แต่มันจะช่วยให้คนอื่น ๆ จากคุณ การนำการติดตั้งไปใช้จะไม่ทำให้ผู้คนไม่สามารถเขียนโค้ดไร้สาระที่สิ้นสุดการหารด้วยศูนย์
Jimmy Hoffa

2
@ JimmyHoffa ฉันเห็นด้วย แต่นั่นไม่เกี่ยวข้องกับสิ่งที่ฉันวิจารณ์ ผู้เขียนอ้างว่าปกติแล้วข้อมูลจะถูกเปิดเผยเพราะมันไม่เปลี่ยนรูปและฉันบอกว่าการเปลี่ยนแปลงไม่ได้นั้นไม่จำเป็นต้องซ่อนรายละเอียดการใช้งานอย่างน่าอัศจรรย์
Doval

คำตอบ:


37

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

ใน C # OOP มีแนวคิดของการมอบหมายความรับผิดชอบที่คุณมอบหมายการจัดการของวัตถุให้กับมันดังนั้นคุณจึงต้องการให้มันเปลี่ยน internals ของตัวเอง

ใน FP คุณไม่ต้องการเปลี่ยนค่าในวัตถุดังนั้นการมีฟังก์ชั่นของคุณฝังอยู่ในวัตถุของคุณไม่สมเหตุสมผล

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

ปัญหาที่ใหญ่ที่สุดที่ฉันเคยเห็นในโมเดลโดเมน anemic ใน C # OOP คือคุณท้ายด้วยรหัสที่ซ้ำกันเพราะคุณมี DTO x และฟังก์ชันที่แตกต่างกัน 4 หน้าที่ที่กระทำกิจกรรม f ถึง DTO x เพราะ 4 คนไม่เห็นการใช้งานอื่น . เมื่อคุณวางเมธอดลงบน DTO x โดยตรงดังนั้นทั้ง 4 คนจะเห็นการใช้ f และนำกลับมาใช้ใหม่

โมเดลข้อมูล Anemic ใน C # OOP นำโค้ดกลับมาใช้ใหม่ แต่นี่ไม่ใช่กรณีใน FP เพราะฟังก์ชั่นเดียวทั่วไปในประเภทต่างๆมากมายที่คุณได้รับการนำโค้ดกลับมาใช้มากขึ้นเนื่องจากฟังก์ชั่นนั้นใช้งานได้ในหลายสถานการณ์ จะเขียนสำหรับ DTO เดียวใน C #


ตามที่ระบุไว้ในความคิดเห็นการอนุมานประเภทเป็นหนึ่งในประโยชน์ที่ได้รับจากการพึ่งพาเพื่อให้เกิดความแตกต่างอย่างมีนัยสำคัญและโดยเฉพาะคุณสามารถติดตามกลับไปที่ระบบพิมพ์Hindley Milnerด้วยการอนุมานอัลกอริธึม W การอนุมานประเภทดังกล่าวในระบบประเภท C # OOP ถูกหลีกเลี่ยงเนื่องจากเวลารวบรวมเมื่อการอนุมานอิงตามข้อ จำกัด นั้นยาวมากเนื่องจากจำเป็นต้องค้นหาอย่างละเอียดรายละเอียดที่นี่: https://stackoverflow.com/questions/3968834/generics-why -cant ที่คอมไพเลอร์-สรุป-the-ชนิดข้อโต้แย้งในนี้กรณี


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

7
@DannyTuppeny สุจริต F # เป็นตัวอย่างที่ไม่ดีสำหรับการเปรียบเทียบมันเป็นเพียงแต่งตัว C # เล็กน้อย มันเป็นภาษาบังคับที่ไม่แน่นอนเหมือน C # แต่ก็มีสิ่งอำนวยความสะดวก FP บางอย่างที่ C # ไม่มี แต่ไม่มาก มองไปที่ Haskell เพื่อดูว่า FP โดดเด่นจริงๆและสิ่งนี้กลายเป็นไปได้มากขึ้นเนื่องจากคลาสประเภทรวมทั้ง ADT ทั่วไป
Jimmy Hoffa

@ MattFenwick ฉันหมายถึง C # อย่างชัดเจนเพราะนั่นคือสิ่งที่ผู้โพสต์ถาม ที่ฉันอ้างถึง OOP ตลอดคำตอบของฉันที่นี่ฉันหมายถึง C # OOP ฉันจะแก้ไขเพื่อชี้แจง
จิมมี่ฮอฟฟา

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

4
@ KeithS จากการใช้งานมากเกินไปตามมูลค่าใน Haskell ฉันคิดว่าคุณหมายถึงการจับคู่รูปแบบ ความสามารถของ Haskell ในการมีฟังก์ชั่นระดับสูงหลายอันที่มีชื่อเดียวกันพร้อมกับรูปแบบที่แตกต่างกันทันทีถึง 1 ระดับฟังก์ชั่น + รูปแบบการจับคู่
jozefg

6

เหตุใดโมเดลโดเมน anemic จึงถือว่าไม่ดีใน C # / OOP แต่สำคัญมากใน F # / FP

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

ที่จริงแล้วฉันไม่สามารถบอกคุณได้ว่าแบบจำลองโดเมนแสดงใน FP อย่างไร คำตอบอื่น ๆ ที่นี่พูดถึงโดยเฉพาะอย่างยิ่งเกี่ยวกับวิธีการทำใน Haskell และไม่สามารถใช้ได้กับ Lisp (แม่ของภาษา FP ทั้งหมด), ตระกูล ML ของภาษาหรือภาษาอื่น ๆ

ทำไมเราไม่ยืมความคิดในการแยกข้อมูล / พฤติกรรมและคิดว่ามันไม่ดี?

ยาสามัญอาจพิจารณาวิธีการแยกข้อมูลและพฤติกรรม Generics มาจากตระกูล ML ของภาษาโปรแกรมการทำงานไม่ใช่ส่วนหนึ่งของ OOP C # มีข้อมูลทั่วไปแน่นอน ดังนั้นเราอาจโต้แย้งว่า C # ยืมความคิดในการแยกข้อมูลและพฤติกรรมอย่างช้าๆ

มันเป็นเพียงแค่ว่าคำจำกัดความไม่เหมาะสมกับ OOP

ฉันเชื่อว่า OOP ขึ้นอยู่กับสถานที่ตั้งที่แตกต่างกันโดยพื้นฐานและดังนั้นจึงไม่ได้ให้เครื่องมือที่คุณต้องการในการแยกข้อมูลและพฤติกรรม เพื่อวัตถุประสงค์ในทางปฏิบัติทั้งหมดคุณต้องการผลิตภัณฑ์และประเภทข้อมูลและส่งไปพวกเขา ใน ML หมายถึงยูเนี่ยนและประเภทบันทึกและการจับคู่รูปแบบ

ตรวจสอบตัวอย่างผมให้ที่นี่

หรือมีเหตุผลอย่างชัดเจนที่ทำให้ C # นั้นไม่ดีด้วยเหตุผลบางอย่างที่ใช้ไม่ได้ใน F # (และที่จริงแล้วกลับกัน)

ระวังการกระโดดจาก OOP ไปยัง C # C # ไม่มีความใกล้เคียงกับเจ้าระเบียบเกี่ยวกับ OOP เหมือนกับภาษาอื่น ๆ .NET Framework ตอนนี้เต็มไปด้วย generics วิธีการคงที่และแม้กระทั่ง lambdas

(หมายเหตุ: ฉันสนใจเป็นพิเศษในความแตกต่างใน C # / F # ที่สามารถเปลี่ยนความคิดเห็นของสิ่งที่ดี / ไม่ดีแทนที่จะเป็นบุคคลที่อาจไม่เห็นด้วยกับความเห็นทั้งในโพสต์บล็อก)

การขาดประเภทสหภาพและการจับคู่รูปแบบใน C # ทำให้แทบเป็นไปไม่ได้ที่จะทำ เมื่อทุกสิ่งที่คุณมีคือค้อนทุกอย่างดูเหมือนเล็บ ...


2
... เมื่อทุกสิ่งที่คุณมีคือค้อน OOP โฟล์คจะสร้างโรงงานค้อน P +1 สำหรับการปักหมุดของสิ่งที่ C # หายไปเพื่อให้ข้อมูลและพฤติกรรมถูกแยกออกจากแต่ละคนอย่างเต็มที่: ประเภทของสหภาพและการจับคู่รูปแบบ
Jimmy Hoffa

-4

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


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