จุดรวมของอินเทอร์เฟซที่หลายคลาสไม่ยึดติดกับชุดของกฎและการใช้งานหรือไม่
จุดรวมของอินเทอร์เฟซที่หลายคลาสไม่ยึดติดกับชุดของกฎและการใช้งานหรือไม่
คำตอบ:
อย่างเคร่งครัดพูดไม่คุณไม่ได้YAGNIใช้ ที่กล่าวไว้ว่าเวลาที่คุณใช้ในการสร้างอินเทอร์เฟซนั้นน้อยมากโดยเฉพาะอย่างยิ่งถ้าคุณมีเครื่องมือสร้างรหัสที่ใช้งานง่ายทำงานส่วนใหญ่ให้คุณ หากคุณไม่แน่ใจว่าคุณต้องการอินเทอร์เฟซหรือไม่ฉันจะบอกว่าดีกว่าที่จะทำผิดด้านการสนับสนุนคำจำกัดความของอินเทอร์เฟซ
นอกจากนี้การใช้อินเทอร์เฟซแม้กระทั่งในชั้นเรียนเดียวจะช่วยให้คุณมีการใช้งานจำลองสำหรับการทดสอบหน่วยอีกครั้งหนึ่งที่ไม่ได้อยู่ในการผลิต คำตอบของ Avner Shahar-Kashtanขยายตัวในประเด็นนี้
ฉันจะตอบว่าไม่ว่าคุณต้องการอินเทอร์เฟซหรือไม่ไม่ขึ้นอยู่กับว่าจะใช้คลาสกี่คลาส อินเทอร์เฟซเป็นเครื่องมือสำหรับกำหนดสัญญาระหว่างระบบย่อยหลาย ๆ โปรแกรมของคุณ ดังนั้นสิ่งที่สำคัญคือแอปพลิเคชันของคุณแบ่งออกเป็นระบบย่อย ควรมีอินเตอร์เฟสเป็นส่วนหน้าของระบบย่อยที่ถูกห่อหุ้มไว้ไม่ว่าคลาสจะใช้งานอย่างไร
นี่เป็นกฎง่ายๆข้อหนึ่งที่มีประโยชน์มาก:
Foo
อ้างอิงโดยตรงกับระดับBarImpl
คุณอย่างยิ่งการกระทำตัวเองที่จะเปลี่ยนทุกครั้งที่คุณเปลี่ยนFoo
BarImpl
โดยพื้นฐานแล้วคุณถือว่าพวกมันเป็นรหัสหนึ่งหน่วยที่แบ่งออกเป็นสองคลาสFoo
ดูที่อินเตอร์เฟซที่ Bar
คุณกระทำตัวเองแทนเพื่อหลีกเลี่ยงการเปลี่ยนแปลงเมื่อคุณเปลี่ยนFoo
BarImpl
หากคุณกำหนดอินเทอร์เฟซที่จุดสำคัญของแอปพลิเคชันของคุณคุณให้ความระมัดระวังกับวิธีการที่พวกเขาควรสนับสนุนและสิ่งที่พวกเขาไม่ควรและคุณแสดงความคิดเห็นอินเทอร์เฟซอย่างชัดเจนเพื่ออธิบายว่า แอพลิเคชันของคุณจะง่ายมากที่จะเข้าใจเพราะการเชื่อมต่อแสดงความคิดเห็นเหล่านี้จะให้การจัดเรียงของสเปคของแอพลิเคชันรายละเอียดของวิธีการก็ตั้งใจที่จะประพฤติ สิ่งนี้ทำให้ง่ายต่อการอ่านรหัส (แทนที่จะถามว่า "สิ่งที่ heck คือรหัสนี้ควรทำ" คุณสามารถถาม "รหัสนี้ทำในสิ่งที่ควรทำอย่างไร")
นอกเหนือจากทั้งหมดนี้ (หรือที่จริงแล้วเป็นเพราะมัน) ส่วนต่อประสานจะส่งเสริมการคอมไพล์แยกต่างหาก เนื่องจากอินเตอร์เฟสนั้นมีความสำคัญในการรวบรวมและมีการพึ่งพาน้อยกว่าการนำไปใช้งานของพวกเขานั่นหมายความว่าถ้าคุณเขียนคลาสFoo
เพื่อใช้อินเตอร์เฟสBar
คุณจึงสามารถคอมBarImpl
ไพล์Foo
ซ้ำได้โดยไม่ต้องคอมไพล์ใหม่ ในแอปพลิเคชันขนาดใหญ่สิ่งนี้สามารถประหยัดเวลาได้มาก
Foo
ขึ้นอยู่กับอินเตอร์เฟซที่Bar
แล้วสามารถแก้ไขได้โดยไม่ต้องคอมไพล์BarImpl
Foo
4. การควบคุมการเข้าถึงที่ละเอียดยิ่งขึ้นกว่าข้อเสนอสาธารณะ / ส่วนตัว (เปิดเผยคลาสเดียวกันกับลูกค้าสองรายผ่านส่วนต่อประสานที่แตกต่างกัน)
If you make class Foo refer directly to class BarImpl, you're strongly committing yourself to change Foo every time you change BarImpl
เกี่ยวกับการเปลี่ยน BarImpl การเปลี่ยนแปลงที่สามารถหลีกเลี่ยงได้ใน Foo โดยใช้interface Bar
คืออะไร? เนื่องจากตราบใดที่ลายเซ็นและการทำงานของวิธีการไม่เปลี่ยนแปลงใน BarImpl, Foo ไม่ต้องการการเปลี่ยนแปลงแม้ไม่มีอินเทอร์เฟซ (วัตถุประสงค์ทั้งหมดของอินเทอร์เฟซ) ฉันกำลังพูดถึงสถานการณ์ที่มีเพียงคลาสเดียวเท่านั้นที่BarImpl
ใช้แถบ สำหรับสถานการณ์หลายชั้นฉันเข้าใจหลักการผกผันของการพึ่งพาและวิธีการอินเทอร์เฟซที่เป็นประโยชน์
อินเทอร์เฟซถูกกำหนดเพื่อกำหนดพฤติกรรมเช่นชุดของต้นแบบของฟังก์ชัน / วิธี ประเภทที่ใช้อินเทอร์เฟซจะใช้ลักษณะการทำงานนั้นดังนั้นเมื่อคุณจัดการกับชนิดดังกล่าวที่คุณรู้จัก
ไม่จำเป็นต้องกำหนดอินเทอร์เฟซถ้าคุณรู้ว่าพฤติกรรมที่กำหนดโดยจะใช้เพียงครั้งเดียว KISS (ทำให้ง่ายโง่)
ในทางทฤษฎีแล้วคุณไม่ควรมีอินเทอร์เฟซเพียงเพื่อประโยชน์ของอินเทอร์เฟซ แต่คำตอบของ Yannis Rizos จะบอกกล่าวกับภาวะแทรกซ้อนเพิ่มเติม:
เมื่อคุณเขียนการทดสอบหน่วยและใช้เฟรมเวิร์กจำลองเช่น Moq หรือ FakeItEasy (เพื่อตั้งชื่อสองรายการล่าสุดที่ฉันเคยใช้) คุณจะสร้างคลาสอื่นที่ใช้อินเทอร์เฟซโดยปริยาย การค้นหารหัสหรือการวิเคราะห์แบบคงที่อาจอ้างว่ามีเพียงการใช้งานเพียงอย่างเดียว แต่ในความเป็นจริงแล้วมีการใช้งานการจำลองภายใน เมื่อใดก็ตามที่คุณเริ่มเขียน mocks คุณจะพบว่าการแยกอินเทอร์เฟซเหมาะสม
แต่เดี๋ยวก่อนยังมีอีกมาก มีสถานการณ์เพิ่มเติมที่มีการใช้อินเทอร์เฟซโดยนัย ตัวอย่างเช่นการใช้ WCF การสื่อสารสแต็กของ. NET สร้างพร็อกซีไปยังบริการระยะไกลซึ่งใช้อินเทอร์เฟซอีกครั้ง
ในสภาพแวดล้อมที่ไม่มีโค้ดฉันเห็นด้วยกับคำตอบที่เหลือที่นี่ อย่างไรก็ตามให้ความสนใจกับกรอบรูปแบบหรือการพึ่งพาที่คุณมีซึ่งอาจใช้ประโยชน์จากส่วนต่อประสาน
ไม่คุณไม่ต้องการมันและฉันคิดว่ามันเป็น anti-pattern เพื่อสร้างอินเตอร์เฟสสำหรับการอ้างอิงทุกคลาสโดยอัตโนมัติ
มีค่าใช้จ่ายจริงในการทำ Foo / FooImpl สำหรับทุกสิ่ง IDE อาจสร้างอินเตอร์เฟส / การนำไปใช้งานฟรี แต่เมื่อคุณนำทางรหัสคุณมีภาระการรับรู้พิเศษจาก F3 / F12 ในfoo.doSomething()
การนำคุณไปสู่ลายเซ็นของอินเทอร์เฟซไม่ใช่การใช้งานจริงที่คุณต้องการ นอกจากนี้คุณยังมีไฟล์สองไฟล์แทนที่จะเป็นไฟล์เดียวสำหรับทุกสิ่ง
ดังนั้นคุณควรจะทำก็ต่อเมื่อคุณต้องการสิ่งนั้นจริงๆ
ตอนนี้ที่อยู่โต้เถียง:
ฉันต้องการอินเทอร์เฟซสำหรับกรอบการฉีดที่พึ่งพา
อินเทอร์เฟซสำหรับการสนับสนุนเฟรมเวิร์กเป็นแบบดั้งเดิม ใน Java อินเทอร์เฟซที่ใช้เป็นข้อกำหนดสำหรับพร็อกซีแบบไดนามิก pre-CGLIB วันนี้คุณไม่ต้องการมัน ถือว่าเป็นความคืบหน้าและประโยชน์สำหรับนักพัฒนาซอฟต์แวร์ที่คุณไม่ต้องการอีกต่อไปใน EJB3, Spring ฯลฯ
ฉันต้องการ mocks สำหรับการทดสอบหน่วย
หากคุณเขียน mocks ของคุณเองและมีสองการใช้งานจริงแล้วอินเตอร์เฟซที่เหมาะสม เราอาจจะไม่ได้พูดคุยกันในตอนแรกถ้า codebase ของคุณมีทั้ง FooImpl และ TestFoo
แต่ถ้าคุณใช้กรอบการเยาะเย้ยเช่น Moq, EasyMock หรือ Mockito คุณสามารถเยาะเย้ยคลาสและไม่ต้องการอินเทอร์เฟซ มันคล้ายกับการตั้งค่าfoo.method = mockImplementation
ในภาษาแบบไดนามิกที่สามารถกำหนดวิธีการได้
เราต้องการอินเทอร์เฟซเพื่อปฏิบัติตามหลักการผกผันของการพึ่งพา (DIP)
กรมทรัพย์สินทางปัญญากล่าวว่าคุณสร้างขึ้นอยู่กับสัญญา (อินเทอร์เฟซ) ไม่ใช่การติดตั้งใช้งาน แต่ชั้นเป็นแล้วการทำสัญญาและนามธรรม นั่นคือสิ่งที่คำหลักสาธารณะ / ส่วนตัวมีไว้สำหรับ ในมหาวิทยาลัยตัวอย่างที่ยอมรับได้คืออะไรเช่นเมทริกซ์หรือโพลิโนเมียมชั้น - ผู้บริโภคมี API สาธารณะเพื่อสร้างเมทริกซ์เพิ่มพวกเขา ฯลฯ แต่ไม่ได้รับอนุญาตให้ดูแลถ้าเมทริกซ์ถูกนำมาใช้ในรูปแบบเบาบางหรือหนาแน่น ไม่มี IMatrix หรือ MatrixImpl ที่จำเป็นในการพิสูจน์จุดนั้น
นอกจากนี้ DIP มักจะถูกนำไปใช้มากเกินไปในทุกระดับการเรียกคลาส / เมธอดไม่เพียง แต่ในขอบเขตของโมดูลหลัก สัญญาณที่คุณใช้มากเกินไปคืออินเทอร์เฟซและการเปลี่ยนแปลงการใช้งานของคุณในขั้นตอนการล็อคซึ่งคุณต้องแตะสองไฟล์เพื่อทำการเปลี่ยนแปลงแทนหนึ่งไฟล์ หากใช้งาน DIP อย่างเหมาะสมนั่นหมายความว่าส่วนต่อประสานของคุณไม่จำเป็นต้องเปลี่ยนบ่อย อีกนัยหนึ่งคืออินเทอร์เฟซของคุณมีผู้บริโภคจริงเพียงรายเดียว (แอปพลิเคชันของตนเอง) เรื่องราวที่แตกต่างหากคุณกำลังสร้างห้องสมุดคลาสเพื่อการบริโภคในแอพที่แตกต่างกัน
นี่เป็นข้อพิสูจน์ถึงประเด็นของลุงบ็อบมาร์ตินเกี่ยวกับการล้อเลียน - คุณควรจะล้อเลียนที่ขอบเขตสถาปัตยกรรมที่สำคัญเท่านั้น ใน webapp การเข้าถึง HTTP และ DB เป็นขอบเขตที่สำคัญ การเรียกคลาส / เมธอดทั้งหมดนั้นไม่ใช่ ไปเหมือนกันสำหรับกรมทรัพย์สินทางปัญญา
ดูสิ่งนี้ด้วย:
new Mockup<YourClass>() {}
และทั้งคลาสรวมถึงคอนสตรัคเตอร์ของมันถูกเยาะเย้ยไม่ว่าจะเป็นอินเทอร์เฟซคลาสนามธรรมหรือคลาสคอนกรีต นอกจากนี้คุณยังสามารถ "แทนที่" พฤติกรรมของตัวสร้างหากคุณต้องการ ฉันคิดว่ามีวิธีที่เท่าเทียมกันใน Mockito หรือ Powermock
ดูเหมือนว่าคำตอบของรั้วทั้งสองด้านสามารถสรุปได้ในเรื่องนี้:
ออกแบบให้ดีและใส่อินเตอร์เฟสที่จำเป็นต้องใช้อินเตอร์เฟส
ดังที่ฉันได้บันทึกไว้ในคำตอบของคำตอบของ Yanniฉันไม่คิดว่าคุณจะมีกฏเกี่ยวกับส่วนต่อประสานที่หนักและรวดเร็วได้ กฎจะต้องมีความยืดหยุ่น กฎของฉันเกี่ยวกับอินเทอร์เฟซก็คือควรใช้อินเทอร์เฟซทุกที่ที่คุณสร้าง API และควรสร้าง API ที่ใดก็ตามที่คุณกำลังข้ามขอบเขตจากขอบเขตความรับผิดชอบหนึ่งไปสู่อีกโดเมนหนึ่ง
ตัวอย่างเช่น (มีแผนที่น่ากลัว) สมมติว่าคุณกำลังสร้างCar
ชั้นเรียน ในชั้นเรียนของคุณคุณจะต้องมีเลเยอร์ UI ในตัวอย่างนี้โดยเฉพาะอย่างยิ่งจะต้องใช้รูปแบบของIginitionSwitch
, SteeringWheel
, GearShift
, และGasPedal
BrakePedal
เนื่องจากรถคันนี้มีคุณไม่จำเป็นต้องมีAutomaticTransmission
ClutchPedal
(และเนื่องจากนี่เป็นรถที่แย่มากไม่มีเครื่องปรับอากาศวิทยุหรือเบาะนั่งตามจริงแล้วพื้นกระดานก็หายไปเช่นกัน - คุณแค่ต้องไปที่พวงมาลัยและหวังว่าจะดีที่สุด!)
ดังนั้นคลาสใดที่ต้องการอินเทอร์เฟซ คำตอบอาจเป็นคำตอบทั้งหมดหรือไม่มีเลยขึ้นอยู่กับการออกแบบของคุณ
คุณสามารถมีส่วนต่อประสานที่มีลักษณะดังนี้:
Interface ICabin
Event IgnitionSwitchTurnedOn()
Event IgnitionSwitchTurnedOff()
Event BrakePedalPositionChanged(int percent)
Event GasPedalPositionChanged(int percent)
Event GearShiftGearChanged(int gearNum)
Event SteeringWheelTurned(float degree)
End Interface
ณ จุดนั้นพฤติกรรมของคลาสเหล่านั้นจะกลายเป็นส่วนหนึ่งของ ICabin Interface / API ในตัวอย่างนี้คลาส (ถ้ามี) อาจจะง่ายมีคุณสมบัติน้อยและฟังก์ชั่นหรือสอง และสิ่งที่คุณระบุโดยนัยเกี่ยวกับการออกแบบของคุณก็คือคลาสเหล่านี้มีไว้เพื่อสนับสนุนการดำเนินการใด ๆ ที่เป็นรูปธรรมของ ICabin ที่คุณมีและพวกเขาไม่สามารถอยู่ได้ด้วยตนเองหรือพวกมันไม่มีความหมายนอกบริบทของ ICabin
เป็นเหตุผลเดียวกับที่คุณไม่ได้ทดสอบสมาชิกส่วนตัว - มีเพียงเพื่อสนับสนุน API สาธารณะและพฤติกรรมของพวกเขาควรได้รับการทดสอบโดยการทดสอบ API
ดังนั้นหากระดับของคุณอยู่ แต่เพียงผู้เดียวที่จะสนับสนุนชั้นอื่นและแนวคิดที่คุณดูว่ามันเป็นไม่ได้จริงๆมีโดเมนเป็นของตัวเองแล้วก็ปรับที่จะข้ามอินเตอร์เฟซ แต่ถ้าคลาสของคุณมีความสำคัญพอที่คุณจะคิดว่ามันโตพอที่จะมีโดเมนของตัวเองให้ไปข้างหน้าและให้อินเทอร์เฟซ
แก้ไข:
บ่อยครั้ง (รวมอยู่ในคำตอบนี้) คุณจะอ่านสิ่งต่าง ๆ เช่น 'โดเมน', 'การพึ่งพา' (บ่อยครั้งควบคู่ไปกับ 'การฉีด') ที่ไม่ได้มีความหมายอะไรกับคุณเมื่อคุณเริ่มโปรแกรม มีความหมายอะไรกับฉัน) สำหรับโดเมนมันหมายถึงสิ่งที่ดูเหมือน:
ดินแดนที่ใช้อำนาจหรืออำนาจเหนือ; สมบัติของอธิปไตยหรือเครือจักรภพหรือไม่ชอบ ยังใช้เปรียบเปรย [WordNet sense 2] [1913 เว็บสเตอร์]
ในแง่ของตัวอย่างของฉัน - IgnitionSwitch
ขอพิจารณา ในรถยนต์ meatspace สวิตช์กุญแจจุดระเบิดรับผิดชอบ:
คุณสมบัติเหล่านั้นประกอบขึ้นเป็นโดเมนของIgnitionSwitch
หรือในคำอื่น ๆ สิ่งที่รู้เกี่ยวกับและรับผิดชอบ
ไม่ได้เป็นผู้รับผิดชอบในการIgnitionSwitch
GasPedal
สวิตช์จุดระเบิดนั้นไม่มีส่วนสำคัญใด ๆ กับคันเร่งในทุก ๆ ทาง พวกเขาทำงานเป็นอิสระอย่างสมบูรณ์จากกัน (แม้ว่ารถจะไม่มีราคาพอสมควรโดยไม่ต้องทั้งคู่!)
ตามที่ฉันกล่าวไว้ในตอนแรกมันขึ้นอยู่กับการออกแบบของคุณ คุณสามารถออกแบบIgnitionSwitch
ที่มีสองค่า: เปิด ( True
) และปิด ( False
) หรือคุณสามารถออกแบบเพื่อรับรองความถูกต้องของคีย์ที่ให้ไว้และโฮสต์ของการดำเนินการอื่น ๆ นั่นคือส่วนที่ยากของการเป็นนักพัฒนาที่จะตัดสินใจว่าจะวาดเส้นบนผืนทราย - และโดยส่วนใหญ่แล้วจะสัมพันธ์กันอย่างสมบูรณ์ บรรทัดเหล่านั้นในทรายนั้นมีความสำคัญ - นั่นคือที่ที่ API ของคุณอยู่และสิ่งที่ส่วนต่อประสานของคุณควรอยู่
ไม่(YAGNI)ยกเว้นว่าคุณวางแผนที่จะเขียนการทดสอบสำหรับคลาสอื่น ๆ ที่ใช้อินเทอร์เฟซนี้และการทดสอบเหล่านั้นจะได้รับประโยชน์จากการเยาะเย้ยอินเทอร์เฟซ
จากMSDN :
อินเทอร์เฟซเหมาะกับสถานการณ์ที่แอปพลิเคชันของคุณต้องการประเภทวัตถุที่ไม่เกี่ยวข้องจำนวนมากเพื่อให้การทำงานบางอย่าง
อินเทอร์เฟซมีความยืดหยุ่นมากกว่าคลาสพื้นฐานเนื่องจากคุณสามารถกำหนดการใช้งานเดี่ยวที่สามารถใช้หลายอินเตอร์เฟสได้
อินเทอร์เฟซดีกว่าในสถานการณ์ที่คุณไม่จำเป็นต้องสืบทอดการใช้งานจากคลาสพื้นฐาน
อินเทอร์เฟซมีประโยชน์ในกรณีที่คุณไม่สามารถใช้การสืบทอดคลาสได้ ตัวอย่างเช่นโครงสร้างไม่สามารถสืบทอดจากคลาส แต่สามารถใช้อินเตอร์เฟสได้
โดยทั่วไปในกรณีของคลาสเดียวมันไม่จำเป็นต้องใช้อินเทอร์เฟซ แต่เมื่อพิจารณาถึงอนาคตของโครงการของคุณมันอาจมีประโยชน์ในการกำหนดพฤติกรรมที่จำเป็นของคลาสอย่างเป็นทางการ
เพื่อตอบคำถาม: มีมากกว่านั้น
สิ่งสำคัญอย่างหนึ่งของอินเทอร์เฟซคือเจตนา
อินเทอร์เฟซคือ "ชนิดนามธรรมที่ไม่มีข้อมูล แต่เปิดเผยพฤติกรรม" - อินเทอร์เฟซ (คำนวณ)ดังนั้นถ้านี่เป็นพฤติกรรมหรือชุดของพฤติกรรมที่คลาสนั้นสนับสนุน อย่างไรก็ตามหากพฤติกรรมนั้นเป็นสิ่งที่อยู่ภายในแนวคิดที่เป็นตัวเป็นตนของคลาสดังนั้นคุณอาจไม่ต้องการอินเทอร์เฟซเลย
คำถามแรกที่ถามคือลักษณะของสิ่งหรือกระบวนการที่คุณพยายามเป็นตัวแทน จากนั้นทำตามด้วยเหตุผลเชิงปฏิบัติในการนำธรรมชาตินั้นไปใช้
เมื่อคุณถามคำถามนี้ฉันคิดว่าคุณได้เห็นความสนใจในการใช้อินเทอร์เฟซที่ซ่อนการใช้งานหลายอย่างแล้ว สิ่งนี้สามารถประจักษ์โดยหลักการการผกผันพึ่งพา
อย่างไรก็ตามความจำเป็นที่จะต้องมีอินเตอร์เฟซหรือไม่นั้นไม่ได้ขึ้นอยู่กับจำนวนการใช้งานของมัน บทบาทที่แท้จริงของอินเทอร์เฟซคือมันกำหนดสัญญาที่ระบุว่าบริการใดควรให้แทนวิธีการนำไปใช้
เมื่อกำหนดสัญญาแล้วสองทีมขึ้นไปสามารถทำงานได้อย่างอิสระ สมมติว่าคุณกำลังทำงานกับโมดูล A และขึ้นอยู่กับโมดูล B ความจริงที่สร้างอินเทอร์เฟซบน B ช่วยให้คุณทำงานต่อไปได้โดยไม่ต้องกังวลเกี่ยวกับการใช้งานของ B เพราะรายละเอียดทั้งหมดจะถูกซ่อนไว้โดยส่วนต่อประสาน ดังนั้นการเขียนโปรแกรมแบบกระจายเป็นไปได้
แม้ว่าโมดูล B จะมีการใช้อินเตอร์เฟสเพียงหนึ่งอินเทอร์เฟซก็ยังจำเป็น
โดยสรุปอินเตอร์เฟสจะซ่อนรายละเอียดการใช้งานจากผู้ใช้ การเขียนโปรแกรมไปยังอินเทอร์เฟซช่วยในการเขียนเอกสารเพิ่มเติมเนื่องจากต้องกำหนดสัญญาการเขียนซอฟต์แวร์แบบแยกส่วนเพิ่มเติมเพื่อส่งเสริมการทดสอบหน่วยและเพื่อเร่งความเร็วในการพัฒนา
คำตอบทั้งหมดที่นี่ดีมาก แน่นอนว่าส่วนใหญ่คุณไม่จำเป็นต้องใช้อินเทอร์เฟซที่แตกต่างกัน แต่มีบางกรณีที่คุณอาจต้องการทำมันต่อไป นี่คือบางกรณีที่ฉันทำ:
คลาสใช้อินเทอร์เฟซอื่นที่ฉันไม่ต้องการให้เปิดเผย
เกิดขึ้นบ่อยครั้งกับคลาสอะแดปเตอร์ที่เชื่อมรหัสของบุคคลที่สาม
interface NameChangeListener { // Implemented by a lot of people
void nameChanged(String name);
}
interface NameChangeCount { // Only implemented by my class
int getCount();
}
class NameChangeCounter implements NameChangeListener, NameChangeCount {
...
}
class SomeUserInterface {
private NameChangeCount currentCount; // Will never know that you can change the counter
}
ชั้นเรียนใช้เทคโนโลยีเฉพาะที่ไม่ควรรั่วราง
ส่วนใหญ่เมื่อมีปฏิสัมพันธ์กับห้องสมุดภายนอก แม้ว่าจะมีการนำไปใช้เพียงครั้งเดียว แต่ฉันก็ใช้อินเทอร์เฟซเพื่อให้แน่ใจว่าฉันจะไม่แนะนำการเชื่อมต่อที่ไม่จำเป็นกับไลบรารี่ภายนอก
interface SomeRepository { // Guarantee that the external library details won't leak trough
...
}
class OracleSomeRepository implements SomeRepository {
... // Oracle prefix allow us to quickly know what is going on in this class
}
การสื่อสารข้ามเลเยอร์
แม้ว่าจะมีเพียงหนึ่งคลาส UI ที่ใช้หนึ่งในโดเมนคลาส แต่ก็อนุญาตให้แยกได้ดีขึ้นระหว่างเลเยอร์เหล่านั้นและที่สำคัญที่สุดคือหลีกเลี่ยงการพึ่งพาแบบวนรอบ
package project.domain;
interface UserRequestSource {
public UserRequest getLastRequest();
}
class UserBehaviorAnalyser {
private UserRequestSource requestSource;
}
package project.ui;
class OrderCompleteDialog extends SomeUIClass implements project.domain.UserRequestSource {
// UI concern, no need for my domain object to know about this method.
public void displayLabelInErrorMode();
// They most certainly need to know about *that* though
public UserRequest getLastRequest();
}
ควรมีชุดย่อยของเมธอดให้กับวัตถุ
ส่วนใหญ่ส่วนใหญ่เกิดขึ้นเมื่อฉันมีวิธีการกำหนดค่าบางอย่างในคลาสรูปธรรม
interface Sender {
void sendMessage(Message message)
}
class PacketSender implements Sender {
void sendMessage(Message message);
void setPacketSize(int sizeInByte);
}
class Throttler { // This class need to have full access to the object
private PacketSender sender;
public useLowNetworkUsageMode() {
sender.setPacketSize(LOW_PACKET_SIZE);
sender.sendMessage(new NotifyLowNetworkUsageMessage());
... // Other details
}
}
class MailOrder { // Not this one though
private Sender sender;
}
ดังนั้นในที่สุดฉันก็ใช้ interface ด้วยเหตุผลเดียวกับที่ฉันใช้ฟิลด์ส่วนตัว: วัตถุอื่นไม่ควรมีการเข้าถึงสิ่งที่พวกเขาไม่ควรเข้าถึง หากฉันมีกรณีเช่นนั้นฉันแนะนำอินเทอร์เฟซแม้ว่ามีเพียงคลาสเดียวที่ใช้งานได้
อินเทอร์เฟซสำคัญมาก แต่พยายามควบคุมจำนวนที่คุณมี
การเริ่มต้นสร้างอินเทอร์เฟซสำหรับทุกสิ่งมันเป็นเรื่องง่ายที่จะลงเอยด้วยรหัส 'สปาเก็ตตี้สับ' ฉันคล้อยตามภูมิปัญญาที่ยิ่งใหญ่ของ Ayende Rahien ที่ได้โพสต์คำพูดที่ฉลาดมากบางเรื่อง:
http://ayende.com/blog/153889/limit-your-abstractions-analyzing-a-ddd-application
นี่คือโพสต์แรกของซีรีส์ทั้งชุดของเขาดังนั้นอ่านต่อไป!
ไม่มีเหตุผลจริงที่จะทำอะไร อินเทอร์เฟซจะช่วยคุณและไม่ใช่โปรแกรมแสดงผล ดังนั้นแม้ว่าอินเทอร์เฟซจะถูกใช้งานโดยคลาสกว่าล้านคลาสไม่มีกฎที่บอกว่าคุณต้องสร้างขึ้นมา คุณสร้างรหัสขึ้นมาเพื่อที่เมื่อคุณหรือใครก็ตามที่ใช้รหัสของคุณต้องการเปลี่ยนบางสิ่งที่มันทำให้เกิดการใช้งานทั้งหมด การสร้างอินเทอร์เฟซจะช่วยคุณในทุกกรณีในอนาคตที่คุณอาจต้องการสร้างคลาสอื่นที่ใช้งาน
ไม่จำเป็นต้องกำหนดอินเตอร์เฟสสำหรับคลาสเสมอไป
วัตถุอย่างง่ายเช่นวัตถุที่มีมูลค่านั้นไม่มีการนำไปใช้งานหลายอย่าง พวกเขาไม่จำเป็นต้องถูกเยาะเย้ยเหมือนกัน การใช้งานสามารถทดสอบได้ด้วยตนเองและเมื่อมีการทดสอบคลาสอื่นที่ขึ้นอยู่กับพวกเขาวัตถุค่าจริงสามารถใช้
โปรดจำไว้ว่าการสร้างส่วนต่อประสานมีค่าใช้จ่าย จำเป็นต้องได้รับการอัปเดตตามการนำไปใช้จำเป็นต้องมีไฟล์เพิ่มเติมและ IDE บางตัวจะมีปัญหาในการซูมเข้าในการใช้งานไม่ใช่ส่วนต่อประสาน
ดังนั้นฉันจะกำหนดอินเทอร์เฟซสำหรับคลาสระดับสูงที่คุณต้องการให้นามธรรมจากการใช้งานเท่านั้น
โปรดทราบว่าในชั้นเรียนคุณจะได้รับส่วนต่อประสานฟรี นอกเหนือจากการนำไปใช้งานคลาสจะกำหนดอินเตอร์เฟสจากชุดวิธีสาธารณะ อินเทอร์เฟซนั้นถูกใช้งานโดยคลาสที่ได้รับทั้งหมด มันไม่ได้พูดอินเทอร์เฟซอย่างเคร่งครัด แต่สามารถใช้งานได้ในลักษณะเดียวกัน ดังนั้นฉันไม่คิดว่าจำเป็นต้องสร้างส่วนต่อประสานที่มีอยู่แล้วภายใต้ชื่อของคลาส