เมื่อใดที่คุณควรใช้คลาสส่วนตัว / ชั้นใน


21

เพื่อชี้แจงสิ่งที่ฉันถามคือ

public class A{
    private/*or public*/ B b;
}

เมื่อเทียบกับ

public class A{
    private/*or public*/ class B{
        ....
    }
}

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


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

ตัวอย่างที่น่าเชื่อถือคืออะไรหากตัวอย่างทางวิชาการไม่ดีพอ

@Job StyleCop จะส่งคำเตือนเฉพาะเมื่อมองเห็นคลาสภายในจากภายนอก หากเป็นแบบส่วนตัวก็ถือว่าใช้ได้และมีประโยชน์หลายอย่าง
julealgon

คำตอบ:


20

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


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

9

สมมติว่าคุณกำลังสร้างต้นไม้รายการกราฟและอื่น ๆ ทำไมคุณควรเปิดเผยรายละเอียดภายในของโหนดหรือเซลล์สู่โลกภายนอก

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

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

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


5

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

ตัวอย่างเช่นหากคุณต้องการผูกรายการข้อมูลที่ประกอบด้วยสองคลาสที่ไม่เกี่ยวข้อง:

public class UiLayer
{
    public BindLists(List<A> as, List<B> bs)
    {
        var list = as.ZipWith(bs, (x, y) => new LayerListItem { AnA = x, AB = y});
        // do stuff with your new list.
    }

    private class LayerListItem
    {
        public A AnA;
        public B AB;
    }
}

หากคลาสภายในของคุณถูกใช้โดยคลาสอื่นคุณควรแยกคลาสนั้น ถ้าคลาสภายในของคุณมีตรรกะใด ๆ คุณควรแยกมันออก

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


2
สมมติว่า C # คุณไม่สามารถใช้สิ่งอันดับที่นี่ได้ไหม
งาน

3
@ งานแน่นอน แต่บางครั้งtuple.ItemXทำให้รหัสไม่ชัดเจน
Lasse Espeholt

2
@Job yup, lasseespeholt มีอันนั้น ฉันอยากเห็นชั้นในของ {string Title; คำอธิบายสตริง; } กว่า Tuple <string, string>
Ed James

ณ จุดนั้นมันจะไม่เหมาะสมที่จะนำคลาสนั้นไปไว้ในไฟล์ของมันเอง?
งาน

ไม่ไม่ใช่ถ้าคุณใช้เพื่อผูกข้อมูลบางอย่างเข้าด้วยกันสั้น ๆ เพื่อให้ได้งานที่ไม่ออกนอกชั้นเรียนของผู้ปกครอง อย่างน้อยก็ไม่ใช่ในความคิดของฉัน!
Ed James

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

  • คลาสที่ซ้อนกันในภาษาอื่น (เช่น C ++) ไม่มีเน็คไทดังกล่าว คุณเพียงแค่ใช้การกำหนดขอบเขตและการควบคุมการเข้าถึงที่จัดทำโดยชั้นเรียน


3
จริงๆแล้ว Java มีทั้งสองอย่าง
Joachim Sauer

3

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


ปัญหานี้มีการบันทึกไว้ที่ใดที่หนึ่งหรือไม่
ริ้น

Downvoter: การยืนยันของฉันไม่ถูกต้องหรือไม่?
kevin cline

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

1
@gnat: ฉันทำวิจัยเล็กน้อยและในขณะที่นี่เป็นความจริงที่รู้จักกันดีฉันไม่พบการอ้างอิงที่ชัดเจนเกี่ยวกับข้อ จำกัด ของ Java HotSwap
kevin cline

เราไม่ได้อยู่บน Skeptics.SE :) การอ้างอิงบางอย่างจะทำมันไม่จำเป็นต้องชัดเจน
gnat

2

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


2

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

// async_op.h -- someone else's api.
struct callback
{
    virtual ~callback(){}
    virtual void fire() = 0;
};

void do_my_async_op(callback *p);



// caller.cpp -- my api
class caller
{
private :
    struct caller_inner : callback
    {
        caller_inner(caller &self) : self_(self) {}
        void fire() { self_.do_callback(); }
        caller &self_;
    };

    void do_callback()
    {
        // Real callback
    }

    caller_inner inner_;

public :
    caller() : inner_(*this) {}

    void do_op()
    {
        do_my_async_op(&inner_);
    }
};

ในสิ่งนี้ do_my_async_op ต้องการวัตถุชนิดการเรียกกลับที่จะส่งผ่านไป การติดต่อกลับนี้มีลายเซ็นของฟังก์ชันสมาชิกสาธารณะที่ใช้ API

เมื่อ do_op () ถูกเรียกจาก outer_class มันจะใช้อินสแตนซ์ของคลาสส่วนตัวภายในซึ่งสืบทอดจากคลาส callback ที่ต้องการแทนที่จะเป็นตัวชี้ไปยังตัวเอง คลาสภายนอกมีการอ้างอิงถึงคลาสภายนอกสำหรับจุดประสงค์ง่าย ๆ ในการหลีกเลี่ยงการโทรกลับไปที่คลาสส่วนตัวภายนอกฟังก์ชันสมาชิก do_callback

ประโยชน์ของสิ่งนี้คือคุณแน่ใจว่าไม่มีใครสามารถเรียกฟังก์ชั่นสมาชิก "fire ()" สาธารณะได้


2

ตาม Jon Skeet ใน C # in Depth การใช้คลาสที่ซ้อนเป็นวิธีเดียวที่จะใช้singleton safe-threaded ที่ปลอดภัยที่ขี้เกียจอย่างสมบูรณ์(ดูรุ่นที่ห้า) (จนถึง. NET 4)


1
เป็นInitialization On Demand Holder สำนวนใน Java มันยังต้องการชั้นคงที่ส่วนตัว แนะนำในคำถามที่พบบ่อยของ JMMโดย Brian Goetz ในJCiP 16.4และโดย Joshua Bloch ในJavaOne 2008 Java มีประสิทธิภาพมากกว่า (PDF) "สำหรับประสิทธิภาพสูงในสนามคงที่ ... "
gnat

1

คลาสส่วนตัวและภายในใช้เพื่อเพิ่มระดับของการห่อหุ้มและซ่อนรายละเอียดการใช้งาน

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

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


ข้อเสนอแนะที่สร้างสรรค์ใด ๆ ใน -1?
hiwaylon

1
ฉันไม่ได้ลงคะแนน แต่ฉันเดาว่าคุณไม่ได้ตอบคำถามจริง ๆ : คุณไม่ได้ให้เหตุผลใด ๆ ในการใช้ชั้นเรียนส่วนตัว / ชั้นใน คุณเพียงแค่อธิบายว่ามันทำงานอย่างไรและจะทำสิ่งที่คล้ายกันใน c ++ ได้อย่างไร
Simon Bergot

จริง ฉันคิดว่ามันชัดเจนโดยของฉันและคำตอบอื่น ๆ (ซ่อนรายละเอียดการใช้งานเพิ่มระดับของการห่อหุ้ม ฯลฯ ) แต่ฉันจะพยายามชี้แจงบางอย่าง ขอบคุณ @Simon
hiwaylon

1
การแก้ไขที่ดี และโปรดอย่าทำผิดเพราะปฏิกิริยาประเภทนี้ เครือข่าย SO กำลังพยายามบังคับใช้นโยบายเพื่อปรับปรุงอัตราส่วนสัญญาณต่อสัญญาณรบกวน บางคนมีความสวยที่เข้มงวดเกี่ยวกับขอบเขตคำถาม / คำตอบและบางครั้งลืมที่จะมีความสุข (โดยการแสดงความคิดเห็นของพวกเขา downvote ตัวอย่าง)
ไซมอน Bergot

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