อะไรคือบทบาทของซิงเกิลตันคลาสนามธรรมและอินเตอร์เฟส


13

ฉันกำลังศึกษา OOP ใน C ++ และแม้ว่าฉันตระหนักถึงคำจำกัดความของแนวคิดทั้งสามนี้ฉันก็ไม่สามารถรู้ได้เลยว่าจะใช้เมื่อไหร่หรืออย่างไร

ลองใช้คลาสนี้เป็นตัวอย่าง:

class Person{
    private:
             string name;
             int age;
    public:
             Person(string p1, int p2){this->name=p1; this->age=p2;}
             ~Person(){}

             void set_name (string parameter){this->name=parameter;}                 
             void set_age (int parameter){this->age=parameter;}

             string get_name (){return this->name;}
             int get_age (){return this->age;}

             };

1. ซิงเกิล

วิธีไม่ข้อ จำกัด ของการเรียนที่จะมีเพียงหนึ่งในการทำงานของวัตถุ?

คุณสามารถออกแบบคลาสที่มีเพียง 2 อินสแตนซ์ได้หรือไม่ หรืออาจจะ 3?

เมื่อใดที่แนะนำ / จำเป็นต้องใช้ซิงเกิลตัน? เป็นการปฏิบัติที่ดีหรือไม่?

2. ระดับนามธรรม

เท่าที่ฉันรู้ถ้ามีเพียงฟังก์ชั่นเสมือนบริสุทธิ์เพียงอย่างเดียวคลาสจะกลายเป็นนามธรรม ดังนั้นการเพิ่ม

virtual void print ()=0;

จะทำมันใช่ไหม

ทำไมคุณต้องมีคลาสที่ไม่จำเป็นต้องมีวัตถุ?

3.Interface

หากอินเตอร์เฟสเป็นคลาสนามธรรมซึ่งเมธอดทั้งหมดเป็นฟังก์ชันเสมือนแท้

ความแตกต่างหลักระหว่าง 2 สิ่งนี้คืออะไร

ขอบคุณล่วงหน้า!


2
Singleton แย้งค้นหาในเว็บไซต์นี้เพื่อรับความคิดเห็นที่หลากหลาย
Winston Ewert

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

1
โปรดทีละคน
JeffO

คำตอบ:


17

1. ซิงเกิล

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

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

หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับ Singletons คุณสามารถเริ่มต้นในWikipediaและโดยเฉพาะอย่างยิ่งสำหรับ C ++ ในโพสต์นี้

มีบางอย่างที่ดีและไม่ดีเกี่ยวกับรูปแบบนี้ แต่การสนทนานี้อยู่ที่อื่น

2. ชั้นเรียนนามธรรม

ใช่ถูกต้อง. เพียงวิธีเสมือนเดียวจะทำเครื่องหมายระดับเป็นนามธรรม

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

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

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

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

3. การเชื่อมต่อ

ไม่มีอินเทอร์เฟซแท้ใน C ++ ในลักษณะเดียวกับที่คุณมีใน Java หรือ C # วิธีเดียวที่จะสร้างได้คือการมีคลาสนามธรรมที่บริสุทธิ์ซึ่งเลียนแบบพฤติกรรมส่วนใหญ่ที่คุณต้องการจากอินเทอร์เฟซ

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

คุณสามารถอ่านเกี่ยวกับข้อมูลจำเพาะของอินเตอร์เฟสสำหรับ C # ใน MSDN เพื่อให้มีแนวคิดที่ดีกว่า:

http://msdn.microsoft.com/en-us/library/ms173156.aspx

C ++ จะให้พฤติกรรมแบบเดียวกันโดยมีคลาสนามธรรมที่บริสุทธิ์


2
คลาสพื้นฐานที่เป็นนามธรรมล้วนให้ทุกสิ่งที่อินเตอร์เฟซทำ อินเทอร์เฟซมีอยู่ใน Java (และ C #) เนื่องจากผู้ออกแบบภาษาต้องการป้องกันการสืบทอดหลาย ๆ อัน (เนื่องจากการปวดหัวที่สร้างขึ้น) แต่ได้รับการยอมรับว่ามีการใช้งานทั่วไปหลายมรดกที่ไม่มีปัญหา
Gort the Robot

@StevenBurnap: แต่ไม่ใช่ใน C ++ ซึ่งเป็นบริบทของคำถาม
DeadMG

3
เขาถามเกี่ยวกับ C ++ และอินเทอร์เฟซ "อินเทอร์เฟซ" ไม่ใช่คุณสมบัติภาษาของ C ++ แต่ผู้ใช้สร้างอินเทอร์เฟซใน C ++ ที่ทำงานเหมือนกับอินเทอร์เฟซ Java โดยใช้คลาสพื้นฐานที่เป็นนามธรรม พวกเขาทำเช่นนั้นก่อนที่ Java จะมีอยู่จริง
Gort the Robot

ดูaccu.org/index.php/journals/233
Gort the Robot

1
เช่นเดียวกันกับ Singletons ใน C ++ ทั้งคู่เป็นรูปแบบการออกแบบไม่ใช่คุณสมบัติทางภาษา นี่ไม่ได้หมายความว่าผู้คนไม่ได้พูดถึงส่วนต่อประสานใน C ++ และมีไว้เพื่ออะไร แนวคิดของ "อินเทอร์เฟซ" ออกมาจากระบบส่วนประกอบเช่น Corba และ COM ซึ่งทั้งคู่ได้รับการพัฒนาเพื่อใช้ในบริสุทธิ์ C ใน C ++ อินเทอร์เฟซมักจะใช้กับคลาสฐานนามธรรมซึ่งวิธีการทั้งหมดเป็นเสมือนจริง ฟังก์ชั่นการใช้งานนี้เหมือนกับของส่วนต่อประสาน Java ดังนั้นแนวคิดของอินเตอร์เฟส Java จึงเป็นเซตย่อยของคลาสนามธรรม C ++
Gort the Robot

8

คนส่วนใหญ่ได้อธิบายแล้วว่าชั้นเดี่ยว / บทคัดย่อ หวังว่าฉันจะให้มุมมองที่แตกต่างกันเล็กน้อยและให้ตัวอย่างที่เป็นประโยชน์

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

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

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

แล้วคุณจะใช้ซิงเกิลที่ไหน? นี่คือตัวอย่างบางส่วน:

  • การบันทึก - หากคุณต้องการให้กระบวนการทั้งหมดของคุณมีบันทึกเดียวคุณสามารถสร้างวัตถุบันทึกและส่งต่อได้ทุกที่ แต่จะเกิดอะไรขึ้นถ้าคุณมีรหัสแอปพลิเคชันดั้งเดิมที่มีบรรทัด 100,000,000 เส้น ปรับเปลี่ยนทั้งหมดหรือไม่ หรือคุณสามารถแนะนำสิ่งต่อไปนี้และเริ่มใช้มันได้ทุกที่ที่คุณต้องการ:

    CLog::GetInstance().write( "my log message goes here" );
  • แคชการเชื่อมต่อเซิร์ฟเวอร์ - นี่คือสิ่งที่ฉันต้องแนะนำในแอปพลิเคชันของเรา รหัสฐานของเราและมีจำนวนมากใช้ในการเชื่อมต่อกับเซิร์ฟเวอร์ทุกครั้งที่พอใจ ส่วนใหญ่เวลานี้ก็โอเคยกเว้นว่ามีความล่าช้าใด ๆ ในเครือข่าย เราต้องการโซลูชันและการออกแบบแอปพลิเคชันอายุ 10 ปีที่ไม่ได้อยู่บนโต๊ะจริงๆ ฉันเขียน CServerConnectionManager แบบซิงเกิล จากนั้นฉันค้นหารหัสและแทนที่การโทร CoCreateInstanceWithAuth ด้วยการเรียกลายเซ็นเหมือนกันที่เรียกคลาสของฉัน หลังจากการพยายามเชื่อมต่อครั้งแรกถูกแคชและความพยายาม "เชื่อมต่อ" ครั้งที่เหลือนั้นเกิดขึ้นทันที บางคนบอกว่าซิงเกิลตันนั้นชั่วร้าย ฉันบอกว่าพวกเขาช่วยชีวิตฉัน

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

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

ต้องบอกว่าทั้งหมดนี้คุณต้องจำไว้เมื่อใดและที่ไหนที่จะใช้ซิงเกิล 9 จาก 10 ครั้งมีวิธีแก้ปัญหาที่ดีกว่าและคุณควรใช้มันแทน อย่างไรก็ตามมีบางครั้งที่ซิงเกิลนั้นเป็นตัวเลือกที่ถูกต้อง

หัวข้อถัดไป ... อินเทอร์เฟซและคลาสนามธรรม ครั้งแรกที่คนอื่น ๆ ได้กล่าวถึง interface เป็นคลาสนามธรรม แต่มันไปไกลกว่านั้นโดยการบังคับให้มันไม่มีการใช้ ในบางคำหลักอินเทอร์เฟซภาษาเป็นส่วนหนึ่งของภาษา ใน C ++ เราเพียงแค่ใช้คลาสนามธรรม Microsoft VC ++ ใช้ขั้นตอนเดียวเพื่อกำหนดสิ่งนี้ภายใน:

typedef struct interface;

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

แล้วคุณจะใช้สิ่งนี้ที่ไหน ลองกลับไปที่ตัวอย่างของตารางวัตถุที่กำลังรันอยู่ สมมุติว่าคลาสฐานมี ...

virtual void print () = 0;

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

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

นี่เป็นอีกตัวอย่างหนึ่ง สมมติว่าฉันมีคลาสที่ใช้ตัวบันทึก CLog คลาสนี้เขียนไปยังไฟล์บนโลคัลดิสก์ ฉันเริ่มใช้คลาสนี้ในโค้ด 100,000 บรรทัดที่เป็นมรดกของฉัน ทั่วทุกสถานที่. ชีวิตช่างดีจนกระทั่งมีคนพูดว่าเดี๋ยวก่อนมาเขียนในฐานข้อมูลแทนไฟล์ ตอนนี้ฉันสร้างคลาสใหม่แล้วเรียกมันว่า CDbLog และเขียนไปยังฐานข้อมูล คุณสามารถถ่ายภาพความยุ่งยากในการผ่าน 100,000 บรรทัดและเปลี่ยนทุกอย่างจาก CLog เป็น CDbLog ได้หรือไม่? หรือฉันอาจมี:

interface ILogger {
    virtual void write( const char* format, ... ) = 0;
};

class CLog : public ILogger { ... };

class CDbLog : public ILogger { ... };

class CLogFactory {
    ILogger* GetLog();
};

หากรหัสทั้งหมดกำลังใช้งานอินเทอร์เฟซ ILogger สิ่งที่ฉันต้องเปลี่ยนก็คือการนำไปใช้ภายในของ CLogFactory :: GetLog () ส่วนที่เหลือของรหัสจะทำงานโดยอัตโนมัติโดยที่ฉันไม่ต้องยกนิ้ว

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการเชื่อมต่อและการออกแบบ OO ดีผมจะขอแนะนำลุงบ๊อบเปรียวหลักการรูปแบบและวิธีปฏิบัติใน C # หนังสือเล่มนี้เต็มไปด้วยตัวอย่างที่ใช้ abstractions และให้คำอธิบายภาษาธรรมดาทุกอย่าง


4

เมื่อใช้แนะนำ / จำเป็นต้องใช้ซิงเกิลตัน? เป็นการปฏิบัติที่ดีหรือไม่?

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

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


ส่วนต่อประสานเป็นส่วนย่อยของคลาสนามธรรม อินเทอร์เฟซเป็นคลาสนามธรรมโดยไม่มีวิธีที่กำหนดไว้ (คลาสนามธรรมโดยไม่มีรหัสเลย)
Gort the Robot

1
@StevenBurnap: อาจเป็นภาษาอื่น
DeadMG

4
"Interface" เป็นเพียงการประชุมใน C ++ เมื่อฉันเห็นมันใช้มันเป็นคลาสนามธรรมที่มีวิธีเสมือนจริงที่บริสุทธิ์และไม่มีคุณสมบัติ เห็นได้ชัดว่าคุณสามารถเขียนชั้นเรียนเก่า ๆ แล้วตบ "I" ต่อหน้าชื่อ
Gort หุ่นยนต์

นี่คือวิธีที่ฉันคาดหวังให้คนตอบคำถามนี้ ทีละคำถาม อย่างไรก็ตามขอบคุณมากสำหรับการแบ่งปันความรู้ของคุณ ชุมชนนี้เป็นเวลาการลงทุนที่คุ้มค่าใน.
appoll

3

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

ซิงเกิลตันที่มีจำนวนอินสแตนซ์คงที่ 2 ตัวหรือมากกว่านั้นคือมัลติตันคิดถึงการรวมการเชื่อมต่อฐานข้อมูลเป็นต้น

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

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

หมายเหตุ:อินเทอร์เฟซและคลาสนามธรรมไม่แตกต่างกันมากในโลก C ++ ที่มีการสืบทอดหลายแบบ แต่มีความหมายแตกต่างกันใน Java และคณะ


พูดได้เป็นอย่างดี! +1
jmort253

3

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

สมมติว่าเรามีฟังก์ชั่นเหมือนกับรหัส Python ดังต่อไปนี้:

function foo(objs):
    for obj in objs:
        obj.printToScreen()

class HappyWidget:
    def printToScreen(self):
        print "I am a happy widget"

class SadWidget:
    def printToScreen(self):
        print "I am a sad widget"

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

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

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

void foo( Printable *objs[], int n){ //Please correctme if I messed up on the type signature
    for(int i=0; i<n; i++){
        objs[i]->printToScreen();
    }
}

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

ใน C ++ แนวคิด absctract class และ interface นั้นค่อนข้างพร่ามัว ถ้าคุณต้องการนิยามให้ดีขึ้นคลาสนามธรรมเป็นสิ่งที่คุณคิดขณะที่อินเตอร์เฟสปกติหมายถึงความคิดข้ามภาษาที่กว้างกว่าของชุดของวิธีการมองเห็นที่วัตถุปรากฏขึ้น (แม้ว่าบางภาษาเช่น Java ให้ใช้คำอินเทอร์เฟซเพื่ออ้างถึงบางสิ่งบางอย่างโดยตรงเช่นคลาสฐานนามธรรม)

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


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


BTW บางคนอาจแสดงความคิดเห็นว่าคำว่า "อินเตอร์เฟส" มีความหมายเฉพาะในภาษาจาวา ฉันคิดว่ามันดีกว่าที่จะยึดติดกับคำจำกัดความทั่วไปมากขึ้นในตอนนี้


1

อินเตอร์เฟซ

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

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

สมมติว่าคุณมีเว็บไซต์เล็ก ๆ และคุณบันทึกข้อมูลผู้ใช้ทั้งหมดของคุณในไฟล์ csv ไม่ใช่วิธีที่ซับซ้อนที่สุด แต่ทำงานได้ดีพอที่จะเก็บรายละเอียดผู้ใช้ของแม่ของคุณ ต่อมาไซต์ของคุณจะปิดและคุณมีผู้ใช้ 10,000 คน อาจถึงเวลาที่จะใช้ฐานข้อมูลที่เหมาะสม

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

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

สิ่งที่ดีคือสิ่งที่คุณต้องทำตอนนี้คือการเปลี่ยนแปลง

Persister* prst = new CsvPersister();

ถึง

Persister* prst = new DbPersister();

แล้วคุณทำเสร็จแล้ว การโทรของคุณprst.store()และprst.retrieve()จะยังคงทำงานอยู่พวกเขากำลังจัดการ "เบื้องหลัง" ที่แตกต่างออกไป

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

ฉันเป็นสนิมใน C ++ ของฉันจริงๆดังนั้นฉันจะใช้ตัวอย่างการเขียนโปรแกรมทั่วไปบางอย่าง ตู้คอนเทนเนอร์เป็นตัวอย่างที่ดีของวิธีที่อินเตอร์เฟสทำให้ชีวิตของคุณง่ายขึ้น

คุณสามารถมีArray, LinkedList, BinaryTreeฯลฯ subclasses ทั้งหมดContainerซึ่งมีวิธีการเช่นinsert(), ,find()delete()

ตอนนี้เมื่อเพิ่มบางสิ่งลงในรายการที่เชื่อมโยงคุณไม่จำเป็นต้องรู้เลยว่ารายการที่เชื่อมโยงคืออะไร คุณเพียงแค่โทรmyLinkedList->insert(4)และมันจะทำซ้ำอย่างน่าอัศจรรย์ผ่านรายการและติดไว้ในนั้น แม้ว่าคุณจะรู้ว่ารายการที่เชื่อมโยงทำงานอย่างไร (ซึ่งคุณควรจริง ๆ ) คุณไม่จำเป็นต้องค้นหาฟังก์ชั่นเฉพาะของมันเพราะคุณอาจรู้อยู่แล้วว่าพวกเขากำลังใช้อะไรที่แตกต่างจากContainerก่อนหน้านี้

ชั้นเรียนนามธรรม

คลาสนามธรรมค่อนข้างคล้ายกับอินเตอร์เฟส (ส่วนใหญ่แล้วอินเทอร์เฟซเป็นคลาสนามธรรม แต่ที่นี่ฉันหมายถึงคลาสพื้นฐานที่มีวิธีการบางอย่าง

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

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

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

singletons

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

นี่คือการอภิปรายที่ดีเกี่ยวกับสถานะโลกจากคนที่มีประสบการณ์และระมัดระวังมากกว่า: ทำไมรัฐโลกถึงชั่วร้าย?


1

ในอาณาจักรสัตว์มีสัตว์ต่าง ๆ ซึ่งเป็นสัตว์เลี้ยงลูกด้วยนม ที่นี่เลี้ยงลูกด้วยนมเป็นชั้นฐานและสัตว์ต่าง ๆ มาจากมัน

คุณเคยเห็นสัตว์เลี้ยงลูกด้วยนมเดินผ่านมาหรือไม่? ใช่หลายครั้งฉันมั่นใจ - อย่างไรก็ตามพวกเขาเป็นสัตว์เลี้ยงลูกด้วยนมทุกชนิดใช่ไหม?

คุณไม่เคยเห็นอะไรที่เป็นเพียงสัตว์เลี้ยงลูกด้วยนม พวกเขาเป็นสัตว์เลี้ยงลูกด้วยนมทุกประเภท

สัตว์เลี้ยงลูกด้วยนมในชั้นเรียนจะต้องกำหนดลักษณะและกลุ่มต่าง ๆ แต่มันไม่ได้มีอยู่จริงเป็นนิติบุคคล

ดังนั้นมันจึงเป็นคลาสพื้นฐานที่เป็นนามธรรม

สัตว์เลี้ยงลูกด้วยนมย้ายอย่างไร พวกเขาเดินว่ายน้ำบิน ฯลฯ หรือไม่

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

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

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

ดังนั้น MoveAround จึงเป็นฟังก์ชั่นเสมือนแท้

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

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