จำเป็นหรือไม่ที่จะต้องปฏิบัติตามแนวทางการเขียนโปรแกรมเชิงป้องกันสำหรับโค้ดที่จะไม่เปิดเผยต่อสาธารณะ


45

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

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

ฉันควรใช้ความคิดโซนนี้ไกลแค่ไหน ใครบ้างที่สามารถให้คำแนะนำกับฉันเกี่ยวกับการรักษาสัญญาในชั้นเรียนที่ฉันเขียนโดยเฉพาะอย่างยิ่งสำหรับคนที่ไม่ได้เผยแพร่สู่สาธารณะ


4
= ~ s / จำเป็น / แนะนำ / gi
GrandmasterB

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

1
ไม่เคยพูดไม่เคย คุณจะไม่สามารถรู้ได้ว่ารหัสของคุณจะอยู่ที่ไหน ;)
Izkata

1
@codebreaker ความคิดเห็นของ GrandmasterB เป็นนิพจน์แทนที่ หมายความว่า: แทนที่ "จำเป็น" ด้วย "แนะนำ"
Ricardo Souza

1
รหัสไม่มีรหัส # 116 ความน่าเชื่อถือไม่มีใครเหมาะสมอย่างยิ่งที่นี่

คำตอบ:


72

ฉันจะไม่แก้ไขปัญหาการออกแบบ - เพียงแค่คำถามว่าจะทำสิ่งที่ "ถูกต้อง" ใน API ที่ไม่ใช่แบบสาธารณะหรือไม่

มันเป็นเพียงสำหรับฉันดังนั้นจึงเป็นชนิดที่ฉันปกป้องรหัสของตัวเองจากตัวเอง

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

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


ดีแล้วที่รู้. ในอดีตที่ผ่านมาฉันเคยโปรแกรมอย่างมีประสิทธิภาพเท่าที่จะทำได้ดังนั้นบางครั้งฉันก็มีช่วงเวลาที่ยากลำบากในการทำความคุ้นเคยกับแนวคิดเช่นนี้ ฉันดีใจที่ฉันไปในทิศทางที่ถูกต้อง
codebreaker

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

2
ฉันเห็นด้วยอย่างแน่นอน ย้อนกลับไปในมหาวิทยาลัยฉันไม่เคยคิดเรื่องนั้นมาก่อน
codebreaker

25

ฉันมักจะทำตามกฎง่ายๆ

  • พยายามเขียนโปรแกรมตามสัญญาเสมอ
  • หากมีวิธีการแบบสาธารณะหรือรับข้อมูลจากโลกภายนอกให้บังคับใช้มาตรการป้องกัน (เช่นIllegalArgumentException)
  • สำหรับทุกสิ่งที่สามารถเข้าถึงได้ภายในเท่านั้นให้ใช้การยืนยัน (เช่นassert input != null)

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

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


1
+1 สำหรับการกล่าวถึงการออกแบบตามสัญญา หากคุณไม่สามารถห้ามพฤติกรรมได้อย่างสมบูรณ์ (และยากที่จะทำ) อย่างน้อยคุณก็ชัดเจนว่าไม่มีการรับประกันกับพฤติกรรมที่ไม่ดี ฉันชอบโยน IllegalStateException หรือ UnsupportedOperationException
user949300

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

16

การตั้งโปรแกรมการป้องกันเป็นสิ่งที่ดีมาก
จนกว่าจะเริ่มได้รับในทางของการเขียนรหัส ถ้าอย่างนั้นมันก็ไม่ได้เป็นสิ่งที่ดี


พูดมากขึ้นในทางปฏิบัติ ...

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

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

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


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

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


2
+1 สำหรับ "จนกว่าจะเริ่มได้รับในการเขียนโค้ด" โดยเฉพาะอย่างยิ่งสำหรับโครงการส่วนบุคคลระยะสั้นการเข้ารหัสสามารถใช้เวลานานกว่าที่คุ้มค่า
Corey

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

13

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

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

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


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

3
@ codebreaker สิ่งหนึ่งที่สามารถช่วยในกรณีนั้นคือการห่อหุ้มการ์ดในวัตถุอื่น Ace of Spades คือสิ่งที่มันเป็น สถานที่ตั้งไม่ได้ระบุตัวตนของมันและการ์ดน่าจะไม่เปลี่ยนรูป อาจมีโซนที่มีการ์ด: อาจมีCardDescriptorการ์ดที่มีตำแหน่งที่ตั้งตำแหน่งหงายหน้า / ลงหรือแม้กระทั่งการหมุนสำหรับเกมที่สนใจ สิ่งเหล่านี้ล้วนเป็นคุณสมบัติที่ไม่แน่นอนที่ไม่ได้เปลี่ยนข้อมูลประจำตัวของการ์ด

1

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

ประการที่สองไม่มี Zone หรือ ZoneList ใช้การรวบรวมหรือสิ่งอื่นใดนอกจากว่าคุณต้องการ นั่นคือถ้า Zone หรือ ZoneList จะถูกส่งผ่านไปยังสิ่งที่คาดว่าจะมีการสะสมแล้วนำไปใช้ คุณสามารถปิดการใช้งานวิธีการต่าง ๆ โดยให้พวกเขาส่งข้อยกเว้น (UnimplementedException หรืออะไรทำนองนั้น) หรือให้พวกมันทำอะไรโดยไม่ทำอะไรเลย (คิดให้ถี่ถ้วนก่อนใช้ตัวเลือกที่สองถ้าคุณทำได้เพราะมันง่ายคุณจะพบว่าคุณไม่มีบั๊กที่คุณสามารถจับได้ แต่เนิ่นๆ)

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


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

จริง ๆ แล้วฉันผ่านเขตพื้นที่ไปยังวิธีที่ใช้คอลเลกชันดังนั้นจำเป็นต้องใช้งาน อย่างไรก็ตามการเรียงลำดับของโซนในเกมเป็นแนวคิดที่น่าสนใจ
codebreaker

@ GlenH7: ฉันพบว่าการทำงานกับตัวอย่างเฉพาะมักจะช่วยได้มากกว่าทฤษฎีเชิงนามธรรม OP ให้สิ่งที่ค่อนข้างน่าสนใจดังนั้นฉันจึงไปกับสิ่งนั้น
RalphChapin

1

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

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

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

อย่างไรก็ตามการนำไปใช้นั้นดูเหมือนว่าจะเป็นCollection<Card>ชุดการ์ดที่มีลักษณะคล้ายกันมากกว่านี้และมี API ที่ จำกัด น้อยกว่า ตัวอย่างเช่นเมื่อคุณต้องการสร้างเครื่องคิดเลขมูลค่ามือหรือ AI คุณต้องมีอิสระในการเลือกว่าจะให้การ์ดแต่ละใบซ้ำกับคุณกี่ใบ

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

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