คุณจะเข้าถึงคุณสมบัติของวัตถุจากภายในวิธีการวัตถุได้อย่างไร [ปิด]


97

วิธี "เจ้าระเบียบ" หรือ "ถูกต้อง" ในการเข้าถึงคุณสมบัติของวัตถุจากภายในเมธอดอ็อบเจ็กต์ที่ไม่ใช่เมธอด getter / setter คืออะไร?

ฉันรู้ว่าจากภายนอกวัตถุคุณควรใช้ getter / setter แต่จากภายในคุณจะทำ:

Java:

String property = this.property;

PHP:

$property = $this->property;

หรือคุณจะทำ:

Java:

String property = this.getProperty();

PHP:

$property = $this->getProperty();

ยกโทษให้ฉันด้วยถ้า Java ของฉันหลุดไปนิดหน่อยเป็นเวลาหนึ่งปีแล้วที่ฉันตั้งโปรแกรมใน Java ...

แก้ไข:

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

คำตอบ:


62

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


ไม่ได้ทำอย่างอื่นใน setter นอกเหนือจากการตั้งค่าคุณสมบัติเป็นตัวอย่างของการใช้งานที่ไม่ถูกต้องใน Java
euphoria83

1
@ euphoria83 บางที แต่นั่นไม่ได้กีดกันไม่ให้เกิดขึ้น
Greg Hurlman

ฉันเห็นด้วยกับคำตอบเดิม อย่างไรก็ตามโปรดจำไว้ว่าหากคุณกำลังตั้งค่าหรือรับสิ่งที่เป็นส่วนตัวภายในคลาสหรือออบเจ็กต์คุณต้องตรวจสอบให้แน่ใจว่า getter / setter ไม่เป็นวัฏจักรหรือซ้ำซ้อนในทางใดทางหนึ่งที่ทำให้โค้ดของคุณล้มเหลว หากคุณไม่สามารถใช้ getter / setter ได้ให้เข้าถึงโดยตรง (เช่น getter / setter ของคุณเข้าถึงวิธีการรอง (ส่วนตัว / ป้องกัน) เพื่อจัดการข้อมูล)
Rick Mac Gillis

43

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

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


26

ฉันค่อนข้างประหลาดใจที่ความรู้สึกเป็นเอกฉันท์gettersและตัวเซ็ตเตอร์นั้นดีและดี ฉันขอแนะนำบทความก่อความไม่สงบโดย Allen Holub " Getters And Setters Are Evil " จริงอยู่ที่ชื่อเรื่องนี้มีไว้สำหรับสร้างความตกใจ แต่ผู้เขียนให้คะแนนที่ถูกต้อง

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

ยิ่งไปกว่านั้นจากมุมมอง OO อย่างเคร่งครัดวัตถุควรตอบสนองต่อข้อความ (วิธีการ) ที่สอดคล้องกับความรับผิดชอบเดียว (หวังว่า) ของพวกเขา ส่วนใหญ่gettersและsettersไม่สมเหตุสมผลสำหรับวัตถุที่เป็นส่วนประกอบของพวกเขา ทำให้รู้สึกมากขึ้นกว่าที่ฉันPen.dispenseInkOnto(Surface)Pen.getColor()

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

อย่างไรก็ตาม Getters และ setters เป็นความชั่วร้ายที่จำเป็นในขอบเขตของเลเยอร์ - UI ความคงอยู่และอื่น ๆ การเข้าถึงภายในของคลาสที่ จำกัด เช่นคีย์เวิร์ดเพื่อนของ C ++, การเข้าถึงแพ็กเกจที่มีการป้องกันของ Java, การเข้าถึงภายในของ. NET และรูปแบบคลาสเพื่อนสามารถช่วยคุณลดการมองเห็นgettersและตัวตั้งค่าให้เหลือเฉพาะผู้ที่ต้องการเท่านั้น


19

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

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


ถ้าออบเจ็กต์ Student เป็นอ็อบเจ็กต์ธุรกิจ / โดเมนคุณกำลังผสมรายละเอียดโครงสร้างพื้นฐาน ตามหลักการแล้ววัตถุทางธุรกิจ / โดเมนควรเกี่ยวข้องกับตรรกะของธุรกิจ / โดเมนเท่านั้น
moffdub

จะเกิดอะไรขึ้นถ้าคุณเพิ่มบูลีนบางประเภทให้กับ getter เช่น PHP: public function getName ($ outsideCall = true) {if ($ outsideCall) {$ this-> IncrementNameCalled (); } คืน $ this-> name; } แล้วจากภายใน Object เองถ้าคุณเรียกชื่อคุณสามารถป้องกันไม่ให้เพิ่มขึ้นได้โดย: PHP: $ name = $ this-> getName (false); ฉันเพิ่งจะลงน้ำที่นี่เหรอ?
cmcculloh

14

PHP มีวิธีจัดการสิ่งนี้มากมายรวมถึงวิธีการใช้เวทมนตร์__getและ__setแต่ฉันชอบตัวรับและตัวตั้งค่าที่ชัดเจน นี่คือเหตุผล:

  1. การตรวจสอบสามารถวางไว้ใน setters (และ getters สำหรับเรื่องนั้น)
  2. Intellisense ทำงานร่วมกับวิธีการที่ชัดเจน
  3. ไม่มีคำถามว่าคุณสมบัติเป็นแบบอ่านอย่างเดียวเขียนอย่างเดียวหรืออ่าน - เขียน
  4. การดึงคุณสมบัติเสมือน (กล่าวคือค่าจากการคำนวณ) มีลักษณะเหมือนกับคุณสมบัติทั่วไป
  5. คุณสามารถตั้งค่าคุณสมบัติออบเจ็กต์ที่ไม่เคยกำหนดได้อย่างง่ายดายจากที่ใดก็ได้ซึ่งจะไม่มีเอกสารประกอบ

13

ฉันเพิ่งจะลงน้ำที่นี่เหรอ?

บางที;)

อีกวิธีหนึ่งคือการใช้วิธีการส่วนตัว / การป้องกันเพื่อทำการรับ (แคช / db / etc) และกระดาษห่อสาธารณะสำหรับการเพิ่มจำนวน:

PHP:

public function getName() {
    $this->incrementNameCalled();
    return $this->_getName();
}

protected function _getName() {
    return $this->name;
}

จากนั้นจากภายในวัตถุเอง:

PHP:

$name = $this->_getName();

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


12

ฉันต้องพลาดตรงนี้ทำไมคุณถึงใช้ getter ในวัตถุเพื่อเข้าถึงคุณสมบัติของวัตถุนั้น

เมื่อนำสิ่งนี้ไปสู่ข้อสรุปผู้เรียกควรเรียกผู้เริ่มต้นซึ่งควรเรียกผู้เริ่มต้น

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


ฉันถูกผลักดันด้วยความต้องการเดียวกันที่คุณต้องแสดงความคิดเห็น ... บวกกับคำตอบว่าไม่ปิด;)
Egg Vans

8

วิธีที่พิถีพิถัน OO คือการหลีกเลี่ยงทั้งสองและปฏิบัติตามกฎหมายของ Demeterโดยใช้บอกไม่ถามวิธีการ

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

  doSomethingWithProperty() {
     doSomethingWith( this.property ) ;
  }

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

  doSomethingWithProperty( this.daysPerWeek() ) ;

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


8

จะดีกว่าถ้าใช้วิธีการเข้าถึงแม้ภายในวัตถุ นี่คือประเด็นที่อยู่ในใจของฉันทันที:

  1. ควรทำเพื่อรักษาความสอดคล้องกับการเข้าถึงที่ทำจากภายนอกวัตถุ

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


8

หากคุณหมายถึง "การห่อหุ้มส่วนใหญ่" โดย "คนเจ้าระเบียบ" ฉันมักจะประกาศเขตข้อมูลทั้งหมดของฉันเป็นแบบส่วนตัวจากนั้นใช้ "this.field" จากภายในชั้นเรียนเอง สำหรับคลาสอื่น ๆ รวมถึงคลาสย่อยฉันเข้าถึงสถานะอินสแตนซ์โดยใช้ getters


7

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


7

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

นักพัฒนา. NET สามารถใช้คุณสมบัติอัตโนมัติเพื่อบังคับใช้สิ่งนี้เนื่องจากคุณไม่เห็นตัวแปรสำรองในขณะออกแบบ


7

มันขึ้นอยู่กับ. เป็นปัญหาเกี่ยวกับสไตล์มากกว่าสิ่งอื่นใดและไม่มีกฎที่ยาก


7

ฉันพูดผิดได้เพราะฉันเป็น autodidact แต่ฉันไม่เคยใช้คุณสมบัติสาธารณะในคลาส Java ของฉันมันเป็นแบบส่วนตัวหรือได้รับการปกป้องเสมอดังนั้นโค้ดภายนอกจะต้องเข้าถึงโดย getters / setters จะดีกว่าสำหรับวัตถุประสงค์ในการบำรุงรักษา / ดัดแปลง และสำหรับโค้ดภายในคลาส ... หากเมธอด getter ไม่สำคัญฉันจะใช้คุณสมบัติโดยตรง แต่ฉันมักจะใช้เมธอด setter เพราะฉันสามารถเพิ่มโค้ดเพื่อเริ่มการทำงานได้อย่างง่ายดายหากต้องการ


7

ถ้าฉันไม่ได้แก้ไขคุณสมบัติที่ฉันจะใช้วิธีการสาธารณะget_property()เว้นแต่จะเป็นโอกาสพิเศษเช่นวัตถุ MySQLi $obj->object_propertyภายในวัตถุอื่นซึ่งในกรณีนี้ผมจะทำให้ประชาชนทรัพย์สินและเรียกมันว่า

ภายในวัตถุจะมีคุณสมบัติ $ this-> สำหรับฉันเสมอ


5

ดูเหมือนว่าด้วยการใช้งานเริ่มต้นของคุณสมบัติ C # 3.0 การตัดสินใจจะดำเนินการสำหรับคุณ คุณต้องตั้งค่าคุณสมบัติโดยใช้ตัวตั้งค่าคุณสมบัติ (อาจเป็นส่วนตัว)

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


5

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

นอกจากนี้โดยส่วนตัวแล้วฉันพบว่าการใช้ getter / setter ทำให้โค้ดอ่านง่ายขึ้นและแก้ไขข้อบกพร่องในภายหลัง


4

ตามที่ระบุไว้ในบางความคิดเห็น: บางครั้งคุณควรบางครั้งคุณไม่ควร ส่วนที่ดีเกี่ยวกับตัวแปรส่วนตัวคือคุณสามารถดูสถานที่ทั้งหมดที่ใช้เมื่อคุณเปลี่ยนแปลงบางสิ่ง หาก getter / setter ของคุณทำบางอย่างที่คุณต้องการให้ใช้มัน ถ้ามันไม่สำคัญว่าคุณจะตัดสินใจ

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

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