การเปลี่ยนชื่อวิธีรักษาการห่อหุ้มได้หรือไม่?


9

ฉันกำลังอ่านหน้านี้เกี่ยวกับเมื่อ getters / setters เป็นธรรมและ OP ให้ตัวอย่างรหัสต่อไปนี้:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

ยอมรับคำตอบรัฐ:

โดยวิธีการในตัวอย่างของคุณ, ฉันจะให้ชั้นและวิธีการแทนและ จากนั้นคุณจะยังคงมีการห่อหุ้มFridgeputCheese()takeCheese()get_cheese()set_cheese()

การห่อหุ้มนั้นถูกเก็บรักษาไว้อย่างไรโดยการเปลี่ยนชื่อจากรับ / ตั้งค่าเป็นputCheese()/ takeCheese()คุณเห็นได้ชัดว่าได้รับ / ตั้งค่าดังนั้นทำไมไม่ปล่อยไว้เป็นรับ / ตั้งค่า?

ในคำตอบเดียวกันมันยังระบุ:

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

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


7
putCheeseจะเพิ่มชีสลงในตู้เย็นและtakeCheeseจะลบออก - สิ่งเหล่านี้คือ abstractions ที่มุ่งเน้นในระดับที่สูงกว่า object field getters & setters (ซึ่งเป็น abstractions การโปรแกรมคอมพิวเตอร์ระดับล่าง
Erik Eidt

คำตอบ:


17

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

public class Fridge
{
    private int numberOfCheeseSlices;

    public void AddCheeseSlices(int n)
    {
         if(n < 0) { 
             throw new Exception("you cant add negative cheese!");
         }
         if(numberOfCheeseSlices + n > this.capacityOfFridge) { 
             throw new Exception("TOO MUCH CHEESE!!");
         }
         ..etc
         this.numberOfCheeseSlices += n;
    }
}

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


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

21
@QueenSvetlana ไม่ใช่ setter มันเป็นการดำเนินการ คุณกำลังบอกตู้เย็นว่า "นี่คือชีสอีกตัว" และมันเพิ่มเข้าไปในตัวชีสที่ห่อหุ้มไว้ Setter จะพูดว่า "คุณมีชีส 5 ตัว" การดำเนินการเหล่านี้มีหน้าที่แตกต่างกันไม่ใช่เฉพาะชื่อที่แตกต่างกัน
Ant P

1
คุณสามารถเรียกมันว่า setCheese แต่นั่นอาจทำให้สับสนเพราะมันจะไม่ตั้งค่าของชีส
Ewan

6
ทราบว่ามีข้อผิดพลาดล้นจำนวนเต็มที่นี่ หากมีมากกว่า 0 ชีสแล้วAddCheese(Integer.MAX_VALUE)ควรล้มเหลว แต่จะไม่
user253751

3
ที่จะครอบคลุมทั้งหมดในส่วน.. ฯลฯ
Ewan

5

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

Encapsulation ถูกเก็บรักษาไว้อย่างไรโดยการเปลี่ยนชื่อจาก get / set เป็น putCheese () / takeCheese () คุณเห็นได้ชัดว่าได้รับ / การตั้งค่าดังนั้นทำไมไม่เพียงแค่ปล่อยให้มันเป็น get / set

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

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

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

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

ที่ไม่สามารถกำหนดอย่างเป็นกลางโดยไม่มีบริบทมากขึ้น ตัวอย่างของคุณมีgo_shopping()วิธีอื่น หากเป็นสิ่งที่ผู้Fridgeเป็นกว่ามันไม่ได้ต้องการgetหรือสิ่งที่มันต้องการคือset Fridge.go_shopping()วิธีนี้คุณมีวิธีการที่ได้มาจากข้อกำหนด "ข้อมูล" ทั้งหมดที่จำเป็นต้องมีอยู่ในเครื่องและคุณมีพฤติกรรมที่แท้จริงแทนที่จะเป็นโครงสร้างข้อมูลที่ถูกปกคลุมบาง ๆ

จำไว้ว่าคุณไม่ได้สร้างสามัญใช้ซ้ำFridgeได้ คุณกำลังสร้าง a Fridgeสำหรับความต้องการของคุณเท่านั้น ความพยายามใด ๆ ที่ทำให้มากกว่าสิ่งที่จำเป็นต้องใช้นั้นสิ้นเปลืองจริง ๆ


10
Getters and setters break encapsulation every single time- นั่นเป็นคำพูดที่แรงเกินไปสำหรับรสนิยมของฉัน วิธีการ (รวมถึงผู้ได้รับและผู้ตั้งค่า) มีจุดประสงค์เดียวกับที่ทำในคอนเสิร์ต: อนุญาตให้เข้าและออกจากสถานที่ได้อย่างเป็นระเบียบ
Robert Harvey

8
"Getters และ setters ทำลายการห่อหุ้มทุกครั้งตามคำจำกัดความ" - คำจำกัดความใด? ฉันก็มีปัญหากับเรื่องนี้เพราะ getters และ setters เป็นเพียงส่วนหนึ่งของส่วนต่อประสานสาธารณะเช่นสมาชิกสาธารณะคนอื่น ๆ พวกเขาทำลาย encapsulation หากพวกเขาเปิดเผยรายละเอียดการใช้งานที่ถือว่าเป็นการออกแบบภายใน(นั่นคือโดยการตัดสินใจของโปรแกรมเมอร์ (หรือทีม)) ความจริงที่ว่า getters และ setters มักจะถูกเพิ่มอย่างจับจดด้วยการควบคุมการมีเพศสัมพันธ์เป็นความคิดภายหลังเป็นเรื่องอื่นทั้งหมด
Filip Milovanović

2
@ FilipMilovanovićจุดยุติธรรม ดังที่ฉันกล่าวถึงในคำตอบ "encapsulation" ใช้เพื่อซ่อนวัตถุภายใน (== วัตถุสถานะ == ตัวแปรอินสแตนซ์) Getters และ setters เผยแพร่วัตถุภายใน ดังนั้นโดยคำจำกัดความเหล่านี้พวกเขาอยู่ในความขัดแย้งตลอดกาล ตอนนี้ไม่ว่าบางครั้งเราจำเป็นต้องทำลาย encapsulation เป็นเรื่องอื่นซึ่งควรจัดการแยกต่างหาก เพื่อความชัดเจนโดยบอกว่า setters / getters มักจะทำลาย encapsulation ฉันไม่ได้บอกว่ามันเป็น "ผิด" เสมอหากช่วยได้
Robert Bräutigam

5
getters และ setters ไม่ "หยุดการห่อหุ้มตัวอักษรเสมอ" Getters และ setters มักจะไม่อ้างถึงสมาชิกที่อยู่ข้างใต้โดยตรงดำเนินการบางอย่างหรือมีการกำหนดไว้เฉพาะคุณอาจมีทะเยอทะยานหรืออาจมี setter มันแบ่ง encapsulation เมื่อ getters และ setters ไม่ได้ทำอะไรมากไปกว่าการเข้าถึงสมาชิกและทั้งสองถูกกำหนดไว้สำหรับเขตข้อมูลเดียวกัน สิ่งนี้อาจจำเป็นสำหรับผู้ดูแลห้องสมุดที่ไม่ต้องการทำลาย ABIs / API ด้วยการเปลี่ยนแปลงภายในและหลีกเลี่ยงการรวบรวมซ้ำของไคลเอ็นต์
WHN

4
ตกลงตัวรับและตัวตั้งค่าทำลายการห่อหุ้ม 99% ของเวลาเท่านั้น มีความสุข? และโดยการเปิดเผยประเภทของวัตถุที่ห่อหุ้มพวกเขาทำลายการห่อหุ้มอย่างแน่นอน เมื่อคุณมีpublic int getFoo()มันเป็นไปไม่ได้เกือบในทางปฏิบัติเพื่อเปลี่ยนเป็นประเภทอื่น
user949300

3

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

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

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

Encapsulation เป็นเรื่องเกี่ยวกับการเก็บข้อมูลและวิธีการที่ปรับเปลี่ยนข้อมูลนั้นโดยตรงในสถานที่ตรรกะหนึ่งระดับ

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

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

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

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

เช่นเดียวกับสิ่งส่วนใหญ่คำจำกัดความของการห่อหุ้มไม่ได้เป็นตัวอักษร คุณควรเห็น X นอกห้องเรียนหรือไม่? คุณควรจะเปลี่ยน Y ไหม ทุกสิ่งที่เกี่ยวข้องกับวัตถุของคุณที่นี่ในชั้นนี้หรือมีการทำงานกระจายไปทั่วหลายชั้น?


ลองดูในทางปฏิบัติ คุณกำลังบอกว่าการห่อหุ้มไม่ได้ถูกทำลายหากรหัสนั้นมีจุดประสงค์ในลักษณะนั้นหรือหากมีเหตุผล "ถูกกฎหมาย" นั่นหมายความว่าเราไม่สามารถบอกได้ว่าการห่อหุ้มนั้นไม่สมบูรณ์เพราะนักพัฒนาต้องบอกว่าเธอ "ตั้งใจ" ด้วยวิธีนั้นหรือมีเหตุผล "ถูกกฎหมาย" ดังนั้นคำจำกัดความนี้ก็ไร้ประโยชน์แล้วใช่ไหม?
Robert Bräutigam

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

ฉันคิดว่านี่เป็นที่ที่เราแตกต่างกันโดยพื้นฐานคุณพูดว่า: "แอปพลิเคชันอาจต้อง" ตั้งค่า "แอตทริบิวต์ของวัตถุ ... " ฉันไม่คิดอย่างนั้น การเลือกการออกแบบที่เกี่ยวข้องกับการตั้งค่าหรือรับแอตทริบิวต์ขึ้นอยู่กับนักพัฒนา มันเป็นทางเลือก! มีหลายวิธีในการเขียนโค้ดบางอย่างไม่เกี่ยวข้องกับการรับหรือการตั้งค่าตัวแปรอินสแตนซ์
Robert Bräutigam

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

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

1

มันไม่ใช่แค่เปลี่ยนชื่อวิธี วิธีการทั้งสองทำงานแตกต่างกัน

(นึกภาพสิ่งนี้ในความคิดของคุณ)

get_cheese และ set_cheese ตีแผ่ชีส putCheese () และ takeCheese () เก็บชีสที่ซ่อนอยู่และดูแลการจัดการและให้วิธีการจัดการกับผู้ใช้ ผู้สังเกตการณ์ไม่เห็นชีสเขา / เธอเห็นเพียงสองวิธีในการจัดการมัน

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