เหตุใดจึงต้องใช้ getters และ setters / accessors


1541

อะไรคือข้อดีของการใช้ getters และ setters - ที่รับและตั้งค่า - แทนที่จะใช้เพียงฟิลด์สาธารณะสำหรับตัวแปรเหล่านั้น

หาก getters และ setters ทำมากกว่าแค่การรับ / เซตง่ายๆฉันสามารถหาอันนี้ออกมาได้อย่างรวดเร็ว แต่ฉันไม่ชัดเจน 100% ว่า:

public String foo;

มีอะไรที่แย่ไปกว่า:

private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

ในขณะที่อดีตใช้รหัสน้อยกว่ามาก


6
@Dean J: ทำซ้ำกับคำถามอื่น ๆ : stackoverflow.com/search?q=getters+setters
Asaph

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

26
Google "accessors are evil"
OMG Ponies

34
"Accessors is evil" ถ้าคุณบังเอิญเขียนโค้ดที่ใช้งานได้หรือวัตถุที่ไม่เปลี่ยนรูป หากคุณกำลังเขียนออบเจกต์ที่ไม่แน่นอนที่เปลี่ยนสถานะได้แล้วสิ่งเหล่านี้ก็สำคัญทีเดียว
Christian Hayter

18
บอกไม่ต้องถาม pragprog.com/articles/tell-dont-ask
Dave Jarvis

คำตอบ:


980

มีเหตุผลที่ดีมากมายในการพิจารณาการใช้ accessorsแทนที่จะเปิดเผยฟิลด์ของคลาสโดยตรงไม่ใช่เพียงแค่การโต้แย้งของ encapsulation และทำให้การเปลี่ยนแปลงในอนาคตง่ายขึ้น

นี่คือเหตุผลบางส่วนที่ฉันรู้:

  • การห่อหุ้มพฤติกรรมที่เกี่ยวข้องกับการรับหรือการตั้งค่าคุณสมบัติ - ช่วยให้สามารถเพิ่มฟังก์ชันการทำงานเพิ่มเติม (เช่นการตรวจสอบความถูกต้อง) ได้ง่ายขึ้นในภายหลัง
  • ซ่อนการเป็นตัวแทนภายในของคุณสมบัติในขณะที่เปิดเผยคุณสมบัติโดยใช้การเป็นตัวแทนทางเลือก
  • ป้องกันอินเทอร์เฟซสาธารณะของคุณจากการเปลี่ยนแปลง - ช่วยให้อินเทอร์เฟซสาธารณะคงที่ในขณะที่การเปลี่ยนแปลงการใช้งานโดยไม่มีผลกระทบต่อผู้บริโภคที่มีอยู่
  • การควบคุมอายุการใช้งานและการจัดการหน่วยความจำ (การกำจัด) ความหมายของคุณสมบัติ - สำคัญอย่างยิ่งในสภาพแวดล้อมหน่วยความจำที่ไม่ได้รับการจัดการ (เช่น C ++ หรือ Objective-C)
  • การให้จุดการดักจับการดีบักเมื่อคุณสมบัติเปลี่ยนไปที่ runtime - การดีบักเวลาและสถานที่ที่คุณสมบัติเปลี่ยนเป็นค่าเฉพาะอาจทำได้ค่อนข้างยากหากไม่มีสิ่งนี้ในบางภาษา
  • ปรับปรุงความสามารถในการทำงานร่วมกันกับไลบรารีที่ออกแบบมาเพื่อใช้งานกับคุณสมบัติตัวรับ / setters - การเยาะเย้ยการทำให้เป็นอนุกรมและ WPF เป็นสิ่งสำคัญ
  • การอนุญาตให้ผู้สืบทอดเปลี่ยนความหมายของวิธีการทำงานของคุณสมบัติและเปิดเผยโดยการแทนที่เมธอด getter / setter
  • การอนุญาตให้ getter / setter ถูกส่งผ่านเป็นนิพจน์แลมบ์ดาแทนที่จะเป็นค่า
  • Getters และ setters สามารถอนุญาตให้มีระดับการเข้าถึงที่แตกต่างกันตัวอย่างเช่น get อาจเป็นแบบสาธารณะ แต่ชุดนั้นอาจได้รับการปกป้อง

59
+1 เพียงแค่เพิ่ม: อนุญาตให้โหลดขี้เกียจ อนุญาตให้คัดลอกเมื่อเขียน
NewbiZ

6
@bjarkef: ฉันเชื่อว่าpublicวิธีการเข้าถึงทำให้เกิดการทำซ้ำรหัสและเปิดเผยข้อมูล accessors สาธารณะไม่จำเป็นสำหรับ marshalling (การทำให้เป็นอนุกรม) ในบางภาษา (Java) การpublicรับ accessors จะไม่สามารถเปลี่ยนชนิดการส่งคืนได้โดยไม่ทำลายคลาสที่ขึ้นต่อกัน ยิ่งกว่านั้นฉันเชื่อว่าพวกเขาใช้หลักการของ OO ดูเพิ่มเติมที่: stackoverflow.com/a/1462424/59087
Dave Jarvis

15
@sbi: สิ่งหนึ่งที่ซื้อโดยใช้ setter คุณสมบัติแม้ในกรอบการออกแบบที่ดีคือความสามารถในการมีวัตถุแจ้งให้ทราบเมื่อมีการเปลี่ยนแปลงคุณสมบัติ
supercat

22
@supercat: แต่โดยทั่วไปฉันไม่ได้สนับสนุนข้อมูลสาธารณะ การแจ้งเตือนของวัตถุอื่นสามารถทำได้เช่นกันจากวิธีการจริงที่ให้นามธรรมอย่างมีนัยสำคัญบนที่เก็บข้อมูลเพียงกับเขตข้อมูลสาธารณะ (มากหรือน้อย) เขตข้อมูลสาธารณะ แทนที่จะฉันต้องการจะบอกplane.turnTo(dir); plane.setSpeed(spd); plane.setTargetAltitude(alt); plane.getBreaks().release(); plane.takeOff(alt)เขตข้อมูลด้านในใดที่จำเป็นต้องเปลี่ยนเพื่อให้ระนาบเข้าสู่takingOffโหมดนั้นไม่มีความกังวลของฉัน และวัตถุอื่น ๆ ( breaks) วิธีการแจ้งเตือนฉันไม่ต้องการที่จะรู้เช่นกัน
sbi

8
@BenLee: ฉันไม่รู้จะพูดยังไงดี "Accessors" เป็นเพียงคำพ้องสำหรับ "getters / setters" และในสองความคิดเห็นแรกของฉันฉันอธิบายว่าทำไมสิ่งเหล่านั้นจึงเป็นการละเมิดคำว่า "OOP" หากคุณต้องการเข้าถึงและจัดการกับรัฐโดยตรงมันไม่ใช่วัตถุในความหมายของ OOP
sbi

480

เพราะ 2 สัปดาห์ที่ผ่านมา (เดือนปี) จากนี้เมื่อคุณตระหนักว่าความต้องการหมาของคุณที่จะทำมากขึ้นกว่าเพียงแค่การตั้งค่าที่คุณจะรู้ว่าทรัพย์สินที่ถูกนำมาใช้โดยตรงใน 238 ชั้นเรียนอื่น ๆ :-)


84
ฉันกำลังนั่งและจ้องมองที่แอพไลน์ 500k ซึ่งไม่จำเป็น ที่กล่าวว่าหากจำเป็นครั้งเดียวมันจะเริ่มก่อให้เกิดฝันร้ายบำรุงรักษา ดีพอสำหรับทำเครื่องหมายสำหรับฉัน
Dean J

20
ฉันทำได้แค่อิจฉาคุณและแอปของคุณ :-) ที่บอกว่ามันขึ้นอยู่กับกองซอฟต์แวร์ของคุณด้วย ตัวอย่างเช่น Delphi (และ C # - ฉันคิดว่า?) ช่วยให้คุณสามารถกำหนดคุณสมบัติเป็นพลเมืองชั้น 1 ที่พวกเขาสามารถอ่าน / เขียนฟิลด์โดยตรงในขั้นต้น แต่ - คุณควรต้องการมันหรือไม่โดยใช้วิธี getter / setter Mucho สะดวกสบาย Java, อนิจจาไม่ - ไม่พูดถึงมาตรฐาน javabeans ซึ่งบังคับให้คุณใช้ getters / setters เช่นกัน
ChssPly76

14
แม้ว่านี่จะเป็นเหตุผลที่ดีสำหรับการใช้ accessors แต่ในขณะนี้สภาพแวดล้อมการเขียนโปรแกรมและผู้แก้ไขหลายคนให้การสนับสนุนการปรับโครงสร้างใหม่ (ทั้งใน IDE หรือเป็น Add-in ฟรี) ซึ่งค่อนข้างจะลดผลกระทบของปัญหา
LBushkin

62
เสียงเหมือนการเพิ่มประสิทธิภาพก่อนวัยผู้ใหญ่
cmcginty

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

357

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

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

ดังนั้นตอนนี้ที่คุณทำสัญญาการเปลี่ยนแปลงทุกไฟล์ใน codebase เป็นสิ่งที่คุณควรทำอย่าหลีกเลี่ยง หากคุณหลีกเลี่ยงมันคุณกำลังทำการสันนิษฐานว่ารหัสทั้งหมดถือว่าสัญญาสำหรับวิธีการเหล่านั้นแตกต่างกัน

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

อาร์กิวเมนต์เดียวกันนี้ใช้กับข้อดีอื่น ๆ ที่ควรได้รับของคู่ pass-through getter / setter: หากคุณตัดสินใจเปลี่ยนค่าที่ตั้งไว้ในภายหลังคุณจะผิดสัญญา หากคุณแทนที่ฟังก์ชันการทำงานเริ่มต้นในคลาสที่ได้รับในวิธีที่นอกเหนือจากการปรับเปลี่ยนที่ไม่เป็นอันตรายสองสามอย่าง (เช่นการบันทึกหรือพฤติกรรมที่ไม่สามารถสังเกตได้อื่น ๆ ) แสดงว่าคุณละเมิดสัญญาของคลาสพื้นฐาน นั่นเป็นการละเมิดหลักการทดแทนของ Liskov ซึ่งถูกมองว่าเป็นหนึ่งในหลักคำสอนของ OO

ถ้าชั้นมี getters ใบ้เหล่านี้และ setters ทุกสนามแล้วมันเป็นระดับที่มีค่าคงที่ใด ๆ ที่ไม่มีสัญญา นั่นเป็นการออกแบบเชิงวัตถุจริงๆเหรอ? ถ้าคลาสทั้งหมดมี getters และ setters เหล่านั้นมันเป็นเพียง data dumb และผู้ถือ data โง่ควรมีลักษณะเหมือน data dumb:

class Foo {
public:
    int DaysLeft;
    int ContestantNumber;
};

การเพิ่มคู่ pass-through getter / setter ให้กับคลาสดังกล่าวจะไม่เพิ่มค่าใด ๆ คลาสอื่น ๆ ควรจัดเตรียมการดำเนินการที่มีความหมายไม่ใช่เฉพาะการดำเนินการที่มีอยู่แล้วในฟิลด์ นั่นคือวิธีที่คุณสามารถกำหนดและรักษาค่าคงที่ที่มีประโยชน์

ลูกค้า : "ฉันจะทำอะไรกับวัตถุของคลาสนี้"
ผู้ออกแบบ : "คุณสามารถอ่านและเขียนตัวแปรได้หลายตัว"
ลูกค้า : "โอ้ ... เท่ห์ฉันเดาเหรอ?"

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


12
คำตอบที่ดี (+1) คำวิจารณ์เดียวของฉันคือมันทำให้ฉันหลายคนอ่านเพื่อหาว่า "การตรวจสอบ" ในวรรคสุดท้ายแตกต่างจาก "การตรวจสอบ" ในไม่กี่ครั้งแรก (ซึ่งคุณโยนออกมาในกรณีหลัง แต่เลื่อนในอดีต); การปรับถ้อยคำอาจช่วยได้ในเรื่องนั้น
การแข่งขัน Lightness ใน Orbit

14
นี่เป็นคำตอบที่ดี แต่ยุคปัจจุบันลืมไปแล้วว่า "การซ่อนข้อมูล" คืออะไรหรือเพื่ออะไร พวกเขาไม่เคยอ่านเกี่ยวกับความไม่สามารถเปลี่ยนแปลงได้และในการแสวงหาความคล่องแคล่วที่สุดพวกเขาไม่เคยวาดแผนภาพการเปลี่ยนสถานะที่กำหนดว่าสถานะทางกฎหมายของวัตถุนั้นเป็นอย่างไร
ดาร์เรล Teague

2
ข้อสังเกตสำคัญคือผู้ได้รับประโยชน์มากกว่า setters มาก ผู้ทะเยอทะยานสามารถคืนค่าที่คำนวณได้หรือค่าที่คำนวณไว้ในแคช ผู้ตั้งค่าทุกคนสามารถทำได้คือการตรวจสอบความถูกต้องและเปลี่ยนprivateฟิลด์ ถ้าคลาสไม่มี setters มันง่ายที่จะทำให้ไม่เปลี่ยนรูป
Raedwald

7
เอ้อฉันเดาว่าคุณไม่รู้ว่าคลาสคงที่คืออะไร ถ้ามันเป็นโครงสร้างที่ไม่สำคัญมันก็หมายความว่ามันมีค่าคงที่ที่จะรักษาและคนหนึ่งก็ไม่สามารถออกแบบได้โดยไม่ต้องอยู่ในใจ "มีข้อผิดพลาดสมาชิกรายหนึ่งอัปเดตผิด" นอกจากนี้ยังมีการอ่านเช่น "การอัปเดตสมาชิกเป็นการละเมิดคลาสที่ไม่เปลี่ยนแปลง" คลาสที่ออกแบบมาอย่างดีไม่อนุญาตให้รหัสลูกค้าละเมิดค่าคงที่
R. Martinho Fernandes

5
+1 สำหรับ 'ผู้ถือข้อมูลโง่ควรมีลักษณะเป็นผู้ถือข้อมูลโง่' โชคไม่ดีที่ทุกคนดูเหมือนจะออกแบบทุกอย่างเกี่ยวกับผู้ถือข้อมูลที่โง่เง่าในวันนี้และกรอบงานจำนวนมากจำเป็นต้องใช้ ...
Joeri Hendrickx

92

ผู้คนมากมายพูดคุยเกี่ยวกับข้อดีของผู้ได้รับและผู้ตั้งค่า แต่ฉันต้องการเล่นผู้สนับสนุนของปีศาจ ตอนนี้ฉันกำลังดีบักโปรแกรมที่มีขนาดใหญ่มากซึ่งโปรแกรมเมอร์ตัดสินใจที่จะทำทุกสิ่งให้กับผู้ตั้งค่าและผู้ตั้งค่า นั่นอาจดูดี แต่มันเป็นฝันร้ายทางวิศวกรรมย้อนกลับ

สมมติว่าคุณกำลังดูโค้ดหลายร้อยบรรทัดและคุณพบสิ่งนี้:

person.name = "Joe";

มันเป็นโค้ดที่เรียบง่าย แต่สวยงามจนกระทั่งคุณรู้ตัวมัน ตอนนี้คุณทำตาม setter นั้นและพบว่ามันยังตั้ง person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName และเรียก person.update () ซึ่งจะส่งเคียวรีไปยังฐานข้อมูล ฯลฯ โอ้นั่นคือ ตำแหน่งที่หน่วยความจำของคุณรั่วไหล

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


8
ใช่. ในขณะนี้การปรับเปลี่ยนรหัสฐานข้อมูลขนาดใหญ่อีกครั้งและนี่เป็นฝันร้าย getters และ setters ทำหน้าที่มากเกินไปรวมถึงการเรียก getters และ setters ที่ยุ่งมาก ๆ ซึ่งจะช่วยลดความสามารถในการอ่านให้เหลือน้อยที่สุด การตรวจสอบความถูกต้องเป็นเหตุผลที่ยอดเยี่ยมสำหรับการใช้ accessors แต่การใช้พวกเขาเพื่อทำมากกว่านั้นดูเหมือนว่าจะลบผลประโยชน์ที่อาจเกิดขึ้น
Fadecomic

31
นี่คือการโต้เถียงกับน้ำตาล syntactic ไม่ใช่กับ setters โดยทั่วไป
Phil

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

"มันเป็นโค้ดเพียงชิ้นเดียวที่สวยงามจนกระทั่งคุณรู้ตัวว่าเป็น setter" - เดี๋ยวก่อนโพสต์ดั้งเดิมเกี่ยวกับ Java หรือ C # ใช่ไหม? :)
Honza Zidek

ฉันเห็นด้วยอย่างสมบูรณ์เกี่ยวกับความสามารถในการอ่าน มันชัดเจนโดยสิ้นเชิงว่าเกิดอะไรขึ้นเมื่อperson.name = "Joe";ถูกเรียก ไม่มี cruft ในทางของข้อมูลการเน้นไวยากรณ์แสดงให้เห็นว่ามันเป็นเขตข้อมูลและ refactoring นั้นง่ายกว่า (ไม่จำเป็นต้อง refactor สองวิธีและเขตข้อมูล) ประการที่สองวงจรสมองที่สูญเปล่าทั้งหมดคิดว่า "getIsValid ()" หรือควรเป็น "isValid ()" เป็นต้นและสามารถลืมได้ ฉันไม่สามารถคิดถึงครั้งเดียวที่ฉันได้รับการบันทึกจากข้อผิดพลาดโดย getter / setter
จะ

53

ในความบริสุทธิ์เชิงวัตถุ getters โลกและ setters เป็นที่น่ากลัวต่อต้านรูปแบบ อ่านบทความนี้: Getters / Setters ชั่วร้าย ระยะเวลาระยะเวลาสรุปพวกเขาสนับสนุนให้โปรแกรมเมอร์คิดเกี่ยวกับวัตถุที่เป็นโครงสร้างข้อมูลและการคิดประเภทนี้เป็นขั้นตอนที่บริสุทธิ์ (เช่นใน COBOL หรือ C) ในภาษาเชิงวัตถุไม่มีโครงสร้างข้อมูล แต่มีเพียงวัตถุที่แสดงพฤติกรรม (ไม่ใช่คุณลักษณะ / คุณสมบัติ!)

คุณสามารถหาข้อมูลเพิ่มเติมเกี่ยวกับพวกเขาได้ในหัวข้อ 3.5 ของElegant Objects (หนังสือของฉันเกี่ยวกับการเขียนโปรแกรมเชิงวัตถุ)


7
Getters และ setters แนะนำโมเดลโดเมนโลหิตจาง
Raedwald

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

3
ฉันขอแนะนำว่าโปรแกรมที่มีประโยชน์ส่วนใหญ่ไม่จำเป็นต้องจำลอง / จำลองวัตถุในโลกแห่งความจริง IMO นี้ไม่ได้เกี่ยวกับการเขียนโปรแกรมภาษาเลย มันเกี่ยวกับสิ่งที่เราเขียนโปรแกรม
Stephen C

3
โลกแห่งความเป็นจริงหรือไม่ yegor ถูกต้องสมบูรณ์ หากสิ่งที่คุณมีคือ "โครงสร้าง" อย่างแท้จริงและคุณไม่จำเป็นต้องเขียนโค้ดใด ๆ ที่อ้างอิงมันด้วยชื่อให้ใส่มันใน hashtable หรือโครงสร้างข้อมูลอื่น ๆ หากคุณจำเป็นต้องเขียนโค้ดให้ใส่มันเป็นสมาชิกของคลาสและวางโค้ดที่จัดการกับตัวแปรนั้นในคลาสเดียวกันและละเว้น setter & getter PS แม้ว่าฉันจะแบ่งปันมุมมองของ yegor เป็นส่วนใหญ่ แต่ฉันก็เชื่อว่าถั่วที่ใส่หมายเหตุประกอบไว้โดยไม่มีรหัสนั้นเป็นโครงสร้างข้อมูลที่มีประโยชน์ - บางครั้ง getters ก็จำเป็นด้วยเช่นกัน setters ไม่ควรมีอยู่จริง
Bill K

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

52

มีหลายเหตุผล สิ่งที่ฉันชอบคือเมื่อคุณต้องการเปลี่ยนพฤติกรรมหรือควบคุมสิ่งที่คุณสามารถตั้งค่าบนตัวแปร ตัวอย่างเช่นสมมติว่าคุณมีเมธอด setSpeed ​​(int speed) แต่คุณต้องการให้คุณสามารถตั้งค่าความเร็วสูงสุดเพียง 100 คุณจะทำสิ่งที่ชอบ:

public void setSpeed(int speed) {
  if ( speed > 100 ) {
    this.speed = 100;
  } else {
    this.speed = speed;
  }
}

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

2 เซนต์ของฉัน :)


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

12
แน่นอนว่าเป็นเรื่องจริง แต่ทำไมทำให้ยากกว่าที่คิดไว้ วิธีการรับ / ชุดยังคงเป็นคำตอบที่ดีกว่า
Hardryv

28
@Nathan: การค้นหาประเพณีอื่น ๆ ไม่ใช่ปัญหา เปลี่ยนพวกเขาทั้งหมดคือ
แกรมเพอร์โรว์

22
@GraemePerrow ต้องเปลี่ยนพวกเขาทั้งหมดเป็นข้อได้เปรียบที่ไม่เป็นปัญหา :( เกิดอะไรขึ้นถ้าคุณมีรหัสที่ความเร็วสันนิษฐานว่าอาจจะสูงกว่า 100 ((เพราะคุณรู้ว่าก่อนที่คุณจะยากจนสัญญาก็ทำได้!) while(speed < 200) { do_something(); accelerate(); })
อาร์ Martinho Fernandes

29
มันเป็นตัวอย่างที่แย่มาก! ใครบางคนควรโทร: myCar.setSpeed(157);และหลังจากไม่กี่บรรทัดspeed = myCar.getSpeed();และตอนนี้ ... ฉันขอให้คุณมีความสุขในการแก้จุดบกพร่องในขณะที่พยายามที่จะเข้าใจว่าทำไมspeed==100เมื่อมันควรจะเป็น157
Piotr Aleksander Chmielowski

38

ข้อดีอย่างหนึ่งของ accessors และ mutators ก็คือคุณสามารถทำการตรวจสอบได้

ตัวอย่างเช่นถ้าfooเป็นแบบสาธารณะฉันสามารถตั้งค่าได้อย่างง่ายดายnullแล้วคนอื่นสามารถลองเรียกวิธีการบนวัตถุ แต่มันไม่มีอีกแล้ว! ด้วยsetFooวิธีการที่ฉันจะให้แน่ใจว่าไม่เคยถูกตั้งค่าให้foonull

Accessors และ mutators อนุญาตให้มีการห่อหุ้ม - หากคุณไม่ควรเห็นค่าเมื่อตั้งค่า (อาจตั้งค่าไว้ในตัวสร้างแล้วใช้วิธีการ แต่ไม่ควรเปลี่ยน) จะไม่มีใครเห็นใครเลย แต่ถ้าคุณสามารถอนุญาตให้คลาสอื่นมองเห็นหรือเปลี่ยนแปลงคุณสามารถจัดเตรียม accessor และ / หรือ mutator ที่เหมาะสม


28

ขึ้นอยู่กับภาษาของคุณ คุณติดแท็ก "เชิงวัตถุ" แทนที่จะเป็น "Java" ดังนั้นฉันขอชี้ให้เห็นว่าคำตอบของ ChssPly76 นั้นขึ้นอยู่กับภาษา ตัวอย่างเช่นใน Python ไม่มีเหตุผลที่จะใช้ getters และ setters หากคุณต้องการเปลี่ยนลักษณะการทำงานคุณสามารถใช้คุณสมบัติซึ่งจะทำให้ผู้ใช้ทะลุผ่านและเข้าสู่รอบการเข้าถึงคุณสมบัติพื้นฐาน บางสิ่งเช่นนี้

class Simple(object):
   def _get_value(self):
       return self._value -1

   def _set_value(self, new_value):
       self._value = new_value + 1

   def _del_value(self):
       self.old_values.append(self._value)
       del self._value

   value = property(_get_value, _set_value, _del_value)

2
ใช่ฉันได้พูดมากในความคิดเห็นด้านล่างคำตอบของฉัน Java ไม่ใช่ภาษาเดียวที่ใช้ getters / setters เนื่องจาก crutch เช่นเดียวกับ Python ไม่ใช่ภาษาเดียวที่สามารถกำหนดคุณสมบัติได้ อย่างไรก็ตามประเด็นหลักยังคงอยู่ - "คุณสมบัติ" ไม่ใช่ "เขตข้อมูลสาธารณะ" ที่เหมือนกัน
ChssPly76

1
@jcd - ไม่เลย คุณกำลังกำหนด "อินเทอร์เฟซ" (API สาธารณะจะเป็นคำที่ดีกว่าที่นี่) โดยการเปิดเผยฟิลด์สาธารณะของคุณ เมื่อเสร็จแล้วจะไม่มีการย้อนกลับ คุณสมบัติไม่ใช่เขตข้อมูลเนื่องจากมีกลไกในการดักจับความพยายามในการเข้าถึงเขตข้อมูล (โดยกำหนดเส้นทางให้กับวิธีการหากกำหนดไว้) นั่นคืออย่างไรก็ตามไม่มีอะไรมากไปกว่าไวยากรณ์น้ำตาลเหนือเมธอด getter / setter มันสะดวกมาก แต่มันไม่ได้เปลี่ยนกระบวนทัศน์พื้นฐาน - การเปิดเผยฟิลด์โดยไม่มีการควบคุมการเข้าถึงพวกมันละเมิดหลักการของการห่อหุ้ม
ChssPly76

14
@ ChssPly76— ฉันไม่เห็นด้วย ฉันมีการควบคุมมากพอ ๆ กับราวกับว่าพวกเขาเป็นคุณสมบัติเพราะฉันสามารถทำให้พวกเขาคุณสมบัติเมื่อใดก็ตามที่ฉันต้องการ ไม่มีความแตกต่างระหว่างคุณสมบัติที่ใช้ boilerplate getters และ setters และแอ็ตทริบิวต์ raw ยกเว้นว่าแอ็ตทริบิวต์ raw นั้นเร็วกว่าเพราะใช้ภาษาพื้นฐานมากกว่าวิธีการโทร หน้าที่พวกเขาเหมือนกัน วิธีเดียวที่สามารถละเมิด encapsulation ได้คือถ้าคุณคิดว่าเครื่องหมายวงเล็บ ( obj.set_attr('foo')) นั้นเหนือกว่าเครื่องหมายเท่ากับ ( obj.attr = 'foo') การเข้าถึงสาธารณะเป็นการเข้าถึงสาธารณะ
jcdyer

1
@ jcdyer สามารถควบคุมได้มาก แต่ก็ไม่สามารถอ่านได้มากนักคนอื่น ๆ มักคิดผิดว่าobj.attr = 'foo'เพิ่งตั้งค่าตัวแปรโดยไม่เกิดอะไรขึ้นอีก
ติโม Huovinen

9
@TimoHuovinen แตกต่างจากผู้ใช้ใน Java อย่างไรโดยสมมติว่าobj.setAttr('foo')"เพียงแค่ตั้งค่าตัวแปรโดยไม่มีสิ่งใดเกิดขึ้น" หากเป็นวิธีสาธารณะก็เป็นวิธีสาธารณะ ถ้าคุณใช้มันเพื่อให้บรรลุบางผลข้างเคียงและมันเป็นที่สาธารณะแล้วคุณได้ดีกว่าสามารถที่จะนับในการทำงานทุกอย่างเช่นถ้าเดียวที่มีผลข้างเคียงตั้งใจที่เกิดขึ้น (กับทุกรายละเอียดอื่น ๆ การดำเนินงานและผลข้างเคียงอื่น ๆ การใช้ทรัพยากรอะไรก็ตาม ซ่อนจากความกังวลของผู้ใช้) นี่ไม่ต่างจาก Python อย่างแน่นอน ไวยากรณ์ของ Python เพื่อให้ได้เอฟเฟกต์นั้นง่ายขึ้น
ely

25

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


24

ขอบคุณที่ทำให้ความคิดของฉันชัดเจน ตอนนี้ที่นี่ (เกือบ) 10 (เกือบ) เหตุผลที่ดีที่จะไม่ใช้ getters และ setters:

  1. เมื่อคุณรู้ว่าคุณต้องทำมากกว่าเพียงแค่ตั้งค่าและรับค่าคุณสามารถทำให้ฟิลด์เป็นส่วนตัวซึ่งจะบอกคุณทันทีว่าคุณเข้าถึงจากที่ใด
  2. การตรวจสอบความถูกต้องใด ๆ ที่คุณดำเนินการจะต้องปราศจากบริบทซึ่งการตรวจสอบความถูกต้องนั้นแทบจะไม่ได้ใช้งานจริง
  3. คุณสามารถเปลี่ยนค่าที่ตั้งไว้ได้ - นี่คือฝันร้ายแน่นอนเมื่อผู้โทรส่งค่าที่พวกเขา [สยองขวัญสยองขวัญ] ต้องการให้คุณจัดเก็บตามที่เป็นอยู่
  4. คุณสามารถซ่อนการเป็นตัวแทนภายในได้อย่างน่าอัศจรรย์ดังนั้นคุณแน่ใจได้เลยว่าการดำเนินการทั้งหมดเหล่านี้มีความสมมาตรใช่ไหม
  5. คุณได้หุ้มอินเทอร์เฟซสาธารณะของคุณจากการเปลี่ยนแปลงภายใต้แผ่นงาน - หากคุณออกแบบอินเทอร์เฟซและไม่แน่ใจว่าการเข้าถึงโดยตรงกับบางสิ่งบางอย่างนั้นใช้ได้หรือไม่คุณควรออกแบบต่อไป
  6. ห้องสมุดบางแห่งคาดหวังสิ่งนี้ แต่ไม่มาก - การสะท้อน, การทำให้เป็นอันดับ, วัตถุจำลองจะทำงานได้ดีกับเขตข้อมูลสาธารณะ
  7. การสืบทอดคลาสนี้คุณสามารถแทนที่ฟังก์ชันการทำงานเริ่มต้น - ในคำอื่น ๆ ที่คุณสามารถสร้างความสับสนให้กับผู้โทรโดยไม่เพียง แต่ซ่อนการใช้งาน แต่ยังทำให้มันไม่สอดคล้องกัน

สามครั้งสุดท้ายที่ฉันเพิ่งจากไป (N / A หรือ D / C) ...


11
ฉันคิดว่าข้อโต้แย้งที่สำคัญคือ"ถ้าคุณออกแบบอินเตอร์เฟซและไม่แน่ใจว่าการเข้าถึงโดยตรงกับบางสิ่งบางอย่างนั้นดีหรือไม่คุณควรออกแบบต่อไป" นั่นเป็นปัญหาที่สำคัญที่สุดกับผู้ได้รับ / setters: พวกเขาลดคลาสให้เหลือเพียงตู้คอนเทนเนอร์ของฟิลด์สาธารณะ (มากหรือน้อย) อย่างไรก็ตามในOOP จริงวัตถุนั้นเป็นมากกว่าที่เก็บของเขตข้อมูล มันสรุปสถานะและอัลกอริทึมเพื่อจัดการสถานะนั้น สิ่งที่สำคัญเกี่ยวกับคำแถลงนี้ก็คือรัฐควรจะถูกห่อหุ้มและถูกจัดการโดยอัลกอริทึมที่จัดทำโดยวัตถุเท่านั้น
sbi

22

ฉันรู้ว่ามันสายไปหน่อย แต่ฉันคิดว่ามีบางคนที่สนใจในการแสดง

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

Time 1: 953ms, Time 2: 741ms
Time 1: 655ms, Time 2: 743ms
Time 1: 656ms, Time 2: 634ms
Time 1: 637ms, Time 2: 629ms
Time 1: 633ms, Time 2: 625ms

(เวลา 1 คือทางตรงเวลา 2 คือทะเยอทะยาน)

คุณเห็นไหมว่าคนทะเลาะกัน (เกือบ) เร็วขึ้นนิดหน่อย จากนั้นฉันลองด้วยจำนวนรอบที่แตกต่างกัน แทนที่จะใช้ 1 ล้านฉันใช้ 10 ล้านและ 0.1 ล้าน ผลลัพธ์ที่ได้:

10 ล้านรอบ:

Time 1: 6382ms, Time 2: 6351ms
Time 1: 6363ms, Time 2: 6351ms
Time 1: 6350ms, Time 2: 6363ms
Time 1: 6353ms, Time 2: 6357ms
Time 1: 6348ms, Time 2: 6354ms

ด้วย 10 ล้านรอบเวลาจะใกล้เคียงกัน นี่คือ 100,000 รอบ (0.1 ล้าน) รอบ:

Time 1: 77ms, Time 2: 73ms
Time 1: 94ms, Time 2: 65ms
Time 1: 67ms, Time 2: 63ms
Time 1: 65ms, Time 2: 65ms
Time 1: 66ms, Time 2: 63ms

ด้วยจำนวนรอบที่แตกต่างกันผู้ทะเยอทะยานก็เร็วกว่าวิธีปกติเล็กน้อย ฉันหวังว่านี่จะช่วยคุณได้


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

16

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

คิดง่ายง่ายเพิ่มความซับซ้อนเมื่อจำเป็น

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

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


16

เราใช้ getters และ setters:

  • เพื่อนำมาใช้ใหม่
  • เพื่อตรวจสอบความถูกต้องในขั้นตอนการเขียนโปรแกรมในภายหลัง

วิธี Getter และ setter เป็นส่วนต่อประสานสาธารณะในการเข้าถึงสมาชิกคลาสส่วนตัว


มนต์ห่อหุ้ม

มนต์ encapsulation คือการทำให้เขตข้อมูลส่วนตัวและวิธีการสาธารณะ

วิธีการทะเยอทะยาน: เราสามารถเข้าถึงตัวแปรส่วนตัว

วิธีการตั้งค่า: เราสามารถปรับเปลี่ยนเขตข้อมูลส่วนตัว

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

  • ดีกว่า;
  • ปลอดภัยมากขึ้น; และ
  • ได้เร็วขึ้น

สามารถใช้ค่าได้ทุกที่วิธีการคืนค่านั้นสามารถเพิ่มได้ แทน:

int x = 1000 - 500

ใช้

int x = 1000 - class_name.getValue();

ในแง่ของคนธรรมดา

เป็นตัวแทนของชั้นเรียน "บุคคล"

Personสมมติว่าเราจำเป็นต้องเก็บรายละเอียดของเรื่องนี้ นี้Personมีเขตข้อมูลname, และage sexการทำเช่นนี้เกี่ยวข้องกับการสร้างวิธีการname, และage sexตอนนี้ถ้าเราจำเป็นต้องสร้างคนอื่นมันจะกลายเป็นสิ่งจำเป็นในการสร้างวิธีการในการname, age,sexทั้งหมดอีกครั้ง

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


15

ฉันใช้เวลาสักครู่หนึ่งคิดเรื่องนี้กับเคส Java และฉันเชื่อว่าเหตุผลที่แท้จริงคือ:

  1. รหัสไปยังอินเตอร์เฟสไม่ใช่การใช้งาน
  2. อินเตอร์เฟสจะระบุเมธอดเท่านั้นไม่ใช่ฟิลด์

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

วิธีการเหล่านั้นเป็นทะเยอทะยานทะลึ่งและหมา ....


1
ตกลงคำถามที่สอง; ในกรณีที่เป็นโครงการที่คุณไม่ได้ส่งออกแหล่งข้อมูลไปยังใครและคุณสามารถควบคุมแหล่งข้อมูลได้อย่างเต็มรูปแบบ ... คุณได้รับอะไรกับ getters และ setters หรือไม่
Dean J

2
ในโครงการ Java ใด ๆ ที่ไม่สำคัญคุณต้องใช้รหัสในการเชื่อมต่อเพื่อทำให้สิ่งต่าง ๆ สามารถจัดการและทดสอบได้ (คิดว่า mockups และพร็อกซีออบเจ็กต์) หากคุณใช้อินเทอร์เฟซคุณต้องได้รับและผู้ตั้งค่า
Thorbjørn Ravn Andersen

15

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

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

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

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

    protected YourType _yourName = null;
    public YourType YourName{
      get
      {
        if (_yourName == null)
        {
          _yourName = new YourType();
          return _yourName;
        }
      }
    }

ดังนั้นผู้ทะเยอทะยานจึงเรียกผู้ตั้ง?
icc97

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


13

ด้านหนึ่งที่ฉันพลาดในคำตอบจนถึงสเปคการเข้าถึง:

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

13

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

สองหลักที่สำคัญคือความแตกต่างและการตรวจสอบ แม้ว่ามันจะเป็นเพียงโครงสร้างข้อมูลที่โง่

สมมติว่าเรามีคลาสง่าย ๆ นี้:

public class Bottle {
  public int amountOfWaterMl;
  public int capacityMl;
}

คลาสที่ง่ายมากที่เก็บของเหลวไว้ในนั้นและความสามารถของมันคืออะไร (เป็นมิลลิลิตร)

จะเกิดอะไรขึ้นเมื่อฉัน:

Bottle bot = new Bottle();
bot.amountOfWaterMl = 1500;
bot.capacityMl = 1000;

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

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

เราจะได้สิ่งที่ชอบ:

public interface LiquidContainer {
  public int getAmountMl();
  public void setAmountMl(int amountMl);
  public int getCapacityMl();
}

ที่ดี! และตอนนี้เราเพียงเปลี่ยนขวดเป็น:

public class Bottle extends LiquidContainer {
  private int capacityMl;
  private int amountFilledMl;

  public Bottle(int capacityMl, int amountFilledMl) {
    this.capacityMl = capacityMl;
    this.amountFilledMl = amountFilledMl;
    checkNotOverFlow();
  }

  public int getAmountMl() {
    return amountFilledMl;
  }

  public void setAmountMl(int amountMl) {
     this.amountFilled = amountMl;
     checkNotOverFlow();
  }
  public int getCapacityMl() {
    return capacityMl;
  }

  private void checkNotOverFlow() {
    if(amountOfWaterMl > capacityMl) {
      throw new BottleOverflowException();
    }
}

ฉันจะทิ้งคำจำกัดความของ BottleOverflowException ไว้เป็นแบบฝึกหัดให้กับผู้อ่าน

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

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

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

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

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


2
ขอบคุณสำหรับคำอธิบายครึ่งแรกของอินเทอร์เฟซที่ฉันได้อ่านทำการอ้างอิงใด ๆ ถึง "รีโมทคอนโทรล" หรือ "รถยนต์"
djvs

1
"คุณสามารถมีขวดที่เขียนสถานะลงดิสก์เมื่อมีการเปลี่ยนแปลงหรือขวดที่บันทึกในฐานข้อมูล SQL" ฉัน lolled อย่างหนัก xD แต่ยังไงก็ตามขอแนะนำให้นำเสนอ!
Xerus

10

ในภาษาที่ไม่รองรับ "คุณสมบัติ" (C ++, Java) หรือต้องการการคอมไพล์ใหม่ของไคลเอนต์เมื่อเปลี่ยนฟิลด์เป็นคุณสมบัติ (C #) การใช้วิธี get / set จะง่ายต่อการปรับเปลี่ยน ตัวอย่างเช่นการเพิ่มตรรกะการตรวจสอบให้กับเมธอด setFoo จะไม่ต้องการการเปลี่ยนอินเตอร์เฟสสาธารณะของคลาส

ในภาษาที่รองรับคุณสมบัติ "ของจริง" (Python, Ruby, อาจเป็น Smalltalk?) ไม่มีวิธีรับ / ตั้งค่า


1
ตอบ: C # หากคุณเพิ่มฟังก์ชันการทำงานให้กับการรับ / การตั้งค่านั้นจะไม่ต้องมีการรวบรวมใหม่หรือไม่?
steamer25

@ steamer25: ขออภัยพิมพ์ผิด ฉันหมายความว่าลูกค้าของชั้นจะต้องได้รับการคอมไพล์ใหม่
John Millikin

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

@ R.MartinhoFernandes วิธีแก้ไขปัญหา "แตก" นี้ได้อย่างไร คอมไพเลอร์ไม่สามารถบอกได้ว่ามันพังหรือไม่ นี่เป็นเพียงข้อกังวลเมื่อคุณกำลังเขียนไลบรารีสำหรับผู้อื่น แต่คุณทำให้มันเป็น zOMG สากลที่นี่เป็นมังกร!
Phil

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

6

หนึ่งในหลักการพื้นฐานของการออกแบบ OO: Encapsulation!

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


23
ตัวรับ encapsulation และตัวเซ็ตข้อเสนอนั้นบางอย่างน่าหัวเราะ ดูที่นี่
sbi

หากส่วนต่อประสานสาธารณะของคุณระบุว่า 'foo' เป็นประเภท 'T' และสามารถตั้งค่าเป็นอะไรก็ได้คุณจะไม่สามารถเปลี่ยนแปลงสิ่งนั้นได้ คุณไม่สามารถตัดสินใจได้ว่าจะพิมพ์เป็น 'Y' หรือไม่สามารถกำหนดกฎเช่นข้อ จำกัด ด้านขนาดได้ ดังนั้นหากคุณมี public / set ที่ไม่ทำอะไรมาก / set คุณจะไม่ได้อะไรที่ public field จะไม่เสนอและทำให้ยุ่งยากในการใช้มากขึ้น หากคุณมีข้อ จำกัด เช่นวัตถุสามารถตั้งค่าเป็นโมฆะหรือค่าต้องอยู่ในช่วงจากนั้นใช่วิธีการตั้งค่าสาธารณะจะต้อง แต่คุณยังคงแสดงสัญญาจากการตั้งค่านี้ที่สามารถ ' t change
thecoshman

ทำไมสัญญาไม่สามารถเปลี่ยนแปลงได้?
Phil

5
เหตุใดสิ่งที่ควรรวบรวมไว้หากสัญญาเปลี่ยนแปลง?
R. Martinho Fernandes

5

คุณควรใช้ getters และ setters เมื่อ:

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

ดังนั้นนี่เป็นคำถามทั่วไป OO น้อยมาก มันเป็นคำถามเฉพาะภาษาพร้อมคำตอบที่ต่างกันสำหรับภาษาต่าง ๆ (และกรณีการใช้งานที่แตกต่างกัน)


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

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

* แม้แต่คลาสธรรมดาคุณอาจไม่ต้องการอนุญาตให้ตั้งค่าxและyค่า ถ้าเรื่องนี้เป็นจริงชั้นไม่ควรมันมีวิธีการเช่นtranslate, rotateฯลฯ ? ถ้าเป็นเพียงคลาสเพราะภาษาของคุณไม่มีเร็กคอร์ด / structs / / ตั้งชื่อ tuples นี่ไม่ใช่คำถามของ OO ...


แต่ไม่มีใครทำแบบ OO ทั่วไป พวกเขากำลังออกแบบและการนำไปใช้งานในภาษาเฉพาะ และในบางภาษาผู้ได้รับและผู้ตั้งค่าอยู่ไกลจากความไร้ประโยชน์

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

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

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

นอกจากนี้สิ่งสำคัญคือต้องปฏิบัติตามสำนวนภาษา (หรือกรอบ) ที่คุณใช้ หากคุณเขียนโค้ดสไตล์ทับทิมที่สวยงามใน C # นักพัฒนา C # ที่มีประสบการณ์นอกเหนือจากที่คุณกำลังจะมีปัญหาในการอ่านและนั่นก็ไม่ดี ภาษาบางภาษามีวัฒนธรรมที่แข็งแกร่งกว่าการประชุมมากกว่าภาษาอื่น - และอาจไม่ใช่เรื่องบังเอิญที่ Java และ Python ซึ่งอยู่ตรงข้ามกับสเปกตรัมสำหรับวิธีการใช้สำนวนที่เกิดขึ้นมีสองวัฒนธรรมที่แข็งแกร่งที่สุด

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


4

จากมุมมองการออกแบบการวางแนววัตถุทั้งสองทางเลือกสามารถสร้างความเสียหายต่อการบำรุงรักษาโค้ดโดยทำให้การห่อหุ้มคลาสอ่อนแอลง สำหรับการสนทนาคุณสามารถดูบทความที่ยอดเยี่ยมนี้: http://typicalprogrammer.com/?p=23


3

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

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

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

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


3

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

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

int getVar() const { return var ; }

ดังนั้นคุณมี:

doSomething( obj->getVar() ) ;

แทน

doSomething( obj->var ) ;

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

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

1) เริ่มต้นด้วยสมาชิกสาธารณะทั้งหมดสำหรับวัตถุพื้นฐานที่มีข้อมูลและพฤติกรรม นี่คือเหตุผลในรหัส "ตัวอย่าง" C ++ ทั้งหมดของฉันคุณจะสังเกตเห็นฉันใช้structแทนclassทุกที่

2) เมื่อพฤติกรรมภายในของวัตถุสำหรับสมาชิกข้อมูลมีความซับซ้อนมากพอ (ตัวอย่างเช่นมันชอบที่จะรักษาภายในstd::listตามลำดับบางอย่าง) ฟังก์ชั่นประเภท accessor จะถูกเขียน เพราะฉันเขียนโปรแกรมด้วยตัวเองฉันไม่ได้ตั้งสมาชิกprivateทันที แต่ที่ไหนสักแห่งที่วิวัฒนาการของชั้นเรียนสมาชิกจะ "เลื่อน" เป็นอย่างใดอย่างหนึ่งprotectedหรือprivateหรือ

3) คลาสที่เต็มไปด้วยเนื้อความและมีกฎระเบียบที่เข้มงวดเกี่ยวกับ internals ของพวกเขา (เช่นพวกเขารู้ว่าสิ่งที่พวกเขากำลังทำและคุณจะไม่ "fuck" (ศัพท์ทางเทคนิค) กับ internals) จะได้รับการclassแต่งตั้งสมาชิกส่วนตัวเริ่มต้น และอนุญาตให้มีสมาชิกเพียงไม่กี่คนpublicเท่านั้น

ฉันพบว่าวิธีการนี้ช่วยให้ฉันหลีกเลี่ยงการนั่งอยู่ที่นั่นและเขียนทะเยอทะยาน / setters อย่างเคร่งศาสนาเมื่อสมาชิกข้อมูลจำนวนมากถูกย้ายออกย้ายไปรอบ ๆ ฯลฯ ในช่วงแรก ๆ ของการวิวัฒนาการของชั้นเรียน


1
"... อินเทอร์เฟซที่มีการกำหนดอย่างดีซึ่งคุณไม่สามารถใช้สกรูกับ internals ของ" และการตรวจสอบใน setters
Agi Hammerthief

3

มีเหตุผลที่ดีที่ควรพิจารณาใช้ accessors คือไม่มีการสืบทอดคุณสมบัติ ดูตัวอย่างถัดไป:

public class TestPropertyOverride {
    public static class A {
        public int i = 0;

        public void add() {
            i++;
        }

        public int getI() {
            return i;
        }
    }

    public static class B extends A {
        public int i = 2;

        @Override
        public void add() {
            i = i + 2;
        }

        @Override
        public int getI() {
            return i;
        }
    }

    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.i);
        a.add();
        System.out.println(a.i);
        System.out.println(a.getI());
    }
}

เอาท์พุท:

0
0
4

3

Gettersและsettersใช้เพื่อสร้างสองลักษณะพื้นฐานของ Object Oriented Programming ซึ่ง ได้แก่ :

  1. สิ่งที่เป็นนามธรรม
  2. encapsulation

สมมติว่าเรามีระดับพนักงาน:

package com.highmark.productConfig.types;

public class Employee {

    private String firstName;
    private String middleName;
    private String lastName;

    public String getFirstName() {
      return firstName;
    }
    public void setFirstName(String firstName) {
       this.firstName = firstName;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
         this.middleName = middleName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName(){
        return this.getFirstName() + this.getMiddleName() +  this.getLastName();
    }
 }

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


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

1
ประโยชน์ของสิ่งนี้คือคุณสามารถเปลี่ยน internals ของคลาสได้โดยไม่ต้องเปลี่ยนส่วนต่อประสาน พูดแทนสามคุณสมบัติคุณมีหนึ่ง - อาร์เรย์ของสตริง หากคุณใช้ getters และ setters คุณสามารถทำการเปลี่ยนแปลงนั้นแล้วอัพเดต getter / setters เพื่อให้ทราบว่าชื่อ [0] เป็นชื่อแรกชื่อ [1] อยู่ตรงกลาง ฯลฯ แต่ถ้าคุณเพิ่งใช้คุณสมบัติสาธารณะ คุณจะต้องเปลี่ยน Employee ที่เข้าถึงทุกคลาสด้วยเนื่องจากคุณสมบัติ FirstName ที่พวกเขาใช้นั้นไม่มีอยู่อีกต่อไป
Andrew Hows

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

2

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


ฉันไม่เคยคาดหวังว่าผู้ทะเยอทะยานหรือผู้ตั้งตนจะเป็นการดำเนินการที่มีราคาแพง ในกรณีเช่นนี้ให้ใช้โรงงาน: ใช้ setters ทั้งหมดที่คุณต้องการแล้วเรียกใช้แพงexecuteหรือbuildวิธีการ
Hubert Grzeskowiak

2

ข้อดีข้อหนึ่งที่ค่อนข้างทันสมัยของ getters / setters คือทำให้ง่ายต่อการเรียกดูโค้ดในเครื่องมือแก้ไขโค้ดที่ติดแท็ก (ดัชนี) เช่นถ้าคุณต้องการดูว่าใครเป็นสมาชิกคุณสามารถเปิดลำดับชั้นการโทรของ setter ได้

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


คุณสามารถทำ clic ที่ถูกต้อง> ค้นหาการใช้งานของสมาชิกได้เหมือนใน getter / setter
Eildosa

2

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


1

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


2
ฉันคิดว่าการเปลี่ยนพฤติกรรมของผู้ทะเยอทะยานหรือผู้ตั้งค่าไม่ใช่การเปลี่ยนแปลงที่ผิดพลาด </sarcasm>
R. Martinho Fernandes

@ R.MartinhoFernandes ไม่จำเป็นต้องเป็น TBYS
Phil

1

ฉันแค่อยากจะโยนความคิดของคำอธิบายประกอบ: @getter และ @setter ด้วย @getter คุณควรจะสามารถ obj = class.field แต่ไม่ใช่ class.field = obj ด้วย @setter ในทางกลับกัน ด้วย @getter และ @setter คุณควรทำทั้งสองอย่างได้ สิ่งนี้จะรักษา encapsulation และลดเวลาโดยไม่เรียกวิธีการที่ไม่สำคัญที่รันไทม์


1
มันจะถูกนำไปใช้ในขณะทำงานด้วย "เมธอดเล็กน้อย" จริงๆแล้วอาจไม่ใช่เรื่องไร้สาระ
Phil

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