คำว่า "สิ่งที่เป็นนามธรรมที่รั่วไหล" หมายความว่าอย่างไร (โปรดอธิบายด้วยตัวอย่างฉันมักจะมีปัญหาในการฟังทฤษฎีเพียงอย่างเดียว)
คำว่า "สิ่งที่เป็นนามธรรมที่รั่วไหล" หมายความว่าอย่างไร (โปรดอธิบายด้วยตัวอย่างฉันมักจะมีปัญหาในการฟังทฤษฎีเพียงอย่างเดียว)
คำตอบ:
นี่คือตัวอย่างMeatspace :
รถยนต์มีนามธรรมสำหรับผู้ขับขี่ พวงมาลัยคันเร่งและเบรกในรูปแบบที่บริสุทธิ์ที่สุด สิ่งที่เป็นนามธรรมนี้ซ่อนรายละเอียดมากมายเกี่ยวกับสิ่งที่อยู่ใต้ฝากระโปรง: เครื่องยนต์, กล้อง, สายพานราวลิ้น, หัวเทียน, หม้อน้ำและอื่น ๆ
สิ่งที่เรียบร้อยเกี่ยวกับสิ่งที่เป็นนามธรรมนี้คือเราสามารถแทนที่บางส่วนของการใช้งานด้วยชิ้นส่วนที่ได้รับการปรับปรุงโดยไม่ต้องฝึกอบรมผู้ใช้ใหม่ สมมติว่าเราเปลี่ยนฝาผู้จัดจำหน่ายด้วยระบบจุดระเบิดแบบอิเล็กทรอนิกส์และเราเปลี่ยนลูกเบี้ยวคงที่ด้วยลูกเบี้ยวแปรผัน การเปลี่ยนแปลงเหล่านี้ช่วยเพิ่มประสิทธิภาพ แต่ผู้ใช้ยังคงบังคับล้อและใช้แป้นเหยียบเพื่อสตาร์ทและหยุด
เป็นเรื่องที่น่าทึ่งจริงๆ ... เด็กอายุ 16 ปีหรือ 80 ปีสามารถใช้เครื่องจักรที่ซับซ้อนนี้ได้โดยไม่รู้จริงๆว่ามันทำงานอย่างไรภายใน!
แต่มีการรั่วไหล การส่งกำลังมีการรั่วไหลเล็กน้อย ในระบบเกียร์อัตโนมัติคุณจะรู้สึกได้ว่ารถสูญเสียกำลังไปชั่วขณะขณะที่เปลี่ยนเกียร์ในขณะที่ใน CVT คุณจะรู้สึกได้ถึงแรงบิดที่นุ่มนวลตลอดทาง
มีรอยรั่วที่ใหญ่กว่าด้วย หากคุณหมุนรอบเครื่องยนต์เร็วเกินไปคุณอาจสร้างความเสียหายได้ หากบล็อกเครื่องยนต์เย็นเกินไปรถอาจสตาร์ทไม่ติดหรืออาจมีประสิทธิภาพต่ำ และถ้าคุณหมุนวิทยุไฟหน้าและ AC ทั้งหมดในเวลาเดียวกันคุณจะเห็นระยะการใช้ก๊าซของคุณลดลง
หมายความว่าสิ่งที่เป็นนามธรรมของคุณเปิดเผยรายละเอียดการใช้งานบางอย่างหรือคุณจำเป็นต้องทราบรายละเอียดการนำไปใช้งานเมื่อใช้สิ่งที่เป็นนามธรรม คำนี้มาจากJoel Spolskyประมาณปี 2002 ดูบทความวิกิพีเดียสำหรับข้อมูลเพิ่มเติม
ตัวอย่างคลาสสิกคือไลบรารีเครือข่ายที่อนุญาตให้คุณจัดการไฟล์ระยะไกลเป็นภายใน นักพัฒนาที่ใช้นามธรรมนี้ต้องทราบว่าปัญหาเครือข่ายอาจทำให้เกิดความล้มเหลวในลักษณะที่ไฟล์ในเครื่องไม่ทำ จากนั้นคุณต้องพัฒนาโค้ดเพื่อจัดการข้อผิดพลาดเฉพาะนอกนามธรรมที่ไลบรารีเครือข่ายมีให้
Wikipedia มีคำจำกัดความที่ดีสำหรับเรื่องนี้
สิ่งที่เป็นนามธรรมที่รั่วไหลหมายถึงสิ่งที่เป็นนามธรรมที่นำมาใช้โดยมีจุดประสงค์เพื่อลด (หรือซ่อน) ความซับซ้อนโดยที่รายละเอียดพื้นฐานจะไม่ถูกซ่อนไว้อย่างสมบูรณ์
หรือกล่าวอีกนัยหนึ่งสำหรับซอฟต์แวร์ก็คือเมื่อคุณสามารถสังเกตรายละเอียดการใช้งานคุณสมบัติผ่านข้อ จำกัด หรือผลข้างเคียงในโปรแกรม
ตัวอย่างสั้น ๆ คือการปิด C # / VB.Net และไม่สามารถจับพารามิเตอร์อ้างอิง / ออกได้ สาเหตุที่ไม่สามารถจับภาพได้เนื่องมาจากรายละเอียดการใช้งานว่ากระบวนการยกเกิดขึ้นได้อย่างไร นี่ไม่ได้หมายความว่ามีวิธีที่ดีกว่าในการทำเช่นนี้
นี่คือตัวอย่างที่คุ้นเคยสำหรับนักพัฒนา. NET: Page
คลาสของ ASP.NET พยายามซ่อนรายละเอียดของการดำเนินการ HTTP โดยเฉพาะการจัดการข้อมูลแบบฟอร์มเพื่อให้นักพัฒนาไม่ต้องจัดการกับค่าที่โพสต์ (เนื่องจากแมปค่าฟอร์มไปยังเซิร์ฟเวอร์โดยอัตโนมัติ การควบคุม)
แต่ถ้าคุณหลงเกินสถานการณ์การใช้งานพื้นฐานที่สุดPage
สิ่งที่เป็นนามธรรมจะเริ่มรั่วไหลและยากที่จะทำงานกับเพจต่างๆเว้นแต่คุณจะเข้าใจรายละเอียดการใช้งานของคลาส
ตัวอย่างทั่วไปอย่างหนึ่งคือการเพิ่มตัวควบคุมลงในเพจแบบไดนามิก - ค่าของตัวควบคุมที่เพิ่มแบบไดนามิกจะไม่ถูกแมปให้คุณเว้นแต่คุณจะเพิ่มในเวลาที่เหมาะสม : ก่อนที่เอ็นจิ้นพื้นฐานจะแมปค่าฟอร์มขาเข้ากับตัวควบคุมที่เหมาะสม เมื่อคุณต้องเรียนรู้ที่เป็นนามธรรมได้รั่วไหลออกมา
ในทางหนึ่งมันเป็นเรื่องทางทฤษฎีล้วนๆแม้ว่าจะไม่สำคัญก็ตาม
เราใช้นามธรรมเพื่อทำให้เข้าใจสิ่งต่างๆได้ง่ายขึ้น ฉันอาจดำเนินการกับคลาสสตริงในบางภาษาเพื่อซ่อนความจริงที่ว่าฉันกำลังจัดการกับชุดอักขระตามลำดับซึ่งเป็นรายการแต่ละรายการ ฉันจัดการกับชุดอักขระตามลำดับเพื่อซ่อนความจริงที่ว่าฉันกำลังจัดการกับตัวเลข ฉันจัดการกับตัวเลขเพื่อซ่อนความจริงที่ว่าฉันกำลังจัดการกับ 1s และ 0s
นามธรรมที่รั่วไหลคือสิ่งที่ไม่ซ่อนรายละเอียดที่ตั้งใจจะซ่อน ถ้า call string.Length บนสตริง 5 อักขระใน Java หรือ. NET ฉันจะได้รับคำตอบใด ๆ จาก 5 ถึง 10 เนื่องจากรายละเอียดการใช้งานโดยที่ภาษาเหล่านั้นเรียกอักขระนั้นเป็นจุดข้อมูล UTF-16 ซึ่งสามารถแสดงถึง 1 หรือ .5 ของตัวละคร สิ่งที่เป็นนามธรรมได้รั่วไหล การไม่รั่วไหลหมายความว่าการหาความยาวอาจต้องใช้พื้นที่จัดเก็บมากขึ้น (เพื่อเก็บความยาวจริง) หรือเปลี่ยนจาก O (1) เป็น O (n) (เพื่อหาว่าความยาวจริงคืออะไร) ถ้าฉันสนใจเกี่ยวกับคำตอบที่แท้จริง (คุณมักจะไม่เข้าใจจริงๆ) คุณต้องพยายามหาความรู้เกี่ยวกับสิ่งที่เกิดขึ้นจริงๆ
กรณีที่เป็นที่ถกเถียงกันมากขึ้นเกิดขึ้นกับกรณีต่างๆเช่นที่วิธีการหรือคุณสมบัติช่วยให้คุณสามารถเข้าไปทำงานภายในได้ไม่ว่าจะเป็นการรั่วไหลของนามธรรมหรือวิธีที่กำหนดไว้อย่างดีในการก้าวไปสู่ระดับนามธรรมที่ต่ำกว่าบางครั้งอาจเป็นเรื่องที่ผู้คนไม่เห็นด้วย
ฉันจะดำเนินการต่อในการให้ตัวอย่างโดยใช้ RPC
ในโลกอุดมคติของ RPC การเรียกโพรซีเดอร์ระยะไกลควรมีลักษณะเหมือนการเรียกโพรซีเดอร์เฉพาะที่ (หรือเพื่อให้เรื่องราวดำเนินไป) โปรแกรมเมอร์ควรมีความโปร่งใสอย่างสมบูรณ์เช่นเมื่อพวกเขาเรียกSomeObject.someFunction()
พวกเขาไม่รู้ว่าSomeObject
(หรือเฉพาะsomeFunction
สำหรับเรื่องนั้น) ถูกจัดเก็บและดำเนินการในเครื่องหรือจัดเก็บและดำเนินการจากระยะไกล ทฤษฎีนี้ทำให้การเขียนโปรแกรมง่ายขึ้น
ความจริงนั้นแตกต่างกันเนื่องจากมีความแตกต่างอย่างมากระหว่างการเรียกใช้ฟังก์ชันท้องถิ่น (แม้ว่าคุณจะใช้ภาษาที่ตีความช้าที่สุดในโลก) และ:
ในช่วงเวลาเดียวที่มีความแตกต่างของขนาดประมาณสามคำสั่ง (หรือมากกว่า!) คำสั่งขนาดสาม + เหล่านี้จะสร้างความแตกต่างอย่างมากในประสิทธิภาพที่จะทำให้นามธรรมของการโทรโพรซีเดอร์ของคุณรั่วไหลค่อนข้างชัดเจนในครั้งแรกที่คุณปฏิบัติต่อ RPC อย่างผิดพลาดเป็นการเรียกใช้ฟังก์ชันจริง นอกจากนี้การเรียกใช้ฟังก์ชันจริงการยกเว้นปัญหาร้ายแรงในโค้ดของคุณจะมีจุดล้มเหลวน้อยมากนอกข้อบกพร่องในการใช้งาน การโทร RPC มีปัญหาที่อาจเกิดขึ้นได้ทั้งหมดดังต่อไปนี้ซึ่งจะถูกแบ่งออกเป็นกรณีความล้มเหลวมากกว่าสิ่งที่คุณคาดหวังจากการโทรในพื้นที่ปกติ:
ตอนนี้การเรียก RPC ของคุณซึ่งเป็น "เช่นเดียวกับการเรียกใช้ฟังก์ชันภายใน" จึงมีเงื่อนไขความล้มเหลวเพิ่มเติมที่คุณไม่ต้องโต้แย้งเมื่อทำการเรียกฟังก์ชันภายในเครื่อง สิ่งที่เป็นนามธรรมรั่วไหลออกมาอีกครั้งและยากยิ่งขึ้น
ในท้ายที่สุด RPC เป็นนามธรรมที่ไม่ดีเพราะมันรั่วไหลเหมือนตะแกรงทุกระดับ - เมื่อประสบความสำเร็จและเมื่อล้มเหลวทั้งสองอย่าง
ตัวอย่างในdjango ORM หลายต่อหลายตัวอย่าง :
โปรดสังเกตในการใช้ API ตัวอย่างที่คุณต้องบันทึก () อ็อบเจ็กต์บทความพื้นฐาน a1 ก่อนที่คุณจะสามารถเพิ่มอ็อบเจ็กต์ Publication ในแอ็ตทริบิวต์แบบกลุ่มต่อกลุ่ม และสังเกตว่าการอัปเดตแอตทริบิวต์แบบกลุ่มต่อกลุ่มจะบันทึกไปยังฐานข้อมูลที่สำคัญทันทีในขณะที่การอัปเดตแอตทริบิวต์เอกพจน์จะไม่ปรากฏในฐานข้อมูลจนกว่าจะมีการเรียก. save ()
สิ่งที่เป็นนามธรรมคือเรากำลังทำงานกับกราฟออบเจ็กต์โดยที่แอตทริบิวต์ค่าเดียวและแอตทริบิวต์หลายค่าเป็นเพียงแอตทริบิวต์ แต่การใช้งานในฐานะฐานข้อมูลเชิงสัมพันธ์ได้สำรองข้อมูลที่เก็บข้อมูลรั่วไหล ... เนื่องจากระบบความสมบูรณ์ของ RDBS ปรากฏผ่านแผ่นไม้อัดบาง ๆ ของอินเทอร์เฟซอ็อบเจ็กต์
Abstraction เป็นวิธีหนึ่งในการทำให้โลกเรียบง่ายขึ้น หมายความว่าคุณไม่ต้องกังวลว่าจะเกิดอะไรขึ้นภายใต้ฝากระโปรงหน้าหรือหลังม่าน หมายความว่ามีบางอย่างที่พิสูจน์ได้อย่างงี่เง่า
เครื่องบินเป็นเครื่องจักรที่ซับซ้อนมาก คุณมีเครื่องยนต์เจ็ทระบบออกซิเจนระบบไฟฟ้าระบบเกียร์ลงจอด ฯลฯ แต่นักบินไม่ต้องกังวลกับความซับซ้อนของเครื่องยนต์เจ็ท.. ทั้งหมดนั้น "ถูกมองข้ามไป" ซึ่งหมายความว่านักบินจำเป็นต้องกังวลเกี่ยวกับการบังคับเครื่องบินเท่านั้น: ไปทางซ้ายเพื่อไปทางซ้ายและไปทางขวาเพื่อไปทางขวาดึงขึ้นเพื่อเพิ่มระดับความสูงและกดลงเพื่อลง
ง่ายพอ ...... ที่จริงฉันโกหก: การควบคุมพวงมาลัยนั้นซับซ้อนกว่าเล็กน้อย ในโลกแห่งอุดมคตินั่นเป็นสิ่งเดียวที่นักบินควรกังวล แต่นี่ไม่ใช่กรณีในชีวิตจริง: หากคุณบินเครื่องบินเหมือนลิงโดยไม่เข้าใจว่าเครื่องบินทำงานอย่างไรหรือรายละเอียดการใช้งานใด ๆ คุณอาจจะชนและฆ่าทุกคนบนเรือ
ในความเป็นจริงนักบินต้องกังวลเกี่ยวกับสิ่งที่สำคัญมากมายไม่ใช่ว่าทุกอย่างจะถูกมองข้ามไปนักบินต้องกังวลเกี่ยวกับความเร็วลมแรงผลักมุมการโจมตีเชื้อเพลิงระดับความสูงปัญหาสภาพอากาศมุมของการตกลงมาและไม่ว่า นักบินกำลังไปในทิศทางที่ถูกต้อง คอมพิวเตอร์สามารถช่วยนักบินในงานเหล่านี้ได้ แต่ไม่ใช่ทุกอย่างที่เป็นระบบอัตโนมัติ / ง่ายขึ้น
เช่นถ้านักบินดึงเสาแรงเกินไปเครื่องบินจะเชื่อฟัง แต่นักบินก็เสี่ยงที่จะถ่วงเครื่องบินและเมื่อจนตรอกแล้วก็ยากที่จะควบคุมมันกลับคืนมาได้ก่อนที่มันจะกระแทกกลับลงมาที่พื้น .
กล่าวอีกนัยหนึ่งก็ไม่เพียงพอสำหรับนักบินที่จะควบคุมพวงมาลัยโดยไม่รู้เรื่องอื่น ......... nooooo ....... เขาต้องรู้เกี่ยวกับความเสี่ยงและข้อ จำกัด พื้นฐานของเครื่องบิน ก่อนที่เขาจะบิน ....... เขาต้องรู้ว่าเครื่องบินทำงานอย่างไรและเครื่องบินบินได้อย่างไร เขาต้องรู้รายละเอียดการนำไปใช้งาน ..... เขาต้องรู้ว่าการดึงแรงเกินไปจะนำไปสู่คอกหรือการลงจอดที่สูงชันเกินไปจะทำลายเครื่องบิน
สิ่งเหล่านั้นไม่ได้เป็นนามธรรมไป หลายสิ่งหลายอย่างเป็นนามธรรมไป แต่ไม่ใช่ทุกสิ่ง นักบินต้องการเพียงแค่กังวลเกี่ยวกับคอพวงมาลัยและอาจมีสิ่งอื่นหนึ่งหรือสองอย่าง สิ่งที่เป็นนามธรรมคือ "รั่ว"
...... รหัสของคุณก็เหมือนกัน หากคุณไม่ทราบรายละเอียดการใช้งานที่สำคัญบ่อยกว่านั้นคุณจะต้องเข้ามุม
นี่คือตัวอย่างในการเข้ารหัส:
ORM เป็นนามธรรมของความยุ่งยากในการจัดการกับแบบสอบถามฐานข้อมูล แต่ถ้าคุณเคยทำสิ่งที่ชอบ:
User.all.each do |user|
puts user.name # let's print each user's name
end
แล้วคุณจะรู้ว่านี่เป็นวิธีที่ดีในการฆ่าแอปของคุณหากคุณมีผู้ใช้มากกว่าสองล้านคน ไม่ใช่ทุกสิ่งที่เป็นนามธรรมไป คุณต้องรู้ว่าการโทรUser.all
กับผู้ใช้ 25 ล้านคนจะขัดขวางการใช้งานหน่วยความจำของคุณและจะทำให้เกิดปัญหา คุณจำเป็นต้องทราบรายละเอียดพื้นฐานบางอย่าง สิ่งที่เป็นนามธรรมรั่วไหล
ความจริงที่ว่าในบางประเด็นซึ่งจะได้รับคำแนะนำจากมาตราส่วนและการดำเนินการของคุณคุณจะต้องทำความคุ้นเคยกับรายละเอียดการใช้งานกรอบนามธรรมของคุณเพื่อที่จะเข้าใจว่าเหตุใดจึงมีพฤติกรรมเช่นนั้น
ตัวอย่างเช่นพิจารณาSQL
คำค้นหานี้:
SELECT id, first_name, last_name, age, subject FROM student_details;
และทางเลือกอื่น:
SELECT * FROM student_details;
ตอนนี้ดูเหมือนว่าโซลูชันที่เทียบเท่ากันทางตรรกะ แต่ประสิทธิภาพของโซลูชันแรกนั้นดีกว่าเนื่องจากข้อกำหนดชื่อคอลัมน์แต่ละคอลัมน์
เป็นตัวอย่างที่ไม่สำคัญ แต่ในที่สุดมันก็กลับมาที่คำพูดของ Joel Spolsky:
นามธรรมที่ไม่สำคัญทั้งหมดในระดับหนึ่งรั่วไหล
เมื่อถึงจุดหนึ่งในการดำเนินการของคุณคุณจะต้องปรับวิธีการทำงานของ DB (SQL) ให้เหมาะสมที่สุด คุณจะต้องรู้วิธีการทำงานของฐานข้อมูลเชิงสัมพันธ์ มันเป็นนามธรรมสำหรับคุณในตอนแรก แต่มันรั่ว คุณต้องเรียนรู้ในบางจุด
สมมติว่าเรามีรหัสต่อไปนี้ในไลบรารี:
Object[] fetchDeviceColorAndModel(String serialNumberOfDevice)
{
//fetch Device Color and Device Model from DB.
//create new Object[] and set 0th field with color and 1st field with model value.
}
เมื่อผู้บริโภคเรียก API พวกเขาจะได้รับ Object [] ผู้บริโภคต้องเข้าใจว่าฟิลด์แรกของอาร์เรย์ออบเจ็กต์มีค่าสีและฟิลด์ที่สองคือค่าโมเดล ที่นี่สิ่งที่เป็นนามธรรมได้รั่วไหลจากห้องสมุดไปสู่รหัสผู้บริโภค
หนึ่งในวิธีแก้ไขคือส่งคืนวัตถุที่ห่อหุ้มโมเดลและสีของอุปกรณ์ ผู้บริโภคสามารถเรียกวัตถุนั้นเพื่อรับรุ่นและค่าสี
DeviceColorAndModel fetchDeviceColorAndModel(String serialNumberOfTheDevice)
{
//fetch Device Color and Device Model from DB.
return new DeviceColorAndModel(color, model);
}
สิ่งที่เป็นนามธรรมที่รั่วไหลเป็นข้อมูลเกี่ยวกับสภาวะห่อหุ้ม ตัวอย่างที่ง่ายมากของนามธรรมที่รั่วไหล:
$currentTime = new DateTime();
$bankAccount1->setLastRefresh($currentTime);
$bankAccount2->setLastRefresh($currentTime);
$currentTime->setTimestamp($aTimestamp);
class BankAccount {
// ...
public function setLastRefresh(DateTimeImmutable $lastRefresh)
{
$this->lastRefresh = $lastRefresh;
} }
และวิธีที่ถูกต้อง (ไม่ใช่นามธรรมที่รั่วไหล):
class BankAccount
{
// ...
public function setLastRefresh(DateTime $lastRefresh)
{
$this->lastRefresh = clone $lastRefresh;
}
}