“ ใช้แผนที่แทนคลาสเพื่อแสดงข้อมูล” -Rich Hickey


19

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

ตัวอย่าง :

PersonAPI {
    Person addPerson(Person obj);
    Map<String, Object> addPerson(Map<String, Object> personMap);
}

ในฟังก์ชั่นที่สองผู้ใช้ API จะรู้ได้อย่างไรว่าอินพุตใดที่จะสร้างบุคคล



ฉันต้องการทราบสิ่งนี้และฉันรู้สึกว่าคำถามตัวอย่างนั้นไม่ได้ตอบคำถามเลย
sydan

ฉันรู้ว่าฉันเคยเห็นการสนทนานี้มาก่อนใน SE ฉันเชื่อว่ามันอยู่ในบริบทของ JavaScript แต่ข้อโต้แย้งนั้นเหมือนกัน หาไม่เจอ
Sebastian Redl

2
เนื่องจาก Clojure เป็น Lisp คุณควรทำสิ่งที่เหมาะสมกับ Lisp เมื่อคุณใช้ Java, รหัสใน ... ดี Java
AK_

คำตอบ:


12

บทสรุปที่พูดเกินจริง(TM)

คุณได้รับบางสิ่ง

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

มีข้อเสียเปรียบอย่างหนึ่ง

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

สิ่งคือคุณจะได้รับวิปัสสนาโดยใช้, um, วิปัสสนา นี่คือสิ่งที่มักจะเกิดขึ้น:

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

กล่าวอีกนัยหนึ่งถ้าคุณไม่จำเป็นต้องเชื่อมต่อกับ FP คุณไม่จำเป็นต้องทำตามคำแนะนำของ Rich Hickey

สุดท้าย แต่ไม่ใช่อย่างน้อยที่สุด (หรือที่สวยที่สุด) ถึงแม้ว่าการใช้Stringเป็นคีย์คุณสมบัติจะทำให้คุณใช้งานStringได้อย่างตรงไปตรงมาที่สุด แต่คุณไม่จำเป็นต้องใช้s ระบบดั้งเดิมจำนวนมากรวมถึง Android ™ใช้ ID จำนวนเต็มผ่านกรอบทั้งหมดเพื่ออ้างถึงคลาสคุณสมบัติทรัพยากรและอื่น ๆ

Android เป็นเครื่องหมายการค้าของ Google Inc.


คุณสามารถทำให้ทั้งสองโลกมีความสุข

สำหรับโลก Java ให้ใช้ getters และ setters ตามปกติ

สำหรับโลก FP ให้ใช้

  • Object getPropertyByName(String name)
  • void setPropertyByName(String name, Object value) throws IllegalPropertyChangeException
  • List<String> getPropertyNames()
  • Class<?> getPropertyValueClass(String name)

ภายในฟังก์ชั่นเหล่านี้ใช่รหัสน่าเกลียด แต่มีปลั๊กอิน IDE ที่จะเติมให้คุณโดยใช้ ... เอ่อปลั๊กอินอัจฉริยะที่อ่านโค้ดของคุณ

ด้าน Java ของสิ่งต่าง ๆ จะเหมือนกับนักแสดงตามปกติ พวกเขาจะไม่ใช้ส่วนที่น่าเกลียดของรหัส คุณอาจต้องการซ่อนจาก Javadoc

ฝั่ง FP ของโลกสามารถเขียนโค้ด "leet" ที่พวกเขาต้องการและพวกเขามักจะไม่ตะโกนใส่คุณเกี่ยวกับรหัสที่ช้า


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

โดยเฉพาะอย่างยิ่งการซีเรียลไลซ์เซชั่น / การดีซีเรียลไลเซชันมักต้องใช้เทคนิคที่คล้ายกัน

แค่ความคิดทั่วไปเกี่ยวกับ "map as object"

  1. คุณยังต้องจัดเตรียมฟังก์ชันสำหรับการตรวจสอบ "map as object" ความแตกต่างคือ "map as object" อนุญาตให้ใช้เกณฑ์การตรวจสอบที่ยืดหยุ่นมากขึ้น (จำกัด น้อยกว่า)
  2. คุณสามารถเพิ่มเขตข้อมูลเพิ่มเติมลงใน "แผนที่เป็นวัตถุ" ได้อย่างง่ายดาย
  3. ในการให้ข้อมูลจำเพาะเกี่ยวกับข้อกำหนดขั้นต่ำของวัตถุที่ถูกต้องคุณจะต้อง:
    • แสดงรายการชุดคีย์ที่จำเป็นต้องมีน้อยที่สุดในแผนที่
    • สำหรับแต่ละคีย์ที่ต้องตรวจสอบความถูกต้องของค่าให้จัดเตรียมฟังก์ชันการตรวจสอบค่า
    • หากมีกฎการตรวจสอบที่ต้องตรวจสอบค่าคีย์หลายค่าให้ระบุเช่นกัน
    • ประโยชน์คืออะไร การให้ข้อมูลจำเพาะด้วยวิธีนี้เป็นเรื่องครุ่นคิด: คุณสามารถเขียนโปรแกรมเพื่อสอบถามชุดคีย์ที่ต้องการน้อยที่สุดและรับฟังก์ชั่นการตรวจสอบความถูกต้องสำหรับแต่ละคีย์
    • ใน OOP สิ่งเหล่านี้ทั้งหมดถูกรีดเป็นกล่องดำในชื่อ "encapsulation" แทนที่ตรรกะการตรวจสอบที่เครื่องอ่านได้ผู้โทรสามารถอ่าน "เอกสาร API" ที่มนุษย์สามารถอ่านได้เท่านั้น (ถ้าโชคดีมันมีอยู่)

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

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

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

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

@NathanDavis (1) ฉันยอมรับคำตอบของฉันถูกเขียนจากมุมมองการพิมพ์แบบคงที่ (C #) และฉันเขียนคำตอบนี้เพราะฉันแบ่งปันมุมมองเดียวกันของผู้ถาม ฉันยอมรับว่าฉันไม่มีจุดชมวิวแบบรวมศูนย์ (2) ยินดีต้อนรับสู่ SE.SE และเนื่องจากคุณเป็นบุคคลที่น่านับถือใน Clojure โปรดใช้เวลาเขียนคำตอบของคุณเองถ้าสิ่งที่มีอยู่ไม่น่าพอใจ downvotes ลบชื่อเสียงและคำตอบใหม่ดึงดูด upvotes ซึ่งเพิ่มชื่อเสียงได้อย่างรวดเร็ว (3) ฉันสามารถดูว่า "วัตถุที่ไม่สมบูรณ์" มีประโยชน์อย่างไร - คุณสามารถค้นหาคุณสมบัติ 2 รายการสำหรับวัตถุที่กำหนด (ชื่อรูปประจำตัว) และแยกส่วนที่เหลือออก

9

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

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

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

ต่อไปนี้เป็นการเปลี่ยนแปลงรหัสบางอย่าง (หนึ่งหรือสองซึ่งถูกกล่าวถึงในการพูดคุย) ที่อาจจะง่ายกว่าโดยใช้รายการแผนที่เปรียบเทียบกับการใช้รายการPersonวัตถุ:

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

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


มีชื่อนี้ไหม พูดว่าการทำแผนที่วัตถุทรัพย์สินหรือการทำแผนที่วัตถุแอตทริบิวต์ (ตามบรรทัดเดียวกับ ORM)?

4
Choosing a class to represent a Person provides the immediate benefit of creating a statically-verifiable API... but that comes with the cost of limiting opportunities or increasing costs for change and reuse later on.ผิดและไม่ตรงไปตรงมาอย่างไม่น่าเชื่อ มันช่วยเพิ่มโอกาสของคุณในการเปลี่ยนแปลงในภายหลังเพราะเมื่อคุณทำการเปลี่ยนแปลงที่แตกสลายคอมไพเลอร์จะค้นหาและชี้ให้คุณทุกสถานที่ที่จำเป็นต้องได้รับการปรับปรุงโดยอัตโนมัติเพื่อให้ codebase ทั้งหมดของคุณเร็วขึ้น มันอยู่ในโค้ดไดนามิกซึ่งคุณไม่สามารถทำได้อย่างที่คุณได้รับควบคู่ไปกับตัวเลือกก่อนหน้า!
Mason Wheeler

4
@MasonWheeler: สิ่งที่คุณพูดจริง ๆ ก็คือคุณให้ความสำคัญกับความปลอดภัยแบบคอมไพล์เวลาคอมไพล์มากกว่าโครงสร้างข้อมูลแบบไดนามิกที่มากขึ้น
Robert Harvey

1
ความแตกต่างไม่ได้เป็นแนวคิดที่ จำกัด OOP ในกรณีของแผนที่คุณอาจมีความหลากหลายที่แตกต่างกัน (ถ้าองค์ประกอบเป็นชนิดย่อยของแผนที่บางประเภทที่สามารถจัดการ) หรือ Ad-hoc polymorphism (ถ้าองค์ประกอบถูกติดแท็กสหภาพ) นี่คือ internals การดำเนินการที่สามารถดำเนินการบนแผนที่สามารถ polymorphic Parametric polymorphism เมื่อเราใช้ฟังก์ชันลำดับสูงกว่าในองค์ประกอบหรือ ad-hoc เมื่อทำการสั่งจ่าย การห่อหุ้มสามารถทำได้ด้วยเนมสเปซหรือรูปแบบอื่น ๆ ของการจัดการการมองเห็น โดยพื้นฐานแล้วการแยกวัตถุไม่เท่ากับการกำหนดการทำงานให้กับชนิดข้อมูล
siefca

1
@GillBates ทำไมคุณถึงพูดอย่างนั้น? คุณเพียงแค่สูญเสียโอกาสในการนำวิธีการเสมือนเหล่านั้น "ในแผนที่" - แต่นั่นคือสิ่งที่ Rich Hickey พูดถึง "ActiveObjects" เป็นรูปแบบการต่อต้านที่แท้จริง คุณควรปฏิบัติต่อข้อมูลในลักษณะที่เป็น (ข้อมูล) และไม่เชื่อมโยงกับพฤติกรรม มีประโยชน์อย่างมากที่จะทำได้โดยการแยกข้อกังวล
เฝอ

4
  • หากข้อมูลมีพฤติกรรมน้อยหรือไม่มีเลยด้วยเนื้อหาที่มีความยืดหยุ่นและมีแนวโน้มที่จะเปลี่ยนแปลงให้ใช้แผนที่ IMO ซึ่งเป็น "javabean" หรือ "Data Object" ทั่วไปที่ประกอบด้วยAnemic Domain Model ที่มีเขตข้อมูล N, ตัวตั้งค่า N และตัวรับ N ไม่มีการเสียเวลา อย่าพยายามสร้างความประทับใจให้ผู้อื่นด้วยโครงสร้างที่ได้รับเกียรติของคุณ ซื่อสัตย์ทำให้ความตั้งใจของคุณชัดเจนและใช้แผนที่ (หรือถ้าเหมาะสมกับโดเมนของคุณวัตถุ JSON หรือ XML)

  • ถ้าข้อมูลมีพฤติกรรมที่สำคัญจริง ๆ รู้จักวิธีการ ( บอกไม่ต้องถาม ) ให้ใช้คลาส และตบหลังตัวเองเพื่อใช้การเขียนโปรแกรม Object Oriented จริง ๆ :-)

  • หากข้อมูลมีพฤติกรรมการตรวจสอบที่จำเป็นจำนวนมากและเขตข้อมูลที่จำเป็นให้ใช้คลาส

  • หากข้อมูลมีพฤติกรรมการตรวจสอบความถูกต้องปานกลางแสดงว่าเป็นเส้นเขตแดน

  • หากข้อมูลเริ่มก่อให้เกิดการเปลี่ยนแปลงสถานที่ให้บริการนั่นเป็นเรื่องง่ายกว่าและน่าเบื่อกว่าในแผนที่ เพียงเขียนคลาสย่อยเล็กน้อย

  • ข้อเสียเปรียบหลักอย่างหนึ่งของการใช้แผนที่คือผู้ใช้ต้องส่งค่าไปยัง Strings, ints, Foos และอื่น ๆ หากสิ่งนี้เป็นที่น่ารำคาญอย่างมากและเกิดข้อผิดพลาดให้พิจารณาชั้นเรียน หรือพิจารณาคลาสตัวช่วยที่ล้อมแผนที่ด้วยตัวเชื่อมต่อที่เกี่ยวข้อง


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

0

API สำหรับ a mapมีสองระดับ

  1. API สำหรับแผนที่
  2. ข้อกำหนดของแอปพลิเคชัน

API สามารถอธิบายได้ในแผนที่ตามแบบแผน ตัวอย่างเช่นคู่:api api-validateสามารถวางไว้ในแผนที่หรือ:api-foo validate-fooอาจเป็นแบบแผน แผนที่สามารถจัดเก็บapi api-documentation-linkได้

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

ไม่มีอะไรน่าอัศจรรย์เกี่ยวกับแผนที่และไม่มีอะไรน่าอัศจรรย์เกี่ยวกับวัตถุ มันคือการจัดส่งทั้งหมด

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