ความแตกต่างระหว่างคลาสย่อยและประเภทย่อยคืออะไร?


44

คำตอบการจัดอันดับสูงสุดที่จะคำถามนี้เกี่ยวกับ Liskov ชดเชยหลักการใช้เวลาปวดจะแยกแยะระหว่างข้อกำหนดย่อยและsubclass นอกจากนี้ยังทำให้จุดที่บางภาษาทำให้ทั้งสองเป็นคู่ในขณะที่คนอื่นทำไม่ได้

สำหรับภาษาเชิงวัตถุที่ฉันคุ้นเคยมากที่สุด (Python, C ++), "type" และ "class" เป็นแนวคิดที่มีความหมายเหมือนกัน ในแง่ของ C ++ การแยกประเภทย่อยและคลาสย่อยหมายความว่าอย่างไร กล่าวว่าเช่นว่าFooเป็น subclass FooBaseแต่ไม่ได้เป็นชนิดย่อยของ หากfooเป็นตัวอย่างของFooสายนี้จะ:

FooBase* fbPoint = &foo;

ไม่ถูกต้องอีกต่อไป?


6
จริงๆแล้วใน Python "type" และ "class" เป็นแนวคิดที่แตกต่าง ในความเป็นจริง, Python ถูกพิมพ์แบบไดนามิก, "type" ไม่ได้เป็นแนวคิดที่ทุกคนในหลาม น่าเสียดายที่นักพัฒนา Python ไม่เข้าใจและยังคงทำให้ทั้งสอง
Jörg W Mittag

11
"Type" และ "class" นั้นแตกต่างกันใน C ++ เช่นกัน "Array of ints" เป็นประเภท; ชั้นไหน "ตัวชี้ไปยังตัวแปรประเภท int" เป็นชนิด; ชั้นไหน สิ่งเหล่านี้ไม่ใช่ชั้นเรียนใด ๆ แต่เป็นประเภทแน่นอน
Eric Lippert

2
ฉันสงสัยสิ่งนี้มากหลังจากอ่านคำถามนั้นและคำตอบนั้น
user369450

4
@JorgWMittag หากไม่มีแนวคิดเกี่ยวกับ "ประเภท" ใน python มีคนควรบอกผู้ที่เขียนเอกสาร: docs.python.org/3/library/stdtypes.html
Matt

@ แมทท์จะเป็นงานที่ยุติธรรมประเภททำใน 3.5 ซึ่งค่อนข้างเร็ว ๆ นี้โดยเฉพาะอย่างยิ่งโดยสิ่งที่ฉันได้รับอนุญาตให้ใช้ในการผลิตมาตรฐาน
Jared Smith

คำตอบ:


53

Subtypingเป็นรูปแบบของ polymorphism ชนิดหนึ่งซึ่ง subtype นั้นเป็นประเภทข้อมูลที่เกี่ยวข้องกับประเภทข้อมูลอื่น (supertype) โดยความคิดของความสามารถในการสับเปลี่ยนความหมายว่าองค์ประกอบของโปรแกรมโดยทั่วไปแล้วรูทีนย่อยหรือฟังก์ชัน ดำเนินการกับองค์ประกอบของประเภทย่อย

หากSเป็นประเภทย่อยของTความสัมพันธ์ของการพิมพ์ย่อยมักจะเขียนS <: Tเพื่อหมายความว่าคำใด ๆ ของประเภทSสามารถนำมาใช้อย่างปลอดภัยในบริบทที่Tคาดว่าคำประเภท ความหมายที่แม่นยำของการพิมพ์ย่อยอย่างมีนัยสำคัญขึ้นอยู่กับรายละเอียดของสิ่งที่ "ใช้อย่างปลอดภัยในบริบทที่" หมายถึงในภาษาการเขียนโปรแกรมที่กำหนด

คลาสย่อยไม่ควรสับสนกับการพิมพ์ย่อย โดยทั่วไปแล้วการพิมพ์ย่อยจะสร้างความสัมพันธ์แบบ is-a ในขณะที่การทำ subclass จะนำไปใช้ใหม่และสร้างความสัมพันธ์ทางวากยสัมพันธ์ไม่จำเป็นต้องเป็นความสัมพันธ์เชิงความหมาย

ที่จะแยกแยะแนวคิดเหล่านี้ subtyping ยังเป็นที่รู้จักกันเป็นมรดกของอินเตอร์เฟซในขณะที่ subclassing เป็นที่รู้จักกันเป็นมรดกของการดำเนินงานหรือมรดกรหัส

อ้างอิง
Subtyping
มรดก


1
พูดได้เป็นอย่างดี. อาจคุ้มค่าที่จะกล่าวถึงในบริบทของคำถามที่โปรแกรมเมอร์ C ++ มักใช้คลาสฐานเสมือนจริงเพื่อสื่อสารความสัมพันธ์ของการพิมพ์ย่อยกับระบบประเภท หลักสูตรการเขียนโปรแกรมทั่วไปมักเป็นที่ต้องการแน่นอน
Aluan Haddad

6
"ความหมายที่แม่นยำของการพิมพ์ย่อยอย่างมีนัยสำคัญขึ้นอยู่กับรายละเอียดของสิ่งที่" ใช้อย่างปลอดภัยในบริบทที่ "หมายถึงในภาษาการเขียนโปรแกรมที่กำหนด" …และ LSP กำหนดความคิดที่สมเหตุสมผลว่าคำว่า "ปลอดภัย" หมายถึงอะไรและบอกเราว่าข้อ จำกัด ใดที่รายการเหล่านั้นต้องพึงพอใจเพื่อเปิดใช้งาน "ความปลอดภัย" รูปแบบนี้โดยเฉพาะ
Jörg W Mittag

อีกตัวอย่างหนึ่งบนกอง: ถ้าฉันเข้าใจอย่างถูกต้องใน C ++ การpublicสืบทอดจะแนะนำชนิดย่อยในขณะที่การprivateสืบทอดจะแนะนำคลาสย่อย
เควนติน

@Quentin การสืบทอดสาธารณะเป็นทั้งประเภทย่อยและคลาสย่อย แต่ส่วนตัวยังเป็นเพียงคลาสย่อย แต่ไม่ใช่ประเภทย่อย คุณสามารถมี Subtyping โดยไม่ต้อง subclassing ที่มีโครงสร้างเช่นการเชื่อมต่อ Java
eques

27

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

ระดับเป็นมัดของวิธีการ มันเป็นชุดของการใช้งานลักษณะการทำงาน

การพิมพ์ย่อยเป็นวิธีการปรับแต่งโปรโตคอล การแบ่งประเภทย่อยเป็นวิธีการใช้รหัสที่แตกต่างกันคือการใช้รหัสซ้ำโดยอธิบายความแตกต่างในพฤติกรรมเท่านั้น

หากคุณใช้ Java หรือC♯คุณอาจได้รับคำแนะนำว่าทุกประเภทควรเป็นinterfaceประเภท หากคุณอ่านข้อมูลเกี่ยวกับการทำความเข้าใจเกี่ยวกับการลบข้อมูลของ William Cook คุณอาจรู้ว่าในการที่จะทำ OO ในภาษาเหล่านั้นคุณต้องใช้interfaces เป็นประเภทเท่านั้น (เช่นเดียวกับข้อเท็จจริงที่น่าสนุก: Java cribbed interfaces โดยตรงจากโปรโตคอลของ Objective-C ซึ่งจะถูกนำโดยตรงจาก Smalltalk)

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

เท่าที่ฉันรู้ไม่มีภาษาที่พิมพ์แบบคงที่หลักซึ่งแยกความแตกต่างอย่างเคร่งครัดระหว่างการสืบทอดรหัส (การสืบทอดการสืบทอด / การทำคลาสย่อย) และการสืบทอดสัญญา (การพิมพ์ย่อย) ใน Java และC♯การสืบทอดอินเตอร์เฟสเป็น subtyping ที่บริสุทธิ์ (หรืออย่างน้อยก็จนกว่าจะมีการแนะนำวิธีการเริ่มต้นใน Java 8 และC♯ 8 ที่น่าจะเป็นเช่นกัน) แต่การสืบทอดคลาสก็ยังพิมพ์ย่อยเช่นเดียวกับการสืบทอดการใช้งาน ฉันจำได้ว่าอ่านเกี่ยวกับภาษา LISP เชิงวัตถุที่เน้นการทดลองแบบคงที่ซึ่งแยกแยะระหว่างมิกซ์อิน (ซึ่งมีพฤติกรรม), structs (ซึ่งมีสถานะ) อย่างเคร่งครัดอินเตอร์เฟส (ซึ่งอธิบายพฤติกรรม) และคลาส (ซึ่งประกอบด้วยศูนย์อย่างน้อย structs กับหนึ่งหรือมากกว่าของ mixins และสอดคล้องกับหนึ่งหรือมากกว่าหนึ่งอินเตอร์เฟส) คลาสเท่านั้นที่สามารถสร้างอินสแตนซ์และสามารถใช้อินเทอร์เฟซเป็นชนิดเท่านั้น

ในภาษา OO ที่พิมพ์แบบไดนามิกเช่น Python, Ruby, ECMAScript หรือ Smalltalk เรามักนึกถึงประเภทของวัตถุเป็นชุดของโปรโตคอลที่สอดคล้องกับมัน สังเกตพหูพจน์: วัตถุสามารถมีได้หลายประเภทและฉันไม่ได้พูดถึงความจริงที่ว่าวัตถุแต่ละประเภทStringนั้นยังเป็นวัตถุประเภทObjectด้วย (BTW: โปรดทราบว่าฉันใช้ชื่อคลาสเพื่อพูดคุยเกี่ยวกับประเภทได้อย่างไรฉันโง่แค่ไหน!) ออบเจ็กต์สามารถใช้หลายโปรโตคอลได้ ตัวอย่างเช่นใน Ruby Arraysสามารถผนวกเข้ากับพวกเขาสามารถจัดทำดัชนีพวกเขาสามารถทำซ้ำมากกว่าและพวกเขาสามารถเปรียบเทียบ นั่นเป็นสี่โปรโตคอลที่แตกต่างกันที่พวกเขาใช้!

ตอนนี้ Ruby ไม่มีประเภท แต่ชุมชน Ruby มีประเภท! พวกเขามีอยู่ในหัวของโปรแกรมเมอร์เท่านั้น และในเอกสารประกอบ ตัวอย่างเช่นวัตถุใด ๆ ที่ตอบสนองต่อวิธีการที่เรียกว่าeachโดยการให้องค์ประกอบของมันทีละคนถือเป็นวัตถุที่นับได้ และมี mixin ที่เรียกว่าEnumerableซึ่งขึ้นอยู่กับโปรโตคอลนี้ ดังนั้นถ้าวัตถุของคุณมีความถูกต้องชนิด (ซึ่งมีอยู่เฉพาะในหัวของโปรแกรม) แล้วมันจะได้รับอนุญาตให้ผสมใน (มรดก) จากEnumerablemixin และมันได้ดีได้รับทุกประเภทของวิธีการเย็นสำหรับฟรีเช่นmap, reduce, filterและอื่น ๆ บน.

ในทำนองเดียวกันหากมีการตอบสนองวัตถุ<=>แล้วก็ถือว่าใช้เทียบเคียงโปรโตคอลและมันสามารถผสมในComparablemixin และได้รับสิ่งที่ชอบ<, <=, >, <=, ==, between?และclampฟรี แต่ก็ยังสามารถดำเนินการทั้งหมดของวิธีการเหล่านั้นเองและไม่ได้รับมรดกจากComparableที่ทั้งหมดและมันจะยังคงได้รับการพิจารณาเทียบเคียง

ตัวอย่างที่ดีคือStringIOไลบรารีซึ่งปลอมแปลงสตรีม I / O ด้วยสตริง มันใช้วิธีการเดียวกันทั้งหมดกับIOคลาส แต่ไม่มีความสัมพันธ์ระหว่างการสืบทอดระหว่างสอง อย่างไรก็ตามStringIOสามารถใช้ได้ทุกที่และIOสามารถใช้ได้ นี้จะเป็นประโยชน์อย่างมากในการทดสอบหน่วยที่คุณสามารถเปลี่ยนไฟล์หรือstdinด้วยStringIOโดยไม่ต้องทำการเปลี่ยนแปลงใด ๆ ต่อไปโปรแกรมของคุณ เนื่องจากStringIOสอดคล้องกับโปรโตคอลเดียวกันขณะที่IOพวกเขาทั้งสองประเภทเดียวกันแม้ว่าพวกเขาจะเป็นชั้นเรียนที่แตกต่างกันและแบ่งปันความสัมพันธ์ (นอกเหนือจากเรื่องเล็กน้อยที่พวกเขาทั้งสองขยายObjectในบางจุด)


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

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

คุณมีข้อมูลอ้างอิงหรือคำหลักบางคำที่ฉันสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับภาษา LISP ทดลองที่คุณพูดถึงว่ามีความแตกต่างอย่างเป็นทางการใน mixins, structs, interfaces และคลาส?
โทร

@tel: ไม่ขอโทษ อาจประมาณ 15-20 ปีที่แล้วและในเวลานั้นความสนใจของฉันอยู่ทั่วสถานที่ ฉันไม่สามารถบอกคุณได้ว่าฉันกำลังมองหาอะไรเมื่อฉันเจอสิ่งนี้
Jörg W Mittag

awww นั่นคือรายละเอียดที่น่าสนใจที่สุดในคำตอบทั้งหมดนี้ ความจริงที่ว่าการแยกแนวคิดอย่างเป็นทางการนั้นเป็นไปได้จริง ๆ แล้วในการนำภาษามาใช้จริง ๆ นั้นช่วยแยกความแตกต่างของชั้นเรียน / ประเภทสำหรับฉัน ฉันเดาว่าฉันจะไปหา LISP นั้นด้วยตัวเองไม่ว่าในกรณีใด คุณจำได้ไหมถ้าคุณอ่านในบทความวารสาร / หรือคุณเพิ่งได้ยินเรื่องนี้ในบทสนทนา?
โทร

2

บางทีมันอาจมีประโยชน์ในการแยกความแตกต่างระหว่างชนิดและคลาสจากนั้นดำดิ่งลงไปในความแตกต่างระหว่างการพิมพ์ย่อยและการแบ่งคลาสย่อย

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

ฉันจะพัฒนารหัสจำลองของเล่นเพื่อช่วยอธิบายความแตกต่างระหว่างประเภทและคลาสเนื่องจากภาษาส่วนใหญ่พูดคุยกันอย่างน้อยส่วนหนึ่ง (ด้วยเหตุผลที่ดีที่ฉันจะสัมผัสสั้น ๆ )

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

ในภาษาของเล่นของเราเราอาจอนุญาตสำหรับการสร้างป้ายกำกับเช่นนั้น

declare type Int
declare type String

จากนั้นเราอาจติดป้ายกำกับค่าต่างๆว่าเป็นประเภทนี้

0 is of type Int
1 is of type Int
-1 is of type Int
...

"" is of type String
"a" is of type String
"b" is of type String
...

ด้วยข้อความเหล่านี้ typechecker ของเราสามารถปฏิเสธข้อความเช่น

0 is of type String

หากหนึ่งในข้อกำหนดของระบบพิมพ์ของเราคือว่าทุกการแสดงออกมีประเภทที่ไม่ซ้ำกัน

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

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

class StringClass:
  defMethod concatenate(otherString): ...
  defField size: ...

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

เราสามารถเลือกที่จะเชื่อมโยงคลาสกับประเภทเช่นว่าทุกอินสแตนซ์ของคลาสจะมีป้ายกำกับด้วยประเภทนั้นโดยอัตโนมัติ

associate StringClass with String

แต่ไม่ใช่ทุกประเภทจะต้องมีคลาสที่เกี่ยวข้อง

# Hmm... Doesn't look like there's a class for Int

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

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

associate MyCustomStringClass with String

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

แล้วประเภทย่อยล่ะ? การพิมพ์ย่อยเป็นชื่อสำหรับกฎทั่วไปในการตรวจสอบชนิดที่ผ่อนคลายกฎอื่น ๆ ที่คุณอาจมี กล่าวคือถ้าA is subtype of Bแล้วทุก typechecker ของคุณเรียกร้องฉลากของก็จะยอมรับBA

ตัวอย่างเช่นเราอาจทำสิ่งต่อไปนี้สำหรับหมายเลขของเราแทนที่จะเป็นสิ่งที่เราเคยทำก่อนหน้านี้

declare type NaturalNum
declare type Int
NaturalNum is subtype of Int

0 is of type NaturalNum
1 is of type NaturalNum
-1 is of type Int
...

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

class ExtendedStringClass is subclass of StringClass:
  # We get concatenate and size for free!
  def addQuestionMark: ...

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

ในทำนองเดียวกันเราสามารถตัดสินใจที่จะสร้างชั้นเรียนใหม่ทั้งหมดNewClassและทำ

associate NewClass with String

ตอนนี้ทุกอินสแตนซ์ของStringClassสามารถถูกแทนที่ด้วยNewClassจากมุมมองของเครื่องพิมพ์ดีด

ดังนั้นในทฤษฎีย่อยและ subclassing เป็นสิ่งที่แตกต่างกันโดยสิ้นเชิง แต่ไม่มีภาษาที่ฉันรู้ว่ามีประเภทและคลาสจริง ๆ แล้วทำสิ่งนี้ มาเริ่มต้นแยกภาษาของเราและอธิบายเหตุผลเบื้องหลังการตัดสินใจของเรา

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

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

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

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

และนี่ทำให้เราได้รับคุณลักษณะที่ส่วนใหญ่ของภาษา OO ที่พิมพ์แบบคงที่ยอดนิยมใช้ร่วมกัน มีชุดของ "ดั้งเดิม" (เช่นint, floatฯลฯ ) ซึ่งไม่เกี่ยวข้องกับชั้นเรียนใด ๆ และไม่ได้กำหนดโดยผู้ใช้ จากนั้นคุณจะมีคลาสที่ผู้ใช้กำหนดเองซึ่งมีชื่อเหมือนกันโดยอัตโนมัติและระบุคลาสย่อยด้วยการพิมพ์ย่อย

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

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