เหตุใดฉันจึงต้องใช้คอนเทนเนอร์ IoC แทนรหัส DI ที่ตรงไปตรงมา [ปิด]


598

ฉันใช้Dependency Injection (DI) มาระยะหนึ่งแล้วฉีดทั้งใน constructor, property หรือ method ฉันไม่เคยรู้สึกว่าต้องใช้คอนเทนเนอร์Inversion of Control (IoC) อย่างไรก็ตามยิ่งฉันอ่านมากขึ้นเท่าไหร่ฉันก็ยิ่งรู้สึกว่าชุมชนใช้คอนเทนเนอร์ IoC มากขึ้นเท่านั้น

ฉันเล่นกับภาชนะ .NET เช่นStructureMap , Ninject , ความสามัคคีและFunq ฉันยังคงล้มเหลวในการดูว่าคอนเทนเนอร์ IoC จะได้รับประโยชน์ / ปรับปรุงรหัสของฉันอย่างไร

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

กรุณาโน้มน้าวฉันว่าฉันต้องใช้คอนเทนเนอร์ IoC ฉันจะใช้ข้อโต้แย้งเหล่านี้เมื่อฉันพูดคุยกับนักพัฒนาเพื่อนในที่ทำงาน


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

3
@ shahkalpesh แต่ฉันสามารถบรรลุข้อต่อหลวมด้วย DI ง่าย อย่างไรก็ตามฉันเห็นประเด็นของคุณในกรณีที่ฉันใช้ไฟล์กำหนดค่า อย่างไรก็ตามฉันไม่แน่ใจว่าฉันต้องการใช้ไฟล์กำหนดค่าหรือไม่ ไฟล์กำหนดค่ามีความละเอียดมากปัญหาในการปรับโครงสร้างใหม่และการสลับระหว่างหลาย ๆ ไฟล์
Vadim

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

4
@ เควินจริง ๆ แล้วเรากำลังใช้ IoC มันไม่ยากอย่างที่คิดที่จะสอนทุกคนเกี่ยวกับ IoC
Vadim

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

คำตอบ:


441

ว้าวไม่น่าเชื่อว่าโจเอลจะชอบสิ่งนี้:

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

มากกว่านี้:

var svc = IoC.Resolve<IShippingService>();

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

คอนเทนเนอร์ IoC นั้นซับซ้อนใช่ไหม แต่สำหรับกรณีนี้ฉันได้แสดงว่ามันง่ายอย่างไม่น่าเชื่อ


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

"ตกลงนั่นมันฟังดูไม่ค่อยดี" ดังนั้นคุณจึงเริ่มเขียน

คุณเริ่มต้นด้วยสิ่งนี้:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime CustomerSince { get; set; }
    public string Status { get; set; }
}

.. และจบลงด้วยสิ่งนี้ :

public class UglyCustomer : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            string oldValue = _firstName;
            _firstName = value;
            if(oldValue != value)
                OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            string oldValue = _lastName;
            _lastName = value;
            if(oldValue != value)
                OnPropertyChanged("LastName");
        }
    }

    private DateTime _customerSince;
    public DateTime CustomerSince
    {
        get { return _customerSince; }
        set
        {
            DateTime oldValue = _customerSince;
            _customerSince = value;
            if(oldValue != value)
                OnPropertyChanged("CustomerSince");
        }
    }

    private string _status;
    public string Status
    {
        get { return _status; }
        set
        {
            string oldValue = _status;
            _status = value;
            if(oldValue != value)
                OnPropertyChanged("Status");
        }
    }

    protected virtual void OnPropertyChanged(string property)
    {
        var propertyChanged = PropertyChanged;

        if(propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

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

เคยได้ยินคำว่าทำงานฉลาดขึ้นหรือไม่?

ลองนึกภาพคนที่ฉลาดในทีมของคุณขึ้นมาแล้วพูดว่า: "นี่เป็นวิธีที่ง่ายกว่า"

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

ขึ้นอยู่กับเครื่องมือ IoC ที่คุณใช้คุณสามารถทำสิ่งที่มีลักษณะดังนี้:

var bindingFriendlyInstance = IoC.Resolve<Customer>(new NotifyPropertyChangedWrapper());

เกย์! คู่มือทั้งหมด INotifyPropertyChanged BS นั้นจะถูกสร้างขึ้นโดยอัตโนมัติสำหรับคุณในทุกตัวตั้งค่าคุณสมบัติเสมือนของวัตถุที่เป็นปัญหา

ความมหัศจรรย์นี้หรือไม่? ใช่ ! หากคุณเชื่อถือได้ว่ารหัสนี้ทำงานได้คุณสามารถข้ามคุณสมบัติทั้งหมดที่ห่อ mumbo-jumbo ได้อย่างปลอดภัย คุณมีปัญหาทางธุรกิจในการแก้ไข

การใช้เครื่องมือ IoC ที่น่าสนใจอื่น ๆ ในการทำ AOP:

  • การทำธุรกรรมฐานข้อมูลการประกาศ & ซ้อน
  • หน่วยงานประกาศ & ซ้อน
  • เข้าสู่ระบบ
  • เงื่อนไขก่อน / หลัง (ออกแบบตามสัญญา)

28
โอ๊ะโอคุณลืมที่จะทำให้คุณสมบัติทั้งหมดเป็นเสมือนและมันใช้งานไม่ได้ เวลาที่คุณต้องใช้ในการดีบักการขโมยนี้จากลูกค้าของคุณด้วยหรือไม่ (ขอโทษถ้าคุณไม่ได้ใช้ DynamicProxy: P.)
ธ ม

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

39
@overstood - ฉันไม่เห็นด้วยอย่างสมบูรณ์ ก่อนอื่นความชัดเจนมีความสำคัญในตัวอย่างนี้ที่ไหน ใกล้ผู้บริโภค ผู้บริโภคกำลังขอ INotifyPropertyChanged อย่างชัดเจน เพียงเพราะเราสามารถสร้างรหัสประเภทนี้ด้วยการสร้างรหัสไมโครหรือมาโครไม่ได้หมายความว่าควรเขียน ว่า INOTifyPropertyChanged gunk เป็นเพียงท่องโลกีย์ประปาและจริง ๆ แล้ว detracts จากการอ่านของชั้นในคำถาม
Ben Scheirman

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

30
ไม่ว่าจะเป็นอะไร (IoC, DynamicProxy, AOP หรือมนต์ดำ ) ใครบางคนสามารถเชื่อมโยงฉันกับบทความที่เป็นรูปธรรม / เอกสารเฉพาะในกรอบที่ให้รายละเอียดเพิ่มเติมสำหรับการบรรลุเป้าหมายนี้?
fostandy

138

ฉันอยู่กับคุณวาดิม คอนเทนเนอร์ IoC ใช้แนวคิดที่เรียบง่ายสง่างามและมีประโยชน์และทำให้เป็นสิ่งที่คุณต้องศึกษาเป็นเวลาสองวันด้วยคู่มือ 200 หน้า

ฉันรู้สึกสับสนว่าชุมชน IoC นำบทความที่สวยงามและสง่างามโดยมาร์ตินฟาวเลอร์และเปลี่ยนมันเป็นกรอบซับซ้อนที่มักจะมีคู่มือ 200-300 หน้า

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

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

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


81
ฉันคิดว่าคุณมีข้อเท็จจริงของคุณปะปนอยู่บ้าง Joel IoC และภาชนะบรรจุมาก่อนจากนั้นสนธิสัญญาของมาร์ตินไม่ใช่ในทางกลับกัน
Glenn Block

55
ส่วนใหญ่ของภาชนะบรรจุให้คุณได้อย่างรวดเร็ว ด้วย autofac, map structure, unity, ninject, etc คุณควรจะสามารถใช้ container ในเวลาประมาณ 5 นาที ใช่พวกเขามีคุณสมบัติขั้นสูง แต่คุณไม่ต้องการที่จะลงจากพื้น
Glenn Block

58
โจเอลฉันเคยคิดแบบเดียวกับคุณ จากนั้นฉันใช้เวลาห้านาทีอย่างแท้จริงและจริงจังเพื่อหาวิธีการตั้งค่าพื้นฐานขั้นสูงสุดและรู้สึกทึ่งกับกฎ 80/20 ที่ใช้งานจริง มันทำให้รหัสสะอาดขึ้นโดยเฉพาะในกรณีง่าย ๆ
จัสติน Bozonier

55
ฉันเขียนโปรแกรมมาไม่ถึงสองปีแล้วและฉันอ่าน Ninject wiki แล้วรับไป ฉันใช้ DI ในหลาย ๆ ที่ตั้งแต่นั้นมา คุณเห็นไหม น้อยกว่าสองปีและอ่านหน้าสองสามหน้าบนวิกิ ฉันไม่ใช่พ่อมดหรืออะไรเลย ฉันไม่คิดว่าตัวเองเป็นอัจฉริยะ แต่มันง่ายสำหรับฉันที่จะเข้าใจ ฉันไม่สามารถจินตนาการถึงคนที่เข้าใจ OO ได้จริง ๆ ไม่สามารถเข้าใจได้ นอกจากนี้คุณหมายถึงอะไรหน้า 200-300 คู่มือ? ฉันไม่เคยเห็นสิ่งเหล่านั้น คอนเทนเนอร์ IoC ทั้งหมดที่ฉันใช้อยู่นั้นค่อนข้างสั้นและตรงประเด็น
JR Garcia

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

37

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

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

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

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

มีการโพสต์บล็อกที่ยอดเยี่ยมโดย Mark Seemann ในเรื่องนี้: เมื่อใดควรใช้ DI Container


การตอบสนองที่ดีมาก สิ่งเดียวที่ฉันเห็นแตกต่างกันคือว่าการฉีดด้วยตนเองจะมีความซับซ้อนน้อยลงหรือไม่น่าเบื่อ
wekempf

+1 หนึ่งในคำตอบที่ดีที่สุดที่นี่ ขอบคุณสำหรับการเชื่อมโยงบทความบล็อก
stakx - ไม่สนับสนุน

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

32

ในความเห็นของฉันประโยชน์อันดับหนึ่งของ IoC คือความสามารถในการรวมศูนย์การกำหนดค่าการพึ่งพาของคุณ

หากคุณกำลังใช้การพึ่งพาการฉีดโค้ดของคุณอาจมีลักษณะเช่นนี้

public class CustomerPresenter
{
  public CustomerPresenter() : this(new CustomerView(), new CustomerService())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

หากคุณใช้คลาส IoC แบบคงที่ซึ่งตรงข้ามกับ, IMHO ยิ่งแฟ้มการกำหนดค่าที่สับสนมากขึ้นคุณอาจมีสิ่งต่อไปนี้:

public class CustomerPresenter
{
  public CustomerPresenter() : this(IoC.Resolve<ICustomerView>(), IoC.Resolve<ICustomerService>())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

จากนั้นคลาส Static IoC ของคุณจะมีลักษณะเช่นนี้ฉันใช้ Unity ที่นี่

public static IoC
{
   private static readonly IUnityContainer _container;
   static IoC()
   {
     InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerView, CustomerView>();
      _container.RegisterType<ICustomerService, CustomerService>();
      // all other RegisterTypes and RegisterInstances can go here in one file.
      // one place to change dependencies is good.
   }
}

11
เบ็นสิ่งที่คุณกำลังพูดถึงคือสถานที่ให้บริการจริง ๆ ไม่ใช่การพึ่งพาการฉีด - มีความแตกต่าง ฉันมักจะชอบคนแรกมากกว่าคนหลัง stevenharman.net/blog/archive/2009/09/25/…
stevenharman

23
แทนที่จะทำ IoC.Resolve <T> ใน Constructor เริ่มต้นของคุณคุณควรใช้ความสามารถของคอนเทนเนอร์ IOC ในการสร้างวัตถุให้คุณ กำจัด Constructor ที่ว่างเปล่าออกและปล่อยให้คอนเทนเนอร์ IOC สร้างวัตถุให้คุณ var presenter = IoC.Resolve <CustomerPresenter> (); voila! การอ้างอิงทั้งหมดมีสายแล้ว
Ben Scheirman

12
ด้านล่างของการรวมศูนย์ของการกำหนดค่าชนิดนี้คือการแยกบริบทของความรู้ โจเอลชี้ให้เห็นปัญหานี้ในการพูดว่า "รหัสกลายเป็นตรงไปตรงมาอ่านยากมาก"
Scott Bellware

6
@bellware ความรู้อะไรบ้าง อินเทอร์เฟซที่ถูกดำเนินการในฐานะการอ้างอิงครั้งแรกทำหน้าที่เป็นสัญญาและควรจะเพียงพอที่จะถ่ายทอดทั้งวัตถุประสงค์และพฤติกรรมของมันไม่ใช่หรือ? ฉันคิดว่าคุณอาจอ้างถึงความรู้ที่ได้จากการทำสัญญาในกรณีที่คุณต้องการติดตามการกำหนดค่าที่เหมาะสม? ฉันใช้คอมพิวเตอร์ (R # ใน VS, IntelliJ และอื่น ๆ ) สำหรับสิ่งที่พวกเขาทำได้ดีในการนำทางโหนดและขอบของกราฟ ฉันจองกองสมองของฉันสำหรับบริบทของไซต์งานที่ฉันกำลังจดจ่ออยู่
stevenharman

7
สตีฟฉันรู้ข้อความ. NET และเดินนั้นเป็นเวลาหลายปี ฉันมีความคุ้นเคยอย่างใกล้ชิดกับโหมดและรูปแบบอื่น ๆ และฉันเข้าใจอย่างชัดเจนว่ามีการแลกเปลี่ยนที่แท้จริง ฉันรู้วิธีการและเวลาที่จะทำให้การแลกเปลี่ยนเหล่านั้น แต่อย่างน้อยความหลากหลายในชีวิตการทำงานของฉันอย่างน้อยก็ทำให้ฉันเข้าใจมุมมองของโจเอลอย่างชัดเจน แทนที่จะบรรยายให้ฉันฟังเกี่ยวกับเครื่องมือและลูกเล่นที่ฉันใช้มานานกว่านักเขียนโมโนวัฒนธรรม. NET ส่วนใหญ่แล้วบอกสิ่งที่ฉันไม่รู้ ฉันจองกองสมองของฉันไว้สำหรับการคิดที่หลากหลายและสำคัญมากกว่า alt.net ภาวะหยุดนิ่งและออร์โธดอกซ์
Scott Bellware

32

IoC Containers ยังเหมาะสำหรับการโหลดการขึ้นต่อกันของคลาสที่ซ้อนกัน ตัวอย่างเช่นถ้าคุณมีรหัสต่อไปนี้โดยใช้การฉีดพึ่งพา

public void GetPresenter()
{
    var presenter = new CustomerPresenter(new CustomerService(new CustomerRepository(new DB())));
}

class CustomerPresenter
{
    private readonly ICustomerService service;
    public CustomerPresenter(ICustomerService service)
    {
        this.service = service;
    }
}

class CustomerService
{
    private readonly IRespository<Customer> repository;
    public CustomerService(IRespository<Customer> repository)
    {
        this.repository = repository;
    }
}

class CustomerRepository : IRespository<Customer>
{
    private readonly DB db;
    public CustomerRepository(DB db)
    {
        this.db = db;
    }
}

class DB { }

หากคุณมีการอ้างอิงทั้งหมดเหล่านี้โหลดและคอนเทนเนอร์ IoC คุณสามารถแก้ไข CustomerService และการพึ่งพาลูกทั้งหมดจะได้รับการแก้ไขโดยอัตโนมัติ

ตัวอย่างเช่น:

public static IoC
{
   private IUnityContainer _container;
   static IoC()
   {
       InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerService, CustomerService>();
      _container.RegisterType<IRepository<Customer>, CustomerRepository>();
   }

   static T Resolve<T>()
   {
      return _container.Resolve<T>();
   }
}

public void GetPresenter()
{
   var presenter = IoC.Resolve<CustomerPresenter>();
   // presenter is loaded and all of its nested child dependencies 
   // are automatically injected
   // -
   // Also, note that only the Interfaces need to be registered
   // the concrete types like DB and CustomerPresenter will automatically 
   // resolve.
}

แต่คลาส IoC เรียกว่าการใช้ anti-pattern locator should_container ถูกส่งผ่านโดยใช้ Constructor DI แทนที่จะเรียกใช้ Static T Resolve <T> ()?
Michael Freidgeim

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

28

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

... หรือบางทีผู้พัฒนาคอนเทนเนอร์ IoC อาจไม่สามารถเขียนเอกสารที่ชัดเจนได้

... หรืออย่างอื่นทั้งสองเป็นจริงในระดับหนึ่งหรืออื่น

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

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


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

ฉันชอบ AspectJ ไม่ใช่ Java แต่ไม่ใช่ XML อย่างใดอย่างหนึ่ง มันจัดเรียงของIS Java ซึ่งมันให้ความรู้สึกและดูเหมือน Java โดยเป็นส่วนขยายของมัน ฉันควรหลีกเลี่ยงแง่มุมของตัวเองจาก POJO ของฉันและในตรรกะทางธุรกิจของฉัน ฉันเกือบจะชอบที่จะออก IoCs ของฉันเช่นกัน มี XML มากเกินไปในเครื่องมือวางสาย IMHO หากฉันต้องมีไฟล์กำหนดค่าให้พวกเขาเป็นสคริปต์ BeanShell / Java, ใช้. net workalikes ของคุณที่นี่ ->
คริส K

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

1
@ KennyEvitt จุดที่ดีดูเหมือนว่าก่อนเวลาอันควรที่จะใช้กรอบ IoC หากมีแอพที่เรียบง่ายและยังไม่ได้ใส่ใจที่จะครอบคลุมการทดสอบที่ดี
Bill Karwin

27

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

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

แน่นอนว่าอาจมีปัญหา:

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

23

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

ดังนั้นคุณมีรหัสที่ใช้งานได้แล้ว และกำลังสงสัยว่าทำไมคุณต้องการแทนที่ด้วยการใช้งานของคนอื่น

ข้อดีสำหรับการพิจารณาคอนเทนเนอร์ IoC ของบุคคลที่สาม

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

จุดด้อย

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

ดังนั้นชั่งน้ำหนักข้อดีข้อเสียของคุณและตัดสินใจ


ฉันเห็นด้วยกับคุณ Sam - DI เป็นประเภทของ IoC ดังนั้นหากโปสเตอร์ต้นฉบับกำลังทำ DI พวกเขากำลังทำ IoC อยู่แล้วดังนั้นคำถามที่แท้จริงคือทำไมคุณต้องม้วนตัวเอง
Jamie Love

3
ฉันไม่เห็นด้วย. IOC เป็นวิธีการทำ DI IOC Containers ทำ DI โดยควบคุมการสร้างอินสแตนซ์ของประเภทโดยใช้การสะท้อนกลับแบบรันไทม์แบบไดนามิกเพื่อสร้างความพึงพอใจและการพึ่งพาการฉีด OP แนะนำว่าเขากำลังทำ DI แบบสแตติกและกำลังไตร่ตรองว่าทำไมเขาถึงต้องแลกเปลี่ยนผลประโยชน์จากการรวบรวมเวลาตรวจสอบผลประโยชน์ที่มักจะเกี่ยวข้องกับการสะท้อนแบบรันไทม์แบบไดนามิกของ IOC
Maxm007

17

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

ค่าที่คุณได้รับจะขึ้นอยู่กับประเภทของแอปพลิเคชันที่คุณกำลังทำงาน:

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

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

  • หากคุณต้องการใช้ AOP สำหรับข้อกังวลข้ามบางอย่างIoC จะให้ hooksเพื่อสกัดกั้นการเรียกใช้เมธอด สิ่งนี้ทำกันน้อยกว่าในโครงการ แต่ IoC ทำให้ง่ายขึ้น

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

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

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


16

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

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

ทำไมไม่ทำเช่นนี้โดยปราศจากเวทมนต์แบบคงที่:

interface IServiceA { }
interface IServiceB { }
class ServiceA : IServiceA { }
class ServiceB : IServiceB { }

class StubServiceA : IServiceA { }
class StubServiceB : IServiceB { }

interface IRoot { IMiddle Middle { get; set; } }
interface IMiddle { ILeaf Leaf { get; set; } }
interface ILeaf { }

class Root : IRoot
{
    public IMiddle Middle { get; set; }

    public Root(IMiddle middle)
    {
        Middle = middle;
    }

}

class Middle : IMiddle
{
    public ILeaf Leaf { get; set; }

    public Middle(ILeaf leaf)
    {
        Leaf = leaf;
    }
}

class Leaf : ILeaf
{
    IServiceA ServiceA { get; set; }
    IServiceB ServiceB { get; set; }

    public Leaf(IServiceA serviceA, IServiceB serviceB)
    {
        ServiceA = serviceA;
        ServiceB = serviceB;
    }
}


interface IApplicationFactory
{
    IRoot CreateRoot();
}

abstract class ApplicationAbstractFactory : IApplicationFactory
{
    protected abstract IServiceA ServiceA { get; }
    protected abstract IServiceB ServiceB { get; }

    protected IMiddle CreateMiddle()
    {
        return new Middle(CreateLeaf());
    }

    protected ILeaf CreateLeaf()
    {
        return new Leaf(ServiceA,ServiceB);
    }


    public IRoot CreateRoot()
    {
        return new Root(CreateMiddle());
    }
}

class ProductionApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new ServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new ServiceB(); }
    }
}

class FunctionalTestsApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new StubServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new StubServiceB(); }
    }
}


namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ProductionApplication();
            var root = factory.CreateRoot();

        }
    }

    //[TestFixture]
    class FunctionalTests
    {
        //[Test]
        public void Test()
        {
            var factory = new FunctionalTestsApplication();
            var root = factory.CreateRoot();
        }
    }
}

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

ข้อดี:

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

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


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

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

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

14

ประโยชน์ที่ใหญ่ที่สุดของการใช้คอนเทนเนอร์ IoC สำหรับฉัน (โดยส่วนตัวแล้วฉันใช้ Ninject) คือการกำจัดการตั้งค่าและวัตถุสถานะทั่วโลกประเภทอื่น ๆ

ฉันไม่ได้เขียนโปรแกรมสำหรับเว็บของฉัน Mine เป็นแอปพลิเคชั่นคอนโซลและในหลาย ๆ ที่ที่ต้นไม้ฉันต้องการเข้าถึงการตั้งค่าหรือข้อมูลเมตาที่ระบุโดยผู้ใช้ที่สร้างขึ้นในสาขาต้นไม้แยกต่างหาก ด้วย IoC ฉันเพียงแค่บอก Ninject ให้ถือว่าการตั้งค่าเป็นแบบซิงเกิล (เพราะมีเพียงหนึ่งตัวอย่างเท่านั้น) ขอการตั้งค่าหรือพจนานุกรมใน Constructor และ Presto ... พวกมันจะปรากฏขึ้นเมื่อฉันต้องการมัน!

หากไม่มีการใช้คอนเทนเนอร์ IoC ฉันจะต้องผ่านการตั้งค่าและ / หรือข้อมูลเมตาลงไปจนถึงวัตถุ 2, 3, ... , n ก่อนที่วัตถุนั้นจะต้องการใช้จริง

มีประโยชน์อื่น ๆ อีกมากมายสำหรับ DI / IoC container เนื่องจากคนอื่น ๆ มีรายละเอียดที่นี่และการย้ายจากความคิดในการสร้างวัตถุไปยังวัตถุที่ร้องขออาจทำให้จิตใจไม่สบายใจ แต่การใช้ DI นั้นมีประโยชน์มากสำหรับฉันและทีมของฉัน คลังแสงของคุณ!


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

1
@ qble วัตถุระดับโลกทำงานได้ดีมาก ... หากคุณต้องการให้โค้ดของคุณเป็นฝันร้ายที่ไม่สามารถทดสอบได้ซึ่งจะยากขึ้นและยากที่จะแก้ไขตลอดเวลา ขอให้โชคดี
Jeffrey Cameron

2
@JeffreyCameron IoC Container เป็นวัตถุระดับโลก มันไม่ได้เป็นการลบการพึ่งพาทั่วโลกของคุณ
qble

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

1
@qble ใช้โรงงานเพื่อสร้างอินสแตนซ์ชั่วคราวได้ทันที โรงงานเป็นบริการเดี่ยว หากอินสแตนซ์ชั่วคราวต้องการบางสิ่งบางอย่างจากคอนเทนเนอร์ IOC ให้ต่อสายเข้ากับโรงงานและให้โรงงานส่งการพึ่งพาเข้าไปในอินสแตนซ์ชั่วคราว
Jeffrey Cameron

14

กรอบ IoC นั้นยอดเยี่ยมหากคุณต้องการ ...

  • ... ทิ้งความปลอดภัยประเภท เฟรมเวิร์ก IoC จำนวนมาก (ทั้งหมด?) บังคับให้คุณเรียกใช้รหัสหากคุณต้องการแน่ใจว่าทุกสิ่งถูกเชื่อมโยงอย่างถูกต้อง "เฮ้! หวังว่าฉันจะตั้งค่าทุกอย่างแล้วดังนั้นการเริ่มต้นของ 100 คลาสเหล่านี้จะไม่ล้มเหลวในการผลิตโดยโยนข้อยกเว้น null-pointer!"

  • ... ทิ้งรหัสของคุณด้วย globals (กรอบ IoC ทั้งหมดเกี่ยวกับการเปลี่ยนสถานะทั่วโลก)

  • ... เขียนโค้ดเส็งเคร็งที่มีการอ้างอิงที่ไม่ชัดเจนซึ่งยากต่อการปรับโครงสร้างเนื่องจากคุณจะไม่มีทางรู้ว่าขึ้นอยู่กับอะไร

ปัญหาของ IoC คือคนที่ใช้พวกมันเคยเขียนโค้ดแบบนี้

public class Foo {
    public Bar Apa {get;set;}
    Foo() {
        Apa = new Bar();
    }
}

ซึ่งเห็นได้ชัดว่ามีข้อบกพร่องเนื่องจากการพึ่งพาระหว่าง Foo และ Bar นั้นมีสายแบบแข็ง จากนั้นพวกเขาก็ตระหนักว่าการเขียนโค้ดเช่นนั้นจะเป็นการดีกว่า

public class Foo {
    public IBar Apa {get;set;}
    Foo() {
        Apa = IoC<IBar>();
    }
}

ซึ่งก็มีข้อบกพร่องเช่นกัน แต่ก็เห็นได้ชัดน้อยกว่าเช่นกัน ใน Haskell ประเภทของFoo()จะเป็นIO Fooแต่คุณไม่ต้องการIOส่วน - และควรเป็นสัญญาณเตือนว่ามีบางอย่างผิดปกติกับการออกแบบของคุณถ้าคุณได้รับมัน

ในการกำจัดมัน (ส่วนของ IO) รับประโยชน์ทั้งหมดของ IoC-frameworks และไม่มีข้อเสียใด ๆ ที่คุณสามารถใช้โรงงานที่เป็นนามธรรมแทน

ทางออกที่ถูกต้องน่าจะเป็นอะไรที่คล้าย ๆ

data Foo = Foo { apa :: Bar }

หรืออาจจะ

data Foo = forall b. (IBar b) => Foo { apa :: b }

และฉีด (แต่ฉันจะไม่เรียกมันว่าฉีด) บาร์

นอกจากนี้: ดูวิดีโอนี้กับ Erik Meijer (นักประดิษฐ์ของ LINQ) ซึ่งเขาบอกว่า DI สำหรับคนที่ไม่รู้จักคณิตศาสตร์ (และฉันไม่เห็นด้วยมาก): http://www.youtube.com/watch?v = 8Mttjyf-8P4

แตกต่างจากนาย Spolsky ฉันไม่เชื่อว่าคนที่ใช้ IoC-frameworks นั้นฉลาดมาก - ฉันเชื่อว่าพวกเขาไม่รู้คณิตศาสตร์


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

คุณละทิ้งความปลอดภัยของประเภทเนื่องจากคุณไม่รู้ว่าประเภทนั้นมีสายหรือไม่ใน IoC-container (ประเภทของ IoC <IBar> นั้นไม่IBarจริงIO IBarใช่ไหม) .. และใช่ในตัวอย่าง Haskell ฉันไม่สามารถรับการอ้างอิงเป็นโมฆะ
ฟินน์สัน

ความปลอดภัยของประเภทไม่ได้เกี่ยวกับวัตถุที่ถูกต่อสายอย่างน้อยไม่ใช่ใน Java / C # เป็นเพียงแค่วัตถุที่เป็นประเภทที่ถูกต้อง และ ConcreteClass myClass = null ยังคงเป็นประเภทที่เหมาะสม หากคุณต้องการบังคับใช้ไม่เป็นโมฆะรหัสสัญญาจะเริ่มเล่น
blowdart

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

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

10

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

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

var merger = new SimpleWorkflowInstanceMerger(
    new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
    new WorkflowAnswerRowUtil(
        new WorkflowFieldAnswerEntMapper(),
        new ActivityFormFieldDisplayInfoEntMapper(),
        new FieldEntMapper()),
    new AnswerRowMergeInfoRepository());

ในการหวนกลับมันจะเร็วกว่าที่จะใช้เฟรมเวิร์ก IoC เนื่องจากโมดูลกำหนดสิ่งเหล่านี้ทั้งหมดโดยการประชุม

หลังจากใช้เวลาศึกษาคำตอบและความคิดเห็นเกี่ยวกับคำถามนี้ฉันเชื่อว่าคนที่ไม่เห็นด้วยกับการใช้คอนเทนเนอร์ IoC ไม่ได้ฝึกการฉีดขึ้นต่อกันอย่างแท้จริง ตัวอย่างที่ฉันเห็นเป็นแนวทางปฏิบัติที่มักสับสนกับการฉีดพึ่งพา บางคนบ่นเกี่ยวกับความยากลำบากในการ "อ่าน" รหัส หากทำอย่างถูกต้องส่วนใหญ่ของรหัสของคุณควรเหมือนกันเมื่อใช้ DI ด้วยมือเช่นเดียวกับเมื่อใช้คอนเทนเนอร์ IoC ความแตกต่างควรอยู่ใน "จุดเริ่มต้น" ทั้งหมดภายในแอปพลิเคชัน

กล่าวอีกนัยหนึ่งถ้าคุณไม่ชอบคอนเทนเนอร์ IoC คุณอาจไม่ได้ขึ้นอยู่กับวิธีการฉีด Dependency ที่ควรจะทำ

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

public class MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController()
    {
        _simpleMerger = new SimpleWorkflowInstanceMerger(
            new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
            new WorkflowAnswerRowUtil(
                new WorkflowFieldAnswerEntMapper(),
                new ActivityFormFieldDisplayInfoEntMapper(),
                new FieldEntMapper()),
            new AnswerRowMergeInfoRepository())
    }
    ...
}

ตอนนี้เปรียบเทียบกับการอนุญาตให้กรอบ DI ทำเพื่อคุณ:

public MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController(ISimpleWorkflowInstanceMerger simpleMerger)
    {
        _simpleMerger = simpleMerger;
    }
    ...
}

ใช้กรอบ DI โปรดทราบว่า:

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

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


9

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

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

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


8

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


7

คุณไม่ต้องการคอนเทนเนอร์ IoC

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

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


6

ฉันเพิ่งเกิดขึ้นที่จะอยู่ในขั้นตอนของการดึงรหัสออก DI ที่ปลูกในบ้านและแทนที่ด้วย IOC ฉันอาจลบโค้ดกว่า 200 บรรทัดและแทนที่ด้วยประมาณ 10 ใช่ฉันต้องเรียนรู้เล็กน้อยเกี่ยวกับวิธีใช้คอนเทนเนอร์ (Winsor) แต่ฉันเป็นวิศวกรที่ทำงานกับเทคโนโลยีอินเทอร์เน็ตใน ศตวรรษที่ 21 ดังนั้นฉันจึงคุ้นเคยกับสิ่งนั้น ฉันอาจใช้เวลาประมาณ 20 นาทีเพื่อดูวิธีการ นี่คุ้มค่ากับเวลาของฉัน


3
"แต่ฉันเป็นวิศวกรที่ทำงานเกี่ยวกับเทคโนโลยีอินเทอร์เน็ตในศตวรรษที่ 21 ดังนั้นฉันจึงคุ้นเคย" -> +1
user96403

5

ในขณะที่คุณยังคงแยกชั้นเรียนของคุณและคว่ำการอ้างอิงของคุณชั้นเรียนยังคงมีขนาดเล็กและ "กราฟการพึ่งพา" ยังคงมีขนาดใหญ่ขึ้นเรื่อย ๆ (สิ่งนี้ไม่ได้เลวร้าย) การใช้คุณสมบัติพื้นฐานของคอนเทนเนอร์ IoC ทำให้การเชื่อมต่อวัตถุเหล่านี้เป็นเรื่องเล็กน้อย แต่การทำด้วยตนเองอาจทำให้เกิดภาระได้มาก ตัวอย่างเช่นถ้าฉันต้องการสร้างตัวอย่างใหม่ของ "Foo" แต่ต้องการ "Bar" และ "บาร์" ต้องการ "A", "B" และ "C" และแต่ละคนต้องการสิ่งอื่น ๆ 3 อย่าง ฯลฯ ฯลฯ (ใช่ฉันไม่สามารถหาชื่อปลอมที่ดีได้ :))

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

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


1
ทำไมเรายังคงเอาใจช่วยผู้ที่มีสามัญน้อยที่สุดต่อไปแทนที่จะทำงานเพื่อดึงพวกเขาขึ้นมา?
stevenharman

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

4

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

มันไม่เคยทำให้ฉันประหลาดใจเมื่อคนเริ่มพ่นออกมาเกี่ยวกับความสะอาดของรหัส IoC ที่เป็นกลุ่มคนที่ครั้งหนึ่งเคยพูดกันว่าเทมเพลตใน C ++ นั้นเป็นวิธีที่สง่างามที่จะย้อนกลับไปในยุค 90 แต่ทุกวันนี้ . เฮีย!


2

ใน. NET world AOP นั้นไม่ได้รับความนิยมมากนักดังนั้นสำหรับ DI Framework นั้นเป็นทางเลือกเดียวที่แท้จริงของคุณไม่ว่าคุณจะเขียนด้วยตนเองหรือใช้เฟรมเวิร์กอื่น

หากคุณใช้ AOP คุณสามารถฉีดเมื่อคุณคอมไพล์แอปพลิเคชันของคุณซึ่งพบได้ทั่วไปใน Java

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


เฟรมเวิร์ก MEF ใหม่นี้อนุญาตให้เพียงแค่นี้สำหรับ. NET และฉันต้องบอกว่ารหัสนั้นสะอาดและเข้าใจง่ายกว่าทำให้เข้าใจแนวคิด DI ได้ง่ายขึ้น
terjetyl

4
@TT: MEF ไม่ได้เป็นภาชนะฉีดขึ้นต่อกันและไม่มีส่วนเกี่ยวข้องกับ AOP
Mauricio Scheffer

2

เกือบ 3 ปีแล้วใช่มั้ย

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

  2. หากเราใช้ Java Spring framework ที่ได้รับความนิยมมากที่สุดการกำหนดค่า IoC จะถูกย้ายจาก XMl ไปเป็นรหัสและตอนนี้ก็จะเป็นเช่นนี้

`@Configuration คลาสสาธารณะ AppConfig {

public @Bean TransferService transferService() {
    return new TransferServiceImpl(accountRepository());
}

public @Bean AccountRepository accountRepository() {
    return new InMemoryAccountRepository();
}

} `และเราจำเป็นต้องมีกรอบในการทำเช่นนี้ทำไม?


1

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

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

ถ้าใช่คุณอาจต้องการคอนเทนเนอร์ IoC ถ้าไม่เช่นนั้นคุณเพียงแค่ย้ายการเริ่มต้นออกจากที่นักพัฒนาสามารถดูได้ง่าย

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

ฉันเชื่อว่าสำหรับการใช้งานส่วนใหญ่บอกว่า IoC Containers ลบรหัสที่ไม่จำเป็นเป็นตำนาน

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

ใครมีความคิดเห็นเกี่ยวกับเรื่องนี้?


แม้สำหรับโครงการขนาดเล็กการวางโครงร่างใน XML ทำให้มันสะอาดยิ่งขึ้น & เป็นโมดูลมากกว่า - และเป็นครั้งแรกที่ทำให้สามารถนำโค้ดกลับมาใช้ใหม่ได้ (เนื่องจากไม่มีการปนเปื้อนด้วยกาวอีกต่อไป) สำหรับผลิตภัณฑ์ซอฟต์แวร์ที่เหมาะสมคุณสามารถกำหนดค่าหรือสลับตัวอย่างShippingCostCalculatorหรือProductUIPresentationหรือกลยุทธ์ / การนำไปใช้อื่น ๆภายใน codebase เดียวกันโดยปรับใช้ไฟล์ XML ที่มีการเปลี่ยนแปลงเพียงไฟล์เดียว
โทมัส W

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

คุณไม่ต้องการอินเทอร์เฟซเลยทำการกำหนดค่า XML / IoC ฉันพบการปรับปรุงที่ชัดเจนในโครงการขนาดเล็กที่มีเพียง 8 เพจและ 5 หรือ 6 บริการเท่านั้น มีความสับสนน้อยกว่าเนื่องจากบริการ & คอนโทรลเลอร์ไม่ต้องการรหัสกาวอีกต่อไป
โทมัส W

1

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


1

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

เพียงเพื่อให้ชัดเจนข้อดีที่ฉันเห็นพวกเขา: 1) งบใหม่ทั้งหมด ในที่เดียวที่ root ของรหัส 2) โค้ดตัวคูณใหม่ทั้งหมดพร้อมการเปลี่ยนแปลงเพียงครั้งเดียว 3) คะแนนพิเศษสำหรับ 'cool factor' 'เพราะมันคือ ... เอ่อ: เจ๋ง : p


1

ฉันจะพยายามค้นหาว่าทำไม IOC อาจไม่ดีสำหรับจากมุมมองของฉัน

เช่นเดียวกับทุกสิ่งทุกอย่าง IOC container (หรือ Einstein จะใส่ I = OC ^ 2) เป็นแนวคิดที่คุณต้องตัดสินใจด้วยตัวเองหากคุณต้องการหรือไม่อยู่ในรหัสของคุณ แฟชั่นล่าสุดโวยวายเกี่ยวกับ IOC เป็นเพียงแฟชั่น อย่าตกหลุมรักแฟชั่นนั่นเป็นครั้งแรก มีแนวคิดมากมายที่คุณสามารถนำไปใช้ในโค้ดของคุณได้ ก่อนอื่นฉันใช้การฉีดแบบพึ่งพาตั้งแต่ฉันเริ่มเขียนโปรแกรมและเรียนรู้คำศัพท์นั้นเมื่อมันได้รับความนิยมภายใต้ชื่อนั้น การควบคุมการพึ่งพาเป็นเรื่องที่เก่าแก่มากและได้รับการแก้ไขแล้วในหลายล้านล้านวิธีขึ้นอยู่กับสิ่งที่แยกจากสิ่งที่ Decoupling ทุกอย่างจากทุกสิ่งเป็นเรื่องไร้สาระ ปัญหาของคอนเทนเนอร์ IOC คือพยายามใช้ประโยชน์จาก Entity Framework หรือ NHibernate ในขณะที่เขียน mapper เชิงสัมพันธ์เชิงวัตถุเป็นสิ่งที่จำเป็นทันทีที่คุณต้องเชื่อมโยงฐานข้อมูลใด ๆ กับระบบของคุณคอนเทนเนอร์ IOC นั้นไม่จำเป็นเสมอไป ดังนั้นเมื่อคอนเทนเนอร์ IOC มีประโยชน์:

  1. เมื่อคุณมีสถานการณ์ที่มีการพึ่งพาจำนวนมากที่คุณต้องการจัดระเบียบ
  2. เมื่อคุณไม่สนใจเกี่ยวกับรหัสของคุณกับผลิตภัณฑ์ของบุคคลที่สาม
  3. เมื่อนักพัฒนาของคุณต้องการเรียนรู้วิธีการทำงานกับเครื่องมือใหม่

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

2: การรวมรหัสของคุณกับรหัสบุคคลที่สามเป็นปัญหา HuGe ฉันทำงานกับรหัสที่มีอายุ 10 ปีขึ้นไปและตามมาในเวลานั้นแนวคิดที่ล้ำสมัยและ ATL, COM, COM + และอื่น ๆ ไม่มีอะไรที่คุณสามารถทำได้กับรหัสนั้นในตอนนี้ สิ่งที่ฉันกำลังพูดคือแนวคิดขั้นสูงให้ประโยชน์ที่ชัดเจน แต่สิ่งนี้ถูกยกเลิกในระยะยาวด้วยความได้เปรียบที่ล้าสมัยเอง มันทำให้ราคาแพงขึ้นทั้งหมด

3: การพัฒนาซอฟต์แวร์นั้นยากพอ คุณสามารถขยายให้อยู่ในระดับที่ไม่สามารถจดจำได้หากคุณอนุญาตให้แนวคิดขั้นสูงสามารถครอบตัดโค้ดของคุณ มีปัญหากับ IOC2 แม้ว่ามันจะเป็นการแยกการพึ่งพามัน แต่ก็เป็นการแยกการไหลของลอจิกเช่นกัน ลองนึกภาพคุณพบข้อผิดพลาดและคุณต้องหยุดพักเพื่อตรวจสอบสถานการณ์ IOC2 เป็นแนวคิดขั้นสูงอื่น ๆ ทำให้ยากขึ้น การแก้ไขจุดบกพร่องภายในแนวคิดนั้นยากกว่าการแก้ไขจุดบกพร่องในรหัสตัววางเพราะเมื่อคุณแก้ไขจุดบกพร่องแนวคิดจะต้องได้รับการปฏิบัติตามอีกครั้ง (เพื่อให้ตัวอย่างแก่คุณ C ++ .NET เปลี่ยนแปลงไวยากรณ์อย่างต่อเนื่องจนคุณต้องคิดให้หนักก่อนที่จะทำการปรับเปลี่ยน. NET รุ่นเก่าบางรุ่น) ดังนั้นปัญหากับ IOC คืออะไร ปัญหาอยู่ในการแก้ไขการอ้างอิง ตรรกะในการแก้ไขนั้นมักถูกซ่อนอยู่ใน IOC2 อาจเขียนในลักษณะแปลกที่คุณต้องเรียนรู้และบำรุงรักษา ผลิตภัณฑ์บุคคลที่สามของคุณจะมีใน 5 ปีหรือไม่ ไมโครซอฟท์ไม่ได้

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

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

ไม่มีอะไรผิดปกติกับ IOC2 ยกเว้นว่าจะแก้ปัญหาเฉพาะที่แก้ไขและแนะนำปัญหาที่แนะนำ ไม่มีอะไรอีกแล้ว. อย่างไรก็ตามการต่อต้านแฟชั่นเป็นเรื่องยากมากพวกเขามีปากเหงื่อผู้ติดตามของอะไร มันแปลกที่ไม่มีใครอยู่ตรงนั้นเมื่อปัญหาเรื่องความเพ้อฝันของพวกเขาชัดเจน แนวคิดหลายประการในอุตสาหกรรมซอฟต์แวร์ได้รับการปกป้องเพราะสร้างผลกำไรหนังสือเขียนการประชุมที่จัดขึ้นผลิตภัณฑ์ใหม่ที่สร้างขึ้น นั่นคือแฟชั่นซึ่งมักมีอายุสั้น ทันทีที่ผู้คนพบสิ่งอื่นเขาก็ละทิ้งมันอย่างสมบูรณ์ IOC2 มีประโยชน์ แต่มันก็แสดงสัญญาณเดียวกับแนวคิดที่หายไปอื่น ๆ ที่ฉันเคยเห็น ฉันไม่ทราบว่ามันจะอยู่รอด ไม่มีกฎสำหรับสิ่งนั้น คุณคิดว่าถ้ามันมีประโยชน์มันจะอยู่รอด ไม่มันไม่ไปทางนั้น บริษัท รวยขนาดใหญ่แห่งหนึ่งก็เพียงพอแล้วและแนวคิดสามารถตายภายในไม่กี่สัปดาห์ เราจะเห็น NHibernate รอดชีวิตมาได้อันดับสอง บางที IOC2 จะอยู่รอดด้วย อย่าลืมว่าแนวคิดส่วนใหญ่ในการพัฒนาซอฟต์แวร์นั้นไม่เกี่ยวกับอะไรเป็นพิเศษพวกเขามีเหตุผลง่ายและชัดเจนและบางครั้งมันก็ยากที่จะจำแบบแผนการตั้งชื่อปัจจุบันมากกว่าที่จะเข้าใจแนวคิดของตัวเอง ความรู้เกี่ยวกับ IOC2 ทำให้นักพัฒนาซอฟต์แวร์เป็นนักพัฒนาที่ดีขึ้นหรือไม่? ไม่เพราะถ้านักพัฒนาซอฟต์แวร์ไม่สามารถสร้างแนวคิดที่คล้ายกันใน IOC2 ได้มันจะยากสำหรับเขาหรือเธอที่จะเข้าใจว่าปัญหาใดที่ IOC2 กำลังแก้ไขอยู่การใช้มันจะดูเป็นของเทียมและเขาหรือเธออาจเริ่มใช้มัน เพื่อความถูกต้องทางการเมือง อย่าลืมว่าแนวคิดส่วนใหญ่ในการพัฒนาซอฟต์แวร์นั้นไม่เกี่ยวกับอะไรเป็นพิเศษพวกเขามีเหตุผลง่ายและชัดเจนและบางครั้งมันก็ยากที่จะจำแบบแผนการตั้งชื่อปัจจุบันมากกว่าที่จะเข้าใจแนวคิดของตัวเอง ความรู้เกี่ยวกับ IOC2 ทำให้นักพัฒนาซอฟต์แวร์เป็นนักพัฒนาที่ดีขึ้นหรือไม่? ไม่เพราะถ้านักพัฒนาซอฟต์แวร์ไม่สามารถสร้างแนวคิดที่คล้ายกันใน IOC2 ได้มันจะยากสำหรับเขาหรือเธอที่จะเข้าใจว่าปัญหาใดที่ IOC2 กำลังแก้ไขอยู่การใช้มันจะดูเป็นของเทียมและเขาหรือเธออาจเริ่มใช้มัน เพื่อความถูกต้องทางการเมือง อย่าลืมว่าแนวคิดส่วนใหญ่ในการพัฒนาซอฟต์แวร์นั้นไม่เกี่ยวกับอะไรเป็นพิเศษพวกเขามีเหตุผลง่ายและชัดเจนและบางครั้งมันก็ยากที่จะจำแบบแผนการตั้งชื่อปัจจุบันมากกว่าที่จะเข้าใจแนวคิดของตัวเอง ความรู้เกี่ยวกับ IOC2 ทำให้นักพัฒนาซอฟต์แวร์เป็นนักพัฒนาที่ดีขึ้นหรือไม่? ไม่เพราะถ้านักพัฒนาซอฟต์แวร์ไม่สามารถสร้างแนวคิดที่คล้ายกันใน IOC2 ได้มันจะยากสำหรับเขาหรือเธอที่จะเข้าใจว่าปัญหาใดที่ IOC2 กำลังแก้ไขอยู่การใช้มันจะดูเป็นของเทียมและเขาหรือเธออาจเริ่มใช้มัน เพื่อความถูกต้องทางการเมือง ความรู้เกี่ยวกับ IOC2 ทำให้นักพัฒนาซอฟต์แวร์เป็นนักพัฒนาที่ดีขึ้นหรือไม่? ไม่เพราะถ้านักพัฒนาซอฟต์แวร์ไม่สามารถสร้างแนวคิดที่คล้ายกันใน IOC2 ได้มันจะยากสำหรับเขาหรือเธอที่จะเข้าใจว่าปัญหาใดที่ IOC2 กำลังแก้ไขอยู่การใช้มันจะดูเป็นของเทียมและเขาหรือเธออาจเริ่มใช้มัน เพื่อความถูกต้องทางการเมือง ความรู้เกี่ยวกับ IOC2 ทำให้นักพัฒนาซอฟต์แวร์เป็นนักพัฒนาที่ดีขึ้นหรือไม่? ไม่เพราะถ้านักพัฒนาซอฟต์แวร์ไม่สามารถสร้างแนวคิดที่คล้ายกันใน IOC2 ได้มันจะยากสำหรับเขาหรือเธอที่จะเข้าใจว่าปัญหาใดที่ IOC2 กำลังแก้ไขอยู่การใช้มันจะดูเป็นของเทียมและเขาหรือเธออาจเริ่มใช้มัน เพื่อความถูกต้องทางการเมือง


0

โดยส่วนตัวแล้วฉันใช้ IoC เป็นโครงสร้างแผนที่บางส่วนของแอปพลิเคชันของฉัน (ใช่ฉันยังชอบ StructureMap;)) มันทำให้ง่ายต่อการแทนที่การใช้งานอินเตอร์เฟสของฉันด้วยการใช้งาน Moq ระหว่างการทดสอบ การสร้างการตั้งค่าการทดสอบสามารถทำได้ง่ายเหมือนกับการเริ่มต้นการโทรใหม่ไปยัง IoC-framework ของฉันโดยการแทนที่คลาสใดก็ตามที่เป็นขอบเขตการทดสอบของฉันด้วยการจำลอง

นี่อาจไม่ใช่สิ่งที่ IoC มีเพื่อ แต่เป็นสิ่งที่ฉันพบว่าตัวเองใช้มันเพื่อประโยชน์สูงสุด ..




0

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

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

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

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


-8

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


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