การใช้ Func แทนส่วนต่อประสานสำหรับ IoC


15

บริบท: ฉันใช้ C #

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

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

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

มีปัญหาใด ๆ หรือไม่ที่มีการใช้งานผู้รับมอบสิทธิ์ทั่วไป / ฟังก์ชั่นการสั่งซื้อที่สูงขึ้นเช่น Func สำหรับ IoC ซึ่งตรงข้ามกับส่วนต่อประสานที่เฉพาะเจาะจงมากขึ้น?


2
สิ่งที่คุณอธิบายคือฟังก์ชั่นที่มีลำดับสูงกว่าซึ่งเป็นแง่มุมที่สำคัญของการเขียนโปรแกรมฟังก์ชั่น
Robert Harvey

2
ฉันจะใช้ตัวแทนแทนที่จะเป็นFuncเพราะคุณสามารถตั้งชื่อพารามิเตอร์ที่คุณสามารถระบุเจตนาของพวกเขา
Stefan Hanke

1
ที่เกี่ยวข้อง: StackOverflow: IOC-โรงงานข้อดีและ @TheCatWhisperer คำถามทั่วไปมากขึ้นในขณะที่คำถาม stackoverflow แคบลงไปที่ "โรงงาน" กรณีพิเศษ
k3b

คำตอบ:


11

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

ฉันเดาว่ามีผู้คนจำนวนมากที่คุ้นเคยกับการใช้interfacesมากกว่าเพราะในขณะที่การฉีดวัคซีนพึ่งพาและ IoC กำลังได้รับความนิยมไม่มีจริงเทียบเท่ากับFuncคลาสใน Java หรือ C ++ (ฉันไม่แน่ใจด้วยซ้ำว่าFuncมีอยู่ในเวลานั้นใน C # ) ดังนั้นแบบฝึกหัดตัวอย่างหรือตำราจำนวนมากยังคงต้องการinterfaceรูปแบบแม้ว่าการใช้Funcจะดูดีกว่า

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

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


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

1
โปรแกรมนี้ไม่ได้ออกแบบโดยคำนึงถึงหลักการของการแยกอินเทอร์เฟซ - ตามคำอธิบายของคุณคุณอาจไม่ทราบว่าคำนั้นสามารถนำไปใช้กับการออกแบบของคุณได้
Doc Brown

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

1
"... ในเวลาที่การฉีดวัคซีนพึ่งพาและ IoC ได้รับความนิยมไม่มีจริงเทียบเท่ากับระดับ Func" หมอนั่นคือสิ่งที่ฉันเกือบจะตอบ แต่ไม่มั่นใจพอจริง ๆ ! ขอบคุณที่ยืนยันความสงสัยของฉันในรายการนั้น
เกรแฮม

9

หนึ่งในข้อได้เปรียบหลักที่ฉันพบกับ IoC คือมันจะช่วยให้ฉันตั้งชื่อการอ้างอิงทั้งหมดของฉันผ่านการตั้งชื่ออินเตอร์เฟสของพวกเขาและคอนเทนเนอร์จะรู้ว่าอันไหนที่จะจัดหาให้กับตัวสร้างโดยจับคู่ชื่อประเภท Func<string, string>นี้จะสะดวกและจะช่วยให้สื่อความหมายมากขึ้นชื่อของการพึ่งพากว่า

ฉันยังมักจะพบว่าแม้จะมีเพียงหนึ่งเดียวพึ่งพาง่ายบางครั้งก็ต้องมีมากกว่าหนึ่งฟังก์ชั่น - อินเตอร์เฟซที่ช่วยให้คุณไปยังกลุ่มฟังก์ชั่นเหล่านี้ร่วมกันในทางที่ตนเอง documenting ที่ VS มีหลายพารามิเตอร์ที่อ่านทั้งหมดเช่นFunc<string, string>, Func<string, int>.

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


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

cwap ฉันรู้สึกถึงความเจ็บปวดของคุณ อย่างไรก็ตามในการใช้งานโดยเฉพาะของฉัน lambdas เป็นหนึ่ง liners ชี้ไปที่ฟังก์ชั่นเดียว พวกเขายังสร้างทั้งหมดในที่เดียว
TheCatWhisperer

จากประสบการณ์ของฉันนี่เป็นเพียงเรื่องของการใช้เครื่องมือ ReSharpers Inspect-> สายเรียกเข้าทำได้ดีในการหาเส้นทางไปยัง funcs / lambdas
คริสเตียน

3

มีปัญหาใด ๆ หรือไม่ที่มีการใช้งานผู้รับมอบสิทธิ์ทั่วไป / ฟังก์ชั่นการสั่งซื้อที่สูงขึ้นเช่น Func สำหรับ IoC ซึ่งตรงข้ามกับส่วนต่อประสานที่เฉพาะเจาะจงมากขึ้น?

ไม่ได้จริงๆ Func เป็นอินเทอร์เฟซของตนเอง (ในความหมายภาษาอังกฤษไม่ใช่ความหมาย C #) "พารามิเตอร์นี้เป็นสิ่งที่ให้ X เมื่อถูกถาม" Func ยังมีประโยชน์ในการให้ข้อมูลตามที่จำเป็นเท่านั้น ฉันทำสิ่งนี้เล็กน้อยและแนะนำในการดูแล

สำหรับข้อเสีย:

  • ภาชนะ IoC มักจะทำบางอย่างเพื่อมายากลลวดขึ้นอ้างอิงในระดับล่างเรียงลำดับของวิธีการและอาจจะไม่เล่นดีเมื่อบางสิ่งบางอย่างและบางสิ่งบางอย่างTFunc<T>
  • Funcs มีทางอ้อมดังนั้นจึงยากที่จะให้เหตุผลและแก้ไขจุดบกพร่องเล็กน้อย
  • Funcs หน่วงเวลาการเริ่มต้นหมายความว่าข้อผิดพลาดรันไทม์อาจปรากฏขึ้นในเวลาที่แปลกหรือไม่เลยในระหว่างการทดสอบ นอกจากนี้ยังสามารถเพิ่มโอกาสของปัญหาการสั่งซื้อและขึ้นอยู่กับการใช้งานของคุณการหยุดชะงักในการเริ่มต้นการสั่งซื้อ
  • สิ่งที่คุณผ่านเข้าสู่ Func นั้นน่าจะเป็นการปิดตัวด้วยค่าใช้จ่ายเล็กน้อยและความยุ่งยากที่เกิดขึ้น
  • การเรียก Func ช้ากว่าการเข้าถึงวัตถุโดยตรง (ไม่เพียงพอที่คุณจะสังเกตเห็นในโปรแกรมที่ไม่สำคัญ แต่มีอยู่)

1
ฉันใช้คอนเทนเนอร์ IoC เพื่อสร้างโรงงานแบบดั้งเดิมจากนั้นโรงงานบรรจุวิธีการเชื่อมต่อลงใน lambdas แก้ปัญหาที่คุณกล่าวถึงในตอนแรก จุดที่ดี
TheCatWhisperer

1

ลองมาตัวอย่างง่าย ๆ - บางทีคุณกำลังฉีดวิธีการเข้าสู่ระบบ

ฉีดชั้นเรียน

class Worker: IWorker
{
    ILogger _logger;

    Worker(ILogger logger)
    {
        _logger = logger;
    }
    void SomeMethod()
    {
        _logger.Debug("This is a debug log statement.");
    }
}        

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

container.RegisterType<ILogger, ConcreteLogger>();
container.RegisterType<IWorker, Worker>();
....
var worker = container.Resolve<IWorker>();

เมื่อทำการดีบั๊กWorkerผู้พัฒนาเพียงต้องการศึกษารูทขององค์ประกอบเพื่อกำหนดว่าจะใช้คลาสคอนกรีตได้อย่างไร

หากผู้พัฒนาต้องการตรรกะที่ซับซ้อนกว่านี้เขามีอินเทอร์เฟซทั้งหมดให้ทำงานกับ:

    void SomeMethod()
    { 
       if (_logger.IsDebugEnabled) {
           _logger.Debug("This is a debug log statement.");
       }
    }

ฉีดวิธีการ

class Worker
{
    Action<string> _methodThatLogs;

    Worker(Action<string> methodThatLogs)
    {
        _methodThatLogs = methodThatLogs;
    }
    void SomeMethod()
    {
        _methodThatLogs("This is a logging statement");
    }
}        

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

ตอนนี้เราจะฉีดวิธีนี้ได้อย่างไร คอนเทนเนอร์ IoC จะไม่ทำเพื่อคุณ Workerดังนั้นคุณจะเหลือฉีดอย่างชัดเจนเมื่อคุณยกตัวอย่าง นี่ทำให้เกิดปัญหาสองสามข้อ:

  1. มันเป็นงานมากกว่าที่จะยกตัวอย่าง Worker
  2. นักพัฒนาที่พยายามแก้ไขข้อบกพร่องWorkerจะพบว่าเป็นการยากที่จะทราบว่ามีการเรียกใช้อินสแตนซ์ที่เป็นรูปธรรมอย่างไร พวกเขาไม่สามารถปรึกษารูทองค์ประกอบได้ พวกเขาจะต้องติดตามรหัส

ถ้าเราต้องการตรรกะที่ซับซ้อนกว่านี้ล่ะ? เทคนิคของคุณเปิดเผยเพียงหนึ่งวิธี ตอนนี้ฉันคิดว่าคุณสามารถอบข้าวของที่ซับซ้อนลงในแลมบ์ดาได้:

var worker = new Worker((s) => { if (log.IsDebugEnabled) log.Debug(s) } );

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

สรุปความแตกต่าง:

  1. การฉีดเพียงวิธีเดียวทำให้ยากที่จะอนุมานจุดประสงค์ในขณะที่ส่วนต่อประสานนั้นสื่อสารวัตถุประสงค์อย่างชัดเจน
  2. การฉีดเพียงวิธีเดียวจะทำให้ฟังก์ชั่นการใช้งานน้อยลงสำหรับคนในชั้นเรียน แม้ว่าคุณไม่ต้องการมันวันนี้คุณอาจต้องการมันในวันพรุ่งนี้
  3. คุณไม่สามารถฉีดวิธีการเดียวโดยอัตโนมัติโดยใช้คอนเทนเนอร์ IoC
  4. คุณไม่สามารถบอกได้จากรูทองค์ประกอบที่คลาสคอนกรีตทำงานในบางกรณี
  5. มันเป็นปัญหาที่หน่วยทดสอบการแสดงออกแลมบ์ดาตัวเอง

หากคุณตกลงกับข้างต้นทั้งหมดแล้วก็ตกลงที่จะฉีดเพียงวิธี มิฉะนั้นฉันจะแนะนำให้คุณติดกับประเพณีและฉีดอินเทอร์เฟซ


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

ลูกแกะชี้ไปที่วิธีการอินเทอร์เฟซในแอปพลิเคชันและสร้างโครงสร้างข้อมูลโดยพลการในการทดสอบหน่วย
TheCatWhisperer

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

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

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

0

พิจารณารหัสต่อไปนี้ที่ฉันเขียนมานานแล้ว:

public interface IPhysicalPathMapper
{
    /// <summary>
    /// Gets the physical path represented by the relative URL.
    /// </summary>
    /// <param name="relativeURL"></param>
    /// <returns></returns>
    String GetPhysicalPath(String relativeURL);
}

public class EmailBuilder : IEmailBuilder
{
    public IPhysicalPathMapper PhysicalPathMapper { get; set; }
    public ITextFileLoader TextFileLoader { get; set; }
    public IEmailTemplateParser EmailTemplateParser { get; set; }
    public IEmaiBodyRenderer EmailBodyRenderer { get; set; }

    public String FromAddress { get; set; }

    public MailMessage BuildMailMessage(String templateRelativeURL, Object model, IEnumerable<String> toAddresses)
    {
        String templateText = this.TextFileLoader.LoadTextFromFile(this.PhysicalPathMapper.GetPhysicalPath(templateRelativeURL));

        EmailTemplate template = this.EmailTemplateParser.Parse(templateText);

        MailMessage email = new MailMessage()
        {
            From = new MailAddress(this.FromAddress),
            Subject = template.Subject,
            IsBodyHtml = true,
            Body = this.EmailBodyRenderer.RenderBodyToHtml(template.BodyTemplate, model)
        };

        foreach (MailAddress recipient in toAddresses.Select<String, MailAddress>(toAddress => new MailAddress(toAddress)))
        {
            email.To.Add(recipient);
        }

        return email;
    }
}

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

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

public class EmailBuilder : IEmailBuilder
{
    public ITextFileLoader TextFileLoader { get; set; }
    public IEmailTemplateParser EmailTemplateParser { get; set; }
    public IEmaiBodyRenderer EmailBodyRenderer { get; set; }

    public String FromAddress { get; set; }

    public MailMessage BuildMailMessage(String templatePath, Object model, IEnumerable<String> toAddresses)
    {
        String templateText = this.TextFileLoader.LoadTextFromFile(templatePath);

        EmailTemplate template = this.EmailTemplateParser.Parse(templateText);

        MailMessage email = new MailMessage()
        {
            From = new MailAddress(this.FromAddress),
            Subject = template.Subject,
            IsBodyHtml = true,
            Body = this.EmailBodyRenderer.RenderBodyToHtml(template.BodyTemplate, model)
        };

        foreach (MailAddress recipient in toAddresses.Select<String, MailAddress>(toAddress => new MailAddress(toAddress)))
        {
            email.To.Add(recipient);
        }

        return email;
    }
}

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

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

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

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