เมื่อใดจึงจะใช้อินเทอร์เฟซ (การทดสอบหน่วย, IoC?)


17

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

ฉันคิดว่าวิธีนี้ในการสร้างส่วนต่อประสานสำหรับทุกสิ่งที่เกิดขึ้นจากความผิดพลาดเล็กน้อย: -

1) ตอนแรกฉันคิดว่าอินเตอร์เฟสจำเป็นต้องสร้างหน่วยทดสอบ mocks (ฉันใช้ Moq) แต่ตั้งแต่ฉันค้นพบว่าคลาสสามารถเยาะเย้ยถ้าสมาชิกเป็นสมาชิกvirtualและมีคอนสตรัคเตอร์แบบไม่ใช้พารามิเตอร์ (แก้ไขฉันถ้า ฉันผิด).

2) ตอนแรกฉันคิดว่าจำเป็นต้องใช้อินเทอร์เฟซในการลงทะเบียนชั้นเรียนด้วยกรอบ IoC (Castle Windsor) เช่น

Container.Register(Component.For<ICalculator>().ImplementedBy<Calculator>()...

เมื่อในความเป็นจริงฉันสามารถลงทะเบียนประเภทคอนกรีตกับตัวเอง:

Container.Register(Component.For<Calculator>().ImplementedBy<Calculator>()...

3) การใช้อินเตอร์เฟสเช่นพารามิเตอร์คอนสตรัคเตอร์สำหรับการฉีดแบบพึ่งพาผลลัพธ์ใน "การเชื่อมต่อแบบหลวม"

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

แก้ไข : - ฉันเพิ่งเล่นกับ Moq และดูเหมือนว่าต้องใช้วิธีการเป็นสาธารณะและเสมือนจริงและมีconstructor แบบไม่มีพารามิเตอร์สาธารณะเพื่อให้สามารถเยาะเย้ยพวกเขาได้ ดูเหมือนว่าฉันจะไม่สามารถมีคลาสภายในได้?


ฉันค่อนข้างมั่นใจว่า C # ไม่ต้องการให้คุณเป็นคลาสสาธารณะเพื่อใช้งานอินเทอร์เฟซแม้ว่าอินเทอร์เฟซจะเป็นแบบสาธารณะ ...
vaughandroid

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

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

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

@Spoike คุณทำให้วิธีการ VM ของคุณเป็นสาธารณะเพียงเพื่อให้สามารถเข้าถึงได้จากการทดสอบหน่วยของเราหรือไม่ บางทีนั่นอาจเป็นสิ่งที่ฉันผิดพลาดพยายามอย่างหนักที่จะทำให้ทุกอย่างอยู่ภายใน ฉันคิดว่ามือของฉันอาจจะถูกมัดด้วย Moq แม้ว่าซึ่งดูเหมือนว่าจะต้องมีชั้นเรียน (ไม่ใช่วิธีการเท่านั้น) เป็นสาธารณะเพื่อเยาะเย้ยพวกเขา (ดูความคิดเห็นของฉันในคำตอบของ Telastyn)
Andrew Stephens

คำตอบ:


7

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

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


1
ขอบคุณสำหรับการตอบกลับ. ดังที่กล่าวมามีเหตุผลที่เข้าใจผิดเล็กน้อยว่าทำไมฉันถึงทำสิ่งที่ฉันทำแม้ว่าฉันจะรู้ว่าอินเตอร์เฟสส่วนใหญ่จะไม่มีการใช้งานมากกว่าหนึ่งครั้ง ส่วนหนึ่งของปัญหาคือฉันใช้ Moq framework ซึ่งยอดเยี่ยมสำหรับการเยาะเย้ยอินเตอร์เฟส แต่การเยาะเย้ยคลาสนั้นจะต้องเป็นสาธารณะมี ctr ที่ไม่มีพารามิเตอร์สาธารณะและวิธีการที่เยาะเย้ยจะต้องเป็น 'public virtual')
Andrew Stephens

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

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

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

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

4

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

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

3) ความเท็จเป็นอย่างไร?

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


3

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

ดังนั้นเวอร์ชันแรกของการลงทะเบียน IoC-Container ของคุณจึงเป็นสิ่งที่ถูกต้องและสิ่งที่คุณควรใช้ในอนาคต

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


2

ฉันกังวลเกี่ยวกับความจริงที่ว่าคุณดูเหมือนจะรู้สึกต้องการ "เยาะเย้ย" ทุกประเภทในโครงการทั้งหมดของคุณ

การทดสอบคู่ผสมส่วนใหญ่มีประโยชน์สำหรับการแยกรหัสของคุณออกจากโลกภายนอก (libs บุคคลที่สามดิสก์ IO เครือข่าย IO ฯลฯ ) หรือจากการคำนวณราคาแพง การเปิดเสรีกรอบการแยกของคุณมากเกินไป (คุณต้องการจริงๆหรือเปล่าโครงการของคุณซับซ้อนหรือไม่) สามารถนำไปสู่การทดสอบที่ควบคู่ไปกับการนำไปใช้และทำให้การออกแบบของคุณเข้มงวดมากขึ้น หากคุณเขียนกลุ่มการทดสอบที่ตรวจสอบว่าคลาสที่เรียกว่าเมธอด X และ Y ในลำดับนั้นแล้วส่งพารามิเตอร์ a, b และ c ไปยังเมธอด Z คุณจะได้รับค่าจริงหรือไม่ บางครั้งคุณได้รับการทดสอบเช่นนี้เมื่อทดสอบการบันทึกหรือการโต้ตอบกับ libs ของบุคคลที่สาม แต่ควรเป็นข้อยกเว้นไม่ใช่กฎ

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

เมื่อพูดถึงอินเทอร์เฟซมากขึ้นโดยเฉพาะฉันพบว่ามันมีประโยชน์ในบางกรณี:

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

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

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


1

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

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