เข้าถึงฟิลด์ส่วนตัวของวัตถุอื่นในคลาสเดียวกัน


91
class Person 
{
   private BankAccount account;

   Person(BankAccount account)
   {
      this.account = account;
   }

   public Person someMethod(Person person)
   {
     //Why accessing private field is possible?

     BankAccount a = person.account;
   }
}

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


5
ฉันเชื่อว่า OP ถือว่าอ็อบเจกต์ "Person" ที่ส่งผ่านไปยัง "someMethod" เป็นอ็อบเจกต์แยกต่างหากดังนั้นเมธอดจึงไม่ควรเข้าถึงไพรเวตเมมเบอร์ ... แม้ว่ามันจะอยู่ในคลาส "Person" ก็ตาม
Inisheer

1
บางภาษาไม่ทำเช่นนี้ (ตัวอย่างเช่น Newspeak) คุณไม่น่าจะได้รับคำตอบที่ดีว่าทำไม คุณจะได้รับคำตอบจากสิ่งที่เกิดขึ้นตามที่ระบุไว้
Tom Hawtin - แทคไลน์

someMethodไม่ถูกต้อง มันไม่ส่งคืนอะไรเลย voidมันจะต้องเป็น
Nicolas Barbulesco

1
หากไม่ใช่กรณีนี้จะเป็นการยากมากที่จะเขียนตัวสร้างสำเนาและตัวดำเนินการกำหนด
rozina

คำตอบ:


73

ฉันก็อยากรู้คำตอบเหมือนกัน

คำตอบที่น่าพึงพอใจที่สุดที่ฉันพบคือจาก Artemix ในโพสต์อื่นที่นี่ (ฉันกำลังเปลี่ยนชื่อ AClass ด้วยคลาส Person): เหตุใดจึงมีตัวปรับการเข้าถึงระดับคลาสแทนที่จะเป็นระดับอ็อบเจ็กต์

ตัวปรับแต่งส่วนตัวบังคับใช้หลักการ Encapsulation

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

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

แก้ไข: โปรดโหวตคำตอบของ Artemix ฉันแค่คัดลอกวาง


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

16
@NicolasBarbulesco ฉันคิดว่าเหตุผลที่ให้ไว้ในคำตอบนั้นฟังดูดี พูดเช่นคุณต้องการที่จะใช้equals(Object)วิธีการในชั้นเรียน Java เพื่อตรวจสอบความเท่าเทียมกันของวัตถุที่มีอินสแตนซ์ของผู้อื่นPerson Personคุณอาจต้องการเปิดใช้งานโลกภายนอกเพื่อตรวจสอบว่าอินสแตนซ์ทั้งสองเท่ากันหรือไม่ แต่คุณอาจไม่ต้องการเปิดเผยฟิลด์ส่วนตัวทั้งหมดของคลาสที่จำเป็นสำหรับการตรวจสอบความเท่าเทียมกับโลกภายนอกโดยใช้วิธีการเข้าถึงสาธารณะ การเข้าถึงprivateฟิลด์ระดับคลาสช่วยให้สามารถใช้วิธีการดังกล่าวได้โดยไม่จำเป็นต้องใช้วิธีการสาธารณะดังกล่าว
Malte Skoruppa

1
@MalteSkoruppa - นี่เป็นตัวอย่างที่ดี (การใช้วิธีการequals)
Nicolas Barbulesco

@MalteSkoruppa - อย่างไรก็ตามการใช้วิธีนี้equalsสามารถทำได้โดยการโทรหาผู้เข้าถึงส่วนตัว
Nicolas Barbulesco

@NicolasBarbulesco ใช่แน่นอน แต่ประเด็นก็คือไม่ว่าคุณจะใช้ตัวเข้าถึงส่วนตัวหรือเข้าถึงฟิลด์ส่วนตัวโดยตรงเพื่อใช้วิธีprivateนี้จะต้องให้สิทธิ์การเข้าถึงระดับคลาส ฉันยอมรับว่าโดยทั่วไปการใช้ accessors เป็นนิสัยที่ดีแม้ว่าในกรณีนี้ส่วนใหญ่จะเป็นเรื่องของบริบทและสไตล์ส่วนตัว โปรดทราบว่าทั้งโจชัวโบลชในที่มีประสิทธิภาพ Java (รายการ 8) และทาลโคเฮนในบทความดร. Dobbs นี้equalsโดยตรงเข้าถึงเขตภาคเอกชนในชื่อรหัสของพวกเขาเมื่อพูดถึงวิธีการดำเนินการ
Malte Skoruppa

17

ดูข้อกำหนดภาษา Java ส่วน 6.6.1 การกำหนดความสามารถในการเข้าถึง

มันระบุ

มิฉะนั้นหากมีการประกาศสมาชิกหรือตัวสร้างprivateการเข้าถึงจะได้รับอนุญาตก็ต่อเมื่อเกิดขึ้นภายในเนื้อความของคลาสระดับบนสุด (§7.6) ที่ล้อมรอบการประกาศของสมาชิกหรือตัวสร้าง

คลิกลิงก์ด้านบนเพื่อดูรายละเอียดเพิ่มเติม ดังนั้นคำตอบคือ: เพราะ James Gosling และผู้เขียนคนอื่น ๆ ของ Java ตัดสินใจให้เป็นเช่นนั้น


17

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

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

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


2
คำตอบนี้ต้องได้รับการโหวต! คำตอบอื่น ๆ เพียงแค่ระบุ 'กฎ' ใหม่ แต่มีเพียงคำตอบนี้เท่านั้นที่เปิดเผยเหตุผลเบื้องหลังกฎและตรงประเด็น
mzoz

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

4

สิ่งนี้ได้ผลเพราะคุณอยู่ในclass Person- ชั้นเรียนได้รับอนุญาตให้เข้าไปในชั้นเรียนของตัวเอง สิ่งนี้ช่วยได้มากเมื่อคุณต้องการเขียนตัวสร้างการคัดลอกตัวอย่างเช่น:

class A
{
   private:
      int x;
      int y;
   public:
      A(int a, int b) x(a), y(b) {}
      A(A a) { x = a.x; y = y.x; }
};

หรือถ้าเราต้องการเขียนoperator+และoperator-สำหรับชั้นเรียนจำนวนมาก


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

แน่นอนว่าหากคุณกำลังพยายามสร้างคลาส A จากออบเจ็กต์ของคลาส B และ B มีองค์ประกอบส่วนตัวดังนั้น A ต้องประกาศว่าเป็นเพื่อนหรือสามารถดูเฉพาะส่วนสาธารณะ [อาจได้รับการป้องกันถ้า A มาจาก B ].
Mats Petersson

ใน java และ. net ไม่มีแนวคิดเรื่องเพื่อน ในกรณีเช่นนี้จะจัดการอย่างไร?
Nageswaran

1

เพียง 2 เซ็นต์ของฉันกับคำถามที่ว่าทำไมความหมายของการมองเห็นส่วนตัวใน Java จึงเป็นระดับคลาสมากกว่าระดับอ็อบเจ็กต์

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

ในความเป็นจริงฉันไม่สามารถสร้างหรือหาตัวอย่างที่แสดงให้เห็นว่าการมองเห็นในระดับคลาสส่วนตัว (เช่นที่ Java นำเสนอ) สร้างปัญหาใด ๆ หากเทียบกับการมองเห็นในระดับออบเจ็กต์ส่วนตัว

ที่กล่าวว่าภาษาโปรแกรมที่มีระบบนโยบายการมองเห็นที่ละเอียดยิ่งขึ้นสามารถให้ทั้งการมองเห็นวัตถุในระดับวัตถุและระดับชั้นเรียน

ตัวอย่างเช่นEiffelมีการส่งออกแบบเฉพาะเจาะจง: คุณสามารถส่งออกคุณลักษณะของคลาสใด ๆ ไปยังคลาสใดก็ได้ที่คุณเลือกจาก {NONE} (object-private) ถึง {ANY} (เทียบเท่ากับ public และเป็นค่าเริ่มต้น) ไปยัง {PERSON} (class-private ดูตัวอย่างของ OP) สำหรับกลุ่มเฉพาะของชั้นเรียน {PERSON, BANK}

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

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


0

เนื่องจากprivate ตัวแก้ไขการเข้าถึงทำให้สามารถมองเห็นได้ภายในคลาสเท่านั้น วิธีนี้เป็นวิธีที่ยังอยู่ในชั้นเรียน


คำถามของฉันคือทำไมมันออกแบบมาเหมือน "ภายในคลาส" แต่ทำไมไม่ชอบ "เฉพาะภายในวัตถุ" ล่ะ?
Nageswaran

เพราะนั่นคือวิธีการสร้าง java
darijan

แล้วทำไม java ถึงทำ java แบบนั้น?
Nageswaran

Java ไม่ได้สร้างขึ้นผู้สร้าง Java ก็ทำ ทำไม? เพราะพวกเขาร็อค!
darijan

1
ผู้สร้าง Java จะไม่ทำโดยไม่มีเหตุผลใด ๆ และต้องมีเหตุผลบางประการในการทำเช่นนี้ ฉันถามเหตุผล
Nageswaran

0

privateข้อมูลสามารถเข้าถึงได้ในระดับ / วัตถุที่ฟิลด์ที่มีการประกาศ เป็นแบบส่วนตัวสำหรับคลาส / อ็อบเจ็กต์อื่น ๆ นอกเหนือจากคลาส / อ็อบเจ็กต์อื่น ๆ


-1

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


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