มันหมายความว่าอย่างไรเมื่อมีคนพูดว่า“ ห่อหุ้มสิ่งที่แตกต่างกัน”?


25

หนึ่งในหลักการของ OOP ที่ฉันพบคือ: - สรุปสิ่งที่แตกต่าง

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


ดูen.wikipedia.org/wiki/Encapsulation_(computer_programming)ซึ่งอธิบายได้ดี ฉันคิดว่า "สิ่งที่แตกต่างกัน" นั้นไม่ถูกต้องเพราะบางครั้งคุณควรสรุปค่าคงที่
qwerty_so

I don't know how exactly would it contribute to a better designรายละเอียดการห่อหุ้มนั้นเกี่ยวกับการมีเพศสัมพันธ์อย่างหลวม ๆ ระหว่าง "รุ่น" และรายละเอียดการใช้งาน น้อยผูกเป็น "รุ่น" รายละเอียดการใช้งานมีความยืดหยุ่นมากขึ้นคือการแก้ปัญหา และทำให้ง่ายต่อการพัฒนา "สรุปตัวเองจากรายละเอียด"
Laiv

@Laiv ดังนั้น "vary" หมายถึงสิ่งที่วิวัฒนาการในระหว่างวงจรชีวิตซอฟต์แวร์ของคุณหรือมีการเปลี่ยนแปลงอะไรบ้างระหว่างการทำงานของโปรแกรมหรือทั้งสองอย่าง?
Haris Ghauri

2
@HarisGhauri ทั้งสอง จัดกลุ่มสิ่งที่แตกต่างกันเข้าด้วยกัน แยกสิ่งที่แตกต่างกันอย่างอิสระ สงสัยในสิ่งที่คุณคิดว่าจะไม่มีวันเปลี่ยนแปลง
candied_orange

1
@ laiv คิดว่า "abstract" เป็นจุดที่ดี มันรู้สึกท่วมท้นในการทำเช่นนั้น ในวัตถุใดวัตถุหนึ่งคุณจะต้องมีความรับผิดชอบ สิ่งที่ดีเกี่ยวกับสิ่งนั้นคือคุณต้องคิดให้รอบคอบเกี่ยวกับสิ่งหนึ่งที่นี่ เมื่อรายละเอียดของปัญหาที่เหลือเป็นปัญหาของคนอื่นมันทำให้สิ่งต่าง ๆ ง่ายขึ้น
candied_orange

คำตอบ:


30

คุณสามารถเขียนโค้ดที่มีลักษณะดังนี้:

if (pet.type() == dog) {
  pet.bark();
} else if (pet.type() == cat) {
  pet.meow();
} else if (pet.type() == duck) {
  pet.quack()
}

หรือคุณสามารถเขียนโค้ดที่มีลักษณะดังนี้:

pet.speak();

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

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

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

คุณอาจดูตัวอย่างนี้และคิดว่าฉันกำลังสับสนกับความหลากหลายและการห่อหุ้ม ฉันไม่. ฉันกำลังพูดถึง "สิ่งที่แตกต่าง" และ "รายละเอียด"

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

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


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

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

1
"สิ่งที่แตกต่างกันไปแค็ปซูล" ให้ฉันหมายถึงการซ่อนไปเปลี่ยนแปลงสถานะ ไม่นะ ฉันชอบความคิดเห็นของผู้ร่วมงาน คำตอบของ Derick Elkinจะลึกซึ้งยิ่งขึ้นอ่านมากกว่าหนึ่งครั้ง ในฐานะ @JacquesB กล่าวว่า"หลักการนี้จริง ๆ แล้วค่อนข้างลึก"
Radarbob

16

"แตกต่างกันไป" ที่นี่หมายถึง "อาจเปลี่ยนแปลงได้ตลอดเวลาเนื่องจากข้อกำหนดที่เปลี่ยนแปลง" นี่คือหลักการออกแบบหลัก: เพื่อแยกและแยกส่วนของรหัสหรือข้อมูลที่อาจมีการเปลี่ยนแปลงแยกในอนาคต หากมีการเปลี่ยนแปลงข้อกำหนดเพียงอย่างเดียวเราควรเปลี่ยนรหัสที่เกี่ยวข้องในที่เดียว แต่ถ้ารหัสฐานได้รับการออกแบบที่ไม่ดีเช่นการเชื่อมต่อระหว่างกันสูงและตรรกะสำหรับความต้องการที่แพร่กระจายในหลายสถานที่การเปลี่ยนแปลงจะยากและมีความเสี่ยงสูงที่ก่อให้เกิดผลที่ไม่คาดคิด

สมมติว่าคุณมีแอปพลิเคชันที่ใช้การคำนวณภาษีการขายในหลาย ๆ ที่ หากอัตราภาษีขายเปลี่ยนแปลงคุณต้องการอะไร:

  • อัตราภาษีการขายเป็นตัวอักษร hardcoded ทุกที่ในแอปพลิเคชันที่คำนวณภาษีการขาย

  • อัตราภาษีขายเป็นค่าคงที่ทั่วโลกซึ่งจะใช้ทุกที่ในแอปพลิเคชันที่คำนวณภาษีการขาย

  • มีวิธีการหนึ่งเรียกว่าcalculateSalesTax(product)ซึ่งเป็นที่เดียวที่จะใช้อัตราภาษีขาย

  • ระบุอัตราภาษีขายในไฟล์กำหนดค่าหรือฟิลด์ฐานข้อมูล

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

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

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

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


2
ภาษีเป็นตัวอย่างที่ดี กฎหมายและภาษีอาจเปลี่ยนจากวันหนึ่งเป็นวันอื่น หากคุณกำลังใช้ระบบการประกาศภาษีคุณจะถูกผูกติดอยู่กับการเปลี่ยนแปลงประเภทนี้ นอกจากนี้ยังเปลี่ยนจากสถานที่หนึ่งไปเป็นอีกสถานที่หนึ่ง (ประเทศจังหวัด ... )
Laiv

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

14

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

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

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

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

การคัดเลือกตัวอย่างของ CandiedOrange ในบริบทอื่นสมมติว่าเรามีภาษาขั้นตอนเช่น C หากฉันมีรหัสบางส่วนที่มี:

if (pet.type() == dog) {
  pet.bark();
} else if (pet.type() == cat) {
  pet.meow();
} else if (pet.type() == duck) {
  pet.quack()
}

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

void speak(pet) {
  if (pet.type() == dog) {
    pet.bark();
  } else if (pet.type() == cat) {
    pet.meow();
  } else if (pet.type() == duck) {
    pet.quack()
  }
}

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

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

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

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


แหมหวานประชด ใช่นี่ไม่ใช่ปัญหา OOP เพียงอย่างเดียว คุณจับฉันให้รายละเอียดกระบวนทัศน์ทางภาษาเป็นมลภาวะคำตอบของฉันและลงโทษฉันอย่างถูกต้องโดย "ปรับเปลี่ยน" กระบวนทัศน์
candied_orange

"แม้ในภาษา OO การห่อหุ้มสิ่งที่แตกต่างกันไม่ได้แปลว่าจำเป็นต้องสร้างคลาสหรือส่วนต่อประสานใหม่" - มันยากที่จะจินตนาการถึงสถานการณ์ที่ไม่ได้สร้างคลาสหรืออินเทอร์เฟซใหม่จะไม่ละเมิด SRP
taurelas

11

"สรุปสิ่งที่แตกต่าง" หมายถึงการซ่อนรายละเอียดของการใช้งานซึ่งอาจเปลี่ยนแปลงและเปลี่ยนแปลงได้

ตัวอย่าง:

ตัวอย่างเช่นสมมติว่าคลาสCourseติดตามของStudentsที่สามารถลงทะเบียน () คุณสามารถนำไปใช้กับ a LinkedListและแสดง container เพื่ออนุญาตให้ทำซ้ำได้:

class Course { 
    public LinkedList<Student> Atendees; 
    public bool register (Student s);  
    ...
}

แต่นี่ไม่ใช่ความคิดที่ดี:

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

หากคุณแค็ปซูลสิ่งที่แตกต่างกัน (หรือมากกว่าที่กล่าวไว้สิ่งที่อาจแตกต่างกัน) คุณมีอิสระในการใช้รหัสและคลาสแค็ปซูลเพื่อพัฒนาซึ่งกันและกัน นี่คือเหตุผลที่มันเป็นหลักการสำคัญใน OOP

อ่านเพิ่มเติม:

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