ServiceLocator เป็นการต่อต้านรูปแบบหรือไม่?


141

เมื่อเร็ว ๆ นี้ฉันได้อ่านบทความของ Mark Seemannเกี่ยวกับรูปแบบการต่อต้าน Service Locator

ผู้เขียนชี้ให้เห็นเหตุผลหลักสองประการที่ว่าทำไม ServiceLocator จึงต่อต้านรูปแบบ:

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

  2. ปัญหาการบำรุงรักษา (ซึ่งทำให้ฉันงง)
    พิจารณาตัวอย่างต่อไปนี้

เรามีคลาส'MyType'ซึ่งใช้วิธีการระบุตำแหน่งบริการ:

public class MyType
{
    public void MyMethod()
    {
        var dep1 = Locator.Resolve<IDep1>();
        dep1.DoSomething();
    }
}

ตอนนี้เราต้องการเพิ่มการอ้างอิงอื่นให้กับคลาส 'MyType'

public class MyType
{
    public void MyMethod()
    {
        var dep1 = Locator.Resolve<IDep1>();
        dep1.DoSomething();

        // new dependency
        var dep2 = Locator.Resolve<IDep2>();
        dep2.DoSomething();
    }
}

และนี่คือจุดเริ่มต้นของความเข้าใจผิดของฉัน ผู้เขียนกล่าวว่า:

มันยากกว่ามากที่จะบอกได้ว่าคุณกำลังแนะนำการเปลี่ยนแปลงที่ทำลายล้างหรือไม่ คุณต้องเข้าใจแอปพลิเคชันทั้งหมดที่ใช้ Service Locator และคอมไพเลอร์จะไม่ช่วยคุณ

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

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

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

อัปเดต (สรุป):

คำตอบสำหรับคำถามของฉัน "Is Service Locator an anti-pattern" ขึ้นอยู่กับสถานการณ์จริงๆ และแน่นอนฉันไม่แนะนำให้ตัดมันออกจากรายการเครื่องมือของคุณ อาจมีประโยชน์มากเมื่อคุณเริ่มจัดการกับรหัสเดิม หากคุณโชคดีพอที่จะอยู่ในจุดเริ่มต้นของโครงการแนวทาง DI อาจเป็นทางเลือกที่ดีกว่าเนื่องจากมีข้อได้เปรียบเหนือ Service Locator

และนี่คือความแตกต่างหลักที่ทำให้ฉันไม่ใช้ Service Locator สำหรับโครงการใหม่ของฉัน:

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

สำหรับรายละเอียดอ่านคำตอบที่ยอดเยี่ยมซึ่งได้รับด้านล่าง


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

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

1
โปรดทราบว่าเนื่องจากมีการโพสต์คำถามนี้มีการอัปเดตเกี่ยวกับService Locatorของ Mark Seemann เป็นโพสต์ Anti-Pattern ที่อธิบายว่าตัวระบุตำแหน่งบริการละเมิด OOP อย่างไรโดยการทำลายการห่อหุ้มซึ่งเป็นข้อโต้แย้งที่ดีที่สุดของเขา (และเหตุผลพื้นฐานสำหรับอาการทั้งหมด ที่เขาใช้ในการโต้แย้งก่อนหน้านี้ทั้งหมด) ปรับปรุง 2015/10/26:ปัญหาพื้นฐานที่มีบริการสก็คือว่ามันละเมิด encapsulation
NightOwl888

คำตอบ:


128

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

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

public class MyType
{
    public void MyMethod()
    {
        var dep1 = Locator.Resolve<IDep1>();
        dep1.DoSomething();

        // new dependency
        var dep2 = Locator.Resolve<IDep2>();
        dep2.DoSomething();
    }
}

ฝันร้ายของการบำรุงรักษาในคลาสนั้นคือการอ้างอิงถูกซ่อนไว้ หากคุณสร้างและใช้คลาสนั้น:

var myType = new MyType();
myType.MyMethod();

คุณไม่เข้าใจว่ามีการอ้างอิงหากซ่อนอยู่โดยใช้สถานที่ให้บริการ ตอนนี้ถ้าเราใช้การฉีดพึ่งพาแทน:

public class MyType
{
    public MyType(IDep1 dep1, IDep2 dep2)
    {
    }

    public void MyMethod()
    {
        dep1.DoSomething();

        // new dependency
        dep2.DoSomething();
    }
}

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

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

รูปแบบเป็นลายต่อต้านหรือไม่?

ไม่

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

แต่ตัวอย่างที่ดีกว่าคือ ASP.NET MVC และ WebApi คุณคิดว่าอะไรทำให้การฉีดแบบพึ่งพาเป็นไปได้ในตัวควบคุม? ถูกต้อง - สถานบริการ

คำถามของคุณ

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

มีปัญหาร้ายแรงอีกสองประการ:

  1. ด้วยสถานที่ให้บริการคุณยังเพิ่มการอ้างอิงอื่น: ตัวระบุตำแหน่งบริการ
  2. คุณจะทราบได้อย่างไรว่าควรมีการอ้างอิงตลอดอายุการใช้งานใดและควรทำความสะอาดอย่างไร / เมื่อใด

ด้วยการฉีดคอนสตรัคเตอร์โดยใช้คอนเทนเนอร์คุณจะได้รับสิ่งนั้นฟรี

หากเราอาจลืมตั้งค่า ServiceLocator เราอาจลืมเพิ่มการแมปใหม่ในคอนเทนเนอร์ IoC ของเราและวิธีการ DI อาจมีปัญหารันไทม์เหมือนกัน

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

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

นอกจากนี้ผู้เขียนยังกล่าวถึงปัญหาในการทดสอบหน่วย แต่เราจะไม่มีปัญหากับแนวทาง DI หรือไม่?

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


2
Jgauffin ขอบคุณสำหรับคำตอบ คุณชี้ให้เห็นสิ่งสำคัญอย่างหนึ่งเกี่ยวกับการตรวจสอบอัตโนมัติเมื่อเริ่มต้น ฉันไม่ได้คิดถึงเรื่องนั้นและตอนนี้ฉันเห็นประโยชน์อีกอย่างของ DI คุณยังยกตัวอย่าง: "var myType = new MyType ()" แต่ฉันไม่สามารถนับว่ามันถูกต้องเนื่องจากเราไม่เคยสร้างอินสแตนซ์การอ้างอิงในแอปจริง (IoC Container ทำเพื่อเราตลอดเวลา) เช่น: ในแอป MVC เรามีคอนโทรลเลอร์ซึ่งขึ้นอยู่กับ IMyService และ MyServiceImpl ขึ้นอยู่กับ IMyRepository เราจะไม่สร้างอินสแตนซ์ MyRepository และ MyService เราได้รับอินสแตนซ์จากพารามิเตอร์ Ctor (เช่นจาก ServiceLocator) และใช้มัน เราไม่?
davidoff

33
อาร์กิวเมนต์เดียวของคุณสำหรับ Service Locator ที่ไม่ต่อต้านรูปแบบคือ: "การกลับคอนเทนเนอร์ควบคุมจะไม่ทำงานหากไม่มีที่ตั้งบริการ" อย่างไรก็ตามอาร์กิวเมนต์นี้ไม่ถูกต้องเนื่องจาก Service Locator เป็นเรื่องเกี่ยวกับความตั้งใจและไม่เกี่ยวกับกลไกดังที่ Mark Seemann อธิบายไว้อย่างชัดเจนที่นี่ : "คอนเทนเนอร์ DI ที่ห่อหุ้มอยู่ใน Composition Root ไม่ใช่ Service Locator แต่เป็นส่วนประกอบโครงสร้างพื้นฐาน"
Steven

4
@jgauffin Web API ไม่ได้ใช้ตำแหน่งบริการสำหรับ DI ลงในคอนโทรลเลอร์ มันไม่ทำ DI เลย มันทำอะไร: ช่วยให้คุณมีตัวเลือกในการสร้าง ControllerActivator ของคุณเองเพื่อส่งผ่านไปยัง Configuration Services จากนั้นคุณสามารถสร้างรูทองค์ประกอบไม่ว่าจะเป็น Pure DI หรือ Containers นอกจากนี้คุณกำลังผสมผสานการใช้สถานที่ให้บริการซึ่งเป็นส่วนประกอบของรูปแบบของคุณกับคำจำกัดความของ "รูปแบบตัวระบุตำแหน่งบริการ" ด้วยคำจำกัดความดังกล่าว Composition Root DI อาจถือได้ว่าเป็น "Service Locator Pattern" จุดรวมของคำจำกัดความนั้นคือการสงสัย
Suamere

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

2
@jgauffin DI และ SL เป็นเวอร์ชันการแสดงผลของ IoC SL เป็นวิธีที่ผิดที่จะทำ คอนเทนเนอร์ IoC อาจเป็น SL หรืออาจใช้ DI ขึ้นอยู่กับว่ามีสายอย่างไร แต่ SL ก็แย่แย่แย่ เป็นวิธีซ่อนความจริงที่ว่าคุณเชื่อมโยงทุกอย่างเข้าด้วยกันอย่างแน่นหนา
MirroredFate

37

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

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


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

RE: "เมื่อคุณจัดการกับรหัสเดิมทุกอย่างมีเหตุผลที่จะทำให้คุณหลุดพ้นจากความยุ่งเหยิงนั้น" บางครั้งฉันก็สงสัยว่ามีรหัสเดิมเพียงเล็กน้อยที่เคยมีอยู่หรือไม่ แต่เพราะเราสามารถแก้ไขอะไรก็ได้เพื่อแก้ไขเรา ไม่เคยทำได้เลย
Drew Delano

8

จากมุมมองของการทดสอบตัวระบุตำแหน่งบริการไม่ดี ดูคำอธิบายดีๆของ Google Tech Talk ของ Misko Hevery พร้อมตัวอย่างโค้ดhttp://youtu.be/RlfLCWKxHJ0เริ่มต้นนาทีที่ 8:45 ฉันชอบการเปรียบเทียบของเขา: ถ้าคุณต้องการ $ 25 ให้ขอเงินโดยตรงแทนที่จะให้กระเป๋าเงินของคุณจากที่ที่จะนำเงินไป นอกจากนี้เขายังเปรียบเทียบ Service Locator กับกองหญ้าที่มีเข็มที่คุณต้องการและรู้วิธีดึงมัน ชั้นเรียนที่ใช้ Service Locator นั้นยากที่จะนำกลับมาใช้ใหม่เพราะมัน


10
นี่เป็นความคิดเห็นที่นำกลับมาใช้ใหม่และน่าจะดีกว่าในฐานะความคิดเห็น นอกจากนี้ฉันคิดว่าการเปรียบเทียบ (ของเขา?) ของคุณใช้เพื่อพิสูจน์ว่ารูปแบบบางอย่างเหมาะสมกับปัญหาบางอย่างมากกว่ารูปแบบอื่น ๆ
8bitjunkie

6

ปัญหาการบำรุงรักษา (ซึ่งทำให้ฉันสงสัย)

มี 2 ​​สาเหตุที่แตกต่างกันว่าทำไมการใช้ตัวระบุตำแหน่งบริการจึงไม่ดีในเรื่องนี้

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

ธรรมดาและเรียบง่าย: คลาสที่มีตัวระบุตำแหน่งบริการอยู่ในนั้นยากที่จะใช้ซ้ำได้ยากกว่าคลาสที่ยอมรับการอ้างอิงผ่านตัวสร้าง

พิจารณากรณีที่คุณจำเป็นต้องใช้บริการจากLibraryAที่ผู้เขียนตัดสินใจที่จะใช้ServiceLocatorAและการบริการจากที่มีผู้เขียนตัดสินใจที่จะใช้LibraryB ServiceLocatorBเราไม่มีทางเลือกอื่นนอกจากการใช้ตัวระบุตำแหน่งบริการ 2 แห่งในโครงการของเรา ต้องกำหนดค่าการอ้างอิงจำนวนเท่าใดเป็นเกมที่คาดเดาหากเราไม่มีเอกสารที่ดีซอร์สโค้ดหรือผู้เขียนเกี่ยวกับการโทรด่วน หากตัวเลือกเหล่านี้ล้มเหลวเราอาจต้องใช้ตัวถอดรหัสเพียงอย่างเดียวเพื่อดูว่าการอ้างอิงคืออะไร เราอาจต้องกำหนดค่า API ตัวระบุตำแหน่งบริการ 2 รายการที่แตกต่างกันโดยสิ้นเชิงและขึ้นอยู่กับการออกแบบอาจเป็นไปไม่ได้ที่จะรวมคอนเทนเนอร์ DI ที่มีอยู่ของคุณ อาจเป็นไปไม่ได้เลยที่จะแบ่งปันอินสแตนซ์เดียวของการพึ่งพาระหว่างไลบรารีทั้งสอง ความซับซ้อนของโครงการอาจรวมกันมากขึ้นหากตัวระบุตำแหน่งบริการไม่ได้อาศัยอยู่ในไลบรารีเดียวกันกับบริการที่เราต้องการโดยปริยายเรากำลังลากการอ้างอิงไลบรารีเพิ่มเติมไปยังโครงการของเราโดยปริยาย

ตอนนี้พิจารณาสองบริการเดียวกันที่ทำด้วยการฉีดตัวสร้าง LibraryAเพิ่มการอ้างอิงถึง LibraryBเพิ่มการอ้างอิงถึง ระบุการอ้างอิงในการกำหนดค่า DI ของคุณ (โดยการวิเคราะห์สิ่งที่จำเป็นผ่าน Intellisense) เสร็จแล้ว

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


1

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

นี่คือย่อหน้าจากAdaptive Code ผ่าน C # :

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

- Hall, Gary McLean Adaptive Code ผ่าน C #: การเข้ารหัสแบบ Agile ด้วยรูปแบบการออกแบบและหลักการ SOLID (การอ้างอิงสำหรับนักพัฒนา) (น. 309) การศึกษาของเพียร์สัน.


0

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

การให้ลูกค้ายอมรับการอ้างอิงถึงบริการ (ไปยังการอ้างอิง) ผ่านอินเทอร์เฟซที่ชัดเจนคุณ

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

คุณคิดถูกแล้วที่ DI มีปัญหา / ข้อเสีย แต่ข้อดีที่กล่าวมานั้นมีมากกว่าพวกเขา ... IMO คุณพูดถูกว่า DI มีการพึ่งพาในอินเทอร์เฟซ (ตัวสร้าง) - แต่หวังว่าจะเป็นการพึ่งพาที่คุณต้องการและคุณต้องการให้มองเห็นได้และตรวจสอบได้


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

อาร์กิวเมนต์ 'ข้อมูลคงที่' / 'การตรวจสอบเวลาคอมไพล์' คือฟางผู้ชาย ตามที่ @davidoff ได้ชี้ให้เห็นว่า DI มีความเสี่ยงต่อข้อผิดพลาดรันไทม์เท่า ๆ กัน ฉันจะเพิ่มด้วยว่า IDE สมัยใหม่ให้มุมมองคำแนะนำเครื่องมือสำหรับข้อมูลความคิดเห็น / สรุปสำหรับสมาชิกและแม้ในส่วนที่ไม่เป็นเช่นนั้นก็ยังมีคนดูเอกสารประกอบจนกว่าพวกเขาจะ 'รู้' API เอกสารเป็นเอกสารไม่ว่าจะเป็นพารามิเตอร์ตัวสร้างที่จำเป็นหรือข้อสังเกตเกี่ยวกับวิธีกำหนดค่าการอ้างอิง
อังคาร

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

0

ใช่บริการค้นหาเป็นรูปแบบการป้องกันมันละเมิด encapsulationและของแข็ง


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