დამოკიდებულების ინვერსია. კრიტიკული შეხედულება დამოკიდებულების ინვერსიის პრინციპზე. ტრადიციული ფენიანი არქიტექტურა

სინამდვილეში, ყველა პრინციპი ᲛᲧᲐᲠᲘისინი მჭიდროდ არიან დაკავშირებული ერთმანეთთან და მათი მთავარი მიზანია ხელი შეუწყონ მაღალი ხარისხის, მასშტაბირებადი პროგრამული უზრუნველყოფის შექმნას. მაგრამ ბოლო პრინციპი ᲛᲧᲐᲠᲘნამდვილად გამოირჩევა მათი ფონზე. პირველ რიგში, მოდით შევხედოთ ამ პრინციპის ფორმულირებას. Ისე, დამოკიდებულების ინვერსიის პრინციპი (დამოკიდებულების ინვერსიის პრინციპი - DIP): „აბსტრაქციებზე დამოკიდებულება. რაიმე კონკრეტულზე არ არის დამოკიდებული“.. პრინციპს ხაზს უსვამს პროგრამული უზრუნველყოფის დარგის ცნობილი სპეციალისტი რობერტ მარტინიც DIPდა წარმოაჩენს მას უბრალოდ სხვა პრინციპების დაცვის შედეგად ᲛᲧᲐᲠᲘ- ღია/დახურული პრინციპი და ლისკოვის ჩანაცვლების პრინციპი. შეგახსენებთ, რომ პირველი ამბობს, რომ კლასი არ უნდა შეიცვალოს ახალი ცვლილებების შესატანად, ხოლო მეორე ეხება მემკვიდრეობას და ვარაუდობს ზოგიერთი ძირითადი ტიპის მიღებული ტიპების უსაფრთხო გამოყენებას პროგრამის სწორად მუშაობაში ჩარევის გარეშე. რობერტ მარტინმა თავდაპირველად ჩამოაყალიბა ეს პრინციპი შემდეგი გზით:

1). უფრო მაღალ დონეზე მოდულები არ უნდა იყოს დამოკიდებული ქვედა დონის მოდულებზე. მოდულები ორივე დონეზე უნდა იყოს დამოკიდებული აბსტრაქციებზე.

2). აბსტრაქციები არ უნდა იყოს დამოკიდებული დეტალებზე. დეტალები უნდა იყოს დამოკიდებული აბსტრაქციებზე.

ანუ, კლასები უნდა განვითარდეს აბსტრაქციების კუთხით და არა მათი კონკრეტული განხორციელებით. და თუ დაიცავთ პრინციპებს OCPდა LSP, მაშინ სწორედ ამას მივაღწევთ. ასე რომ, მოდით, ცოტათი დავუბრუნდეთ გაკვეთილს. იქ, მაგალითად, განვიხილეთ კლასი ბარდი, რომელიც თავიდანვე მკაცრად იყო მიბმული კლასზე გიტარა, რომელიც წარმოადგენს კონკრეტულ მუსიკალურ ინსტრუმენტს:

საჯარო კლასის ბარდი ( კერძო გიტარა გიტარა; საჯარო ბარდი (გიტარა გიტარა) ( this.guitar = გიტარა; ) public void play() ( guitar.play(); ) )

საჯარო კლასი ბარდი (

პირადი გიტარა გიტარა;

საჯარო ბარდი (გიტარა გიტარა)

ეს. გიტარა = გიტარა;

საჯარო void თამაში ()

გიტარა თამაში ();

თუ გვინდოდა ამ კლასში სხვა მუსიკალური ინსტრუმენტების მხარდაჭერა, ამა თუ იმ გზით ამ კლასის შეცვლა მოგვიწევდა. ეს პრინციპის აშკარა დარღვევაა OCP. და შეიძლება უკვე შენიშნეთ, რომ ესეც პრინციპის დარღვევაა DIP, ვინაიდან ჩვენს შემთხვევაში ჩვენი აბსტრაქცია დეტალებზე იყო დამოკიდებული. ჩვენი კლასის შემდგომი გაფართოების თვალსაზრისით, ეს სულაც არ არის კარგი. რომ ჩვენი კლასი აკმაყოფილებდეს პრინციპის პირობებს OCPჩვენ დავამატეთ ინტერფეისი სისტემაში ინსტრუმენტი, რომელიც განხორციელდა კონკრეტული კლასების მიერ, რომლებიც წარმოადგენდნენ მუსიკალური ინსტრუმენტების ცალკეულ სახეებს.

ფაილი ინსტრუმენტი.java:

საჯარო ინტერფეისის ინსტრუმენტი ( void play();)

საჯარო ინტერფეისის ინსტრუმენტი (

void play();

ფაილი გიტარა.java:

class Guitar ახორციელებს Instrument( @Override public void play() ( System.out.println("Play Guitar!"); ) )

კლასის გიტარის ინსტრუმენტები (

@Override

საჯარო void თამაში ()

სისტემა. გარეთ . println ("ითამაშე გიტარა!");

ფაილი ლუტი.java:

საჯარო კლასის Lute ახორციელებს Instrument ( @Override public void play() ( System.out.println ("Play Lute!"); ) )

საჯარო კლასის ლაიტის ინსტრუმენტები (

@Override

საჯარო void თამაში ()

სისტემა. გარეთ . println ("ითამაშე ლაიტი!");

ამის შემდეგ კლასი შევცვალეთ ბარდი, რათა საჭიროების შემთხვევაში შევცვალოთ იმპლემენტაციები ზუსტად იმით, რაც გვჭირდება. ეს ქმნის დამატებით მოქნილობას შექმნილ სისტემაში და ამცირებს მის დაწყვილებას (კლასების ძლიერი დამოკიდებულება ერთმანეთზე).

საჯარო კლასი Bard ( კერძო საკრავის ინსტრუმენტი; საჯარო Bard() ( ) public void play() ( instrument.play(); ) public void setInstrument(ინსტრუმენტის ინსტრუმენტი) ( this.instrument = instrument; ) )

საჯარო კლასი ბარდი (

კერძო ინსტრუმენტული ინსტრუმენტი;

დამოკიდებულების ინვერსია არის პროგრამირების ერთ-ერთი ყველაზე მნიშვნელოვანი იდიომა. რუსულენოვან ინტერნეტში ამ იდიომის (პრინციპის) საოცრად ცოტა აღწერაა. ასე რომ, გადავწყვიტე ვცადო აღწერა. მაგალითებს გავაკეთებ ჯავაში, in ამ მომენტშიჩემთვის უფრო ადვილია, თუმცა დამოკიდებულების ინვერსიის პრინციპი ვრცელდება ნებისმიერ პროგრამირების ენაზე.

ეს აღწერა შემუშავდა ვლადიმერ მატვეევთან ერთად ჯავის შემსწავლელი სტუდენტების კლასებისთვის მოსამზადებლად.

სხვა სტატიები ამ სერიიდან:

ნება მომეცით დავიწყებ "დამოკიდებულების" განმარტებით. რა არის დამოკიდებულება? თუ თქვენი კოდი იყენებს ზოგიერთ კლასს შინაგანად ან აშკარად მოუწოდებს რომელიმე კლასის ან ფუნქციის სტატიკურ მეთოდს, ეს არის დამოკიდებულება. ნება მომეცით აგიხსნათ მაგალითებით:

ქვემოთ, კლასი A, მეთოდის შიგნით, სახელად someMethod(), აშკარად ქმნის B კლასის ობიექტს და უწოდებს მის მეთოდს someMethodOfB()

საჯარო კლასი A ( void someMethod() ( B b = new B(); b.someMethodOfB(); ) )

ანალოგიურად, მაგალითად, კლასი B აშკარად წვდება System კლასის სტატიკურ ველებსა და მეთოდებს:

საჯარო კლასი B ( void someMethodOfB() ( System.out.println ("Hello World"); ) )

ყველა შემთხვევაში, როდესაც რომელიმე კლასი (ტიპი A) დამოუკიდებლად ქმნის რომელიმე კლასს (ტიპი B) ან აშკარად წვდება სტატიკურ ველებს ან კლასის წევრებს, ეს ე.წ. სწორიდამოკიდებულება. იმათ. მნიშვნელოვანია: თუ კლასი თავის შიგნით მუშაობს სხვა კლასთან, ეს არის დამოკიდებულება. თუ ის ასევე ქმნის ამ კლასს თავის შიგნით, მაშინ ეს სწორიდამოკიდებულება.

რა არის ცუდი პირდაპირ დამოკიდებულებებში? პირდაპირი დამოკიდებულებები ცუდია, რადგან კლასი, რომელიც დამოუკიდებლად ქმნის სხვა კლასს თავის შიგნით, "მჭიდროდ" არის მიბმული ამ კლასთან. იმათ. თუ ცალსახად წერია, რომ B = new B(); , მაშინ კლასი A ყოველთვის იმუშავებს B კლასთან და არცერთ სხვა კლასთან. ან თუ წერია System.out.println("..."); მაშინ კლასი ყოველთვის გამოვა System.out და არსად სხვაგან.

მცირე კლასებისთვის, დამოკიდებულებები არ არის საშინელი. ეს კოდი შეიძლება საკმაოდ კარგად იმუშაოს. მაგრამ ზოგიერთ შემთხვევაში, იმისათვის, რომ თქვენმა A კლასმა შეძლოს უნივერსალურად მუშაობა სხვადასხვა კლასის გარემოში, შეიძლება მოითხოვოს კლასების სხვა დანერგვა - დამოკიდებულებები. იმათ. მაგალითად, დაგჭირდებათ არა კლასი B, არამედ სხვა კლასი იგივე ინტერფეისით, ან არა System.out, მაგრამ მაგალითად, გამომავალი ლოგერში (მაგალითად log4j).

პირდაპირი ურთიერთობა შეიძლება გრაფიკულად იყოს ნაჩვენები შემდეგნაირად:

იმათ. როდესაც თქვენ შექმნით A კლასს თქვენს კოდში: A a = new A(); ფაქტობრივად, იქმნება არა მხოლოდ ერთი კლასი A, არამედ დამოკიდებული კლასების მთელი იერარქია, რომლის მაგალითი მოცემულია ქვემოთ მოცემულ სურათზე. ეს იერარქია არის „ხისტი“: ცალკეული კლასების საწყისი კოდის შეცვლის გარეშე, თქვენ არ შეგიძლიათ შეცვალოთ რომელიმე კლასი იერარქიაში. ამიტომ, კლასი A ასეთ განხორციელებაში ცუდად ადაპტირდება ცვალებად გარემოსთან. სავარაუდოდ, ის არ შეიძლება გამოყენებულ იქნას სხვა კოდში, გარდა იმ კონკრეტული კოდისა, რომლისთვისაც თქვენ დაწერეთ.

A კლასის სპეციფიკური დამოკიდებულებისგან გამოსაყოფად გამოიყენეთ დამოკიდებულების ინექცია. რა არის დამოკიდებულების ინექცია? კოდში საჭირო კლასის აშკარად შექმნის ნაცვლად, დამოკიდებულებები გადაეცემა A კლასს კონსტრუქტორის მეშვეობით:

საჯარო კლასი A ( კერძო საბოლოო B b; საჯარო A(B b) ( this.b = b; ) საჯარო void someMethod() (b.someMethodOfB(); ) )

რომ. კლასი A ახლა იღებს თავის დამოკიდებულებას კონსტრუქტორის მეშვეობით. ახლა, A კლასის შესაქმნელად, ჯერ უნდა შექმნათ მისი დამოკიდებული კლასი. IN ამ შემთხვევაშიეს არის B:

B b = ახალი B(); A a = ახალი A(b); a.someMethod();

თუ ერთი და იგივე პროცედურა მეორდება ყველა კლასისთვის, ე.ი. გადასცეთ D კლასის ინსტანცია B კლასის კონსტრუქტორს, მისი დამოკიდებულებები E და F D კლასის კონსტრუქტორს და ა.შ., შემდეგ მიიღებთ კოდს, რომელშიც ყველა დამოკიდებულება იქმნება საპირისპირო თანმიმდევრობით:

G g = new G(); H h = ახალი H(); F f = ახალი (g,h); E e = ახალი E(); D d = ახალი D(e,f); B b = ახალი B(d); A a = ახალი A(b); a.someMethod();

ეს შეიძლება იყოს ნაჩვენები გრაფიკულად შემდეგნაირად:

თუ შეადარებთ 2 სურათს - ზემოთ მოცემულ სურათს პირდაპირი დამოკიდებულებით და მეორე სურათს დამოკიდებულების ინექციით - ხედავთ, რომ ისრების მიმართულება შეიცვალა საპირისპიროდ. ამ მიზეზით, იდიომს უწოდებენ დამოკიდებულებების „ინვერსიას“. სხვა სიტყვებით რომ ვთქვათ, დამოკიდებულების ინვერსია ნიშნავს, რომ კლასი დამოუკიდებლად არ ქმნის დამოკიდებულებებს, არამედ იღებს მათ შექმნილ ფორმაში კონსტრუქტორში (ან სხვაგვარად).

რატომ არის დამოკიდებულების ინვერსია კარგი? დამოკიდებულების ინვერსიით, შეგიძლიათ შეცვალოთ ყველა დამოკიდებულება კლასში მისი კოდის შეცვლის გარეშე. ეს ნიშნავს, რომ თქვენი კლასი A შეიძლება მოქნილად იყოს კონფიგურირებული სხვა პროგრამაში გამოსაყენებლად, გარდა იმ პროგრამისა, რომლისთვისაც იგი თავდაპირველად დაიწერა. რომ. დამოკიდებულების ინვერსიის პრინციპი (ზოგჯერ მას უწოდებენ დამოკიდებულების ინექციის პრინციპს) არის გასაღები მოქნილი, მოდულარული, მრავალჯერადი გამოყენების კოდის შესაქმნელად.

დამოკიდებულების ინექციის მინუსი ასევე ჩანს ერთი შეხედვით - კლასების ობიექტები, რომლებიც შექმნილია ამ ნიმუშის გამოყენებით, შრომატევადი კონსტრუქციაა. ამიტომ, დამოკიდებულების ინექცია ჩვეულებრივ გამოიყენება ზოგიერთ ბიბლიოთეკასთან ერთად, რომელიც შექმნილია ამ ამოცანის გასაადვილებლად. მაგალითად, ერთ-ერთი Google Guice ბიბლიოთეკა. Სმ. .

2 პასუხი

კარგი შეკითხვაა - სიტყვა ინვერსია გარკვეულწილად გასაკვირია (რადგან DIP-ის გამოყენების შემდეგ, ქვედა დონის დამოკიდებულების მოდული აშკარად არ არის დამოკიდებული აბონენტის მოდულზე უფრო მეტად მაღალი დონე: დამრეკი ან დამოკიდებული ახლა უფრო თავისუფლად არის დაკავშირებული დამატებითი აბსტრაქციის საშუალებით).

შეიძლება ვინმემ იკითხოს, რატომ ვიყენებ სიტყვას „ინვერსია“. სამართლიანობისთვის, ეს იმიტომ ხდება, რომ პროგრამული უზრუნველყოფის განვითარების უფრო ტრადიციული მეთოდები, როგორიცაა სტრუქტურირებული ანალიზი და დიზაინი, მიდრეკილია შექმნას პროგრამული სტრუქტურები, რომლებშიც მაღალი დონის მოდულები დამოკიდებულია დაბალი დონის მოდულებზე და რომლებშიც აბსტრაქციები დამოკიდებულია დეტალებზე. ფაქტობრივად, ამ მეთოდების ერთ-ერთი მიზანია განსაზღვროს სუბრუტინული იერარქია, რომელიც აღწერს, თუ როგორ ურეკავენ მაღალი დონის მოდულები დაბალი დონის მოდულებს... ამრიგად, კარგად შემუშავებული ობიექტზე ორიენტირებული პროგრამის დამოკიდებულების სტრუქტურა არის " ინვერსიული" დამოკიდებულების სტრუქტურასთან შედარებით, რომელიც, როგორც წესი, ტრადიციული პროცედურული მეთოდების შედეგია.

ერთი პუნქტი, რომელიც გასათვალისწინებელია ბიძია ბობის ნაშრომის DIP-ზე კითხვისას არის ის, რომ C++-ს არ აქვს (და წერის დროს, არ აქვს) ინტერფეისი, ამიტომ ამ აბსტრაქციის მიღწევა C++-ში ჩვეულებრივ ხორციელდება აბსტრაქტული/სუფთა ვირტუალური საბაზისო კლასის მეშვეობით, მაშინ როცა ჯავა ან C# აბსტრაქცია დაწყვილების გასახსნელად, როგორც წესი, წყდება ინტერფეისის დამოკიდებულებიდან აბსტრაქტით და უფრო მაღალი დონის მოდული(ებ)ის ინტერფეისთან დაწყვილებით.

რედაქტირებაუბრალოდ გასარკვევად:

"ერთ ადგილას მე ასევე ვხედავ, რომ მას უწოდებენ დამოკიდებულების ინვერსიას"

ინვერსია:დამოკიდებულების მართვის ინვერსია აპლიკაციიდან კონტეინერში (მაგ. Spring).

დამოკიდებულების ინექცია:

იმის ნაცვლად, რომ დაწეროთ ქარხნული შაბლონი, როგორ უნდა შეიყვანოთ ობიექტი პირდაპირ კლიენტის კლასში. მოდით, კლიენტის კლასმა მიმართოს ინტერფეისს და ჩვენ უნდა შევძლოთ კონკრეტული ტიპის შეყვანა კლიენტის კლასში. ამასთან, კლიენტის კლასს არ სჭირდება ახალი საკვანძო სიტყვის გამოყენება და სრულიად განცალკევებულია კონკრეტული კლასებისგან.

რაც შეეხება კონტროლის ინვერსიას (IoC)?

ტრადიციულ პროგრამირებაში ბიზნეს ლოგიკის ნაკადი განისაზღვრება ობიექტებით, რომლებიც სტატიკურად არიან მინიჭებული ერთმანეთს. კონტროლის ინვერსიით, ნაკადი დამოკიდებულია ობიექტის გრაფიკზე, რომელიც იქმნება ასამბლერის ინსტანციის მიერ და შესაძლებელი ხდება აბსტრაქციების საშუალებით განსაზღვრული ობიექტების ურთიერთქმედებით. სავალდებულო პროცესი მიიღწევა დამოკიდებულების ინექციით, თუმცა ზოგიერთი ამტკიცებს, რომ სერვისის ლოკატორის გამოყენება ასევე უზრუნველყოფს კონტროლის ინვერსიას.

კონტროლის ინვერსია, როგორც დიზაინის სახელმძღვანელო ემსახურება შემდეგ მიზნებს:

  • არსებობს კონკრეტული დავალების შესრულების გამოყოფა მისი განხორციელებისგან.
  • თითოეულ მოდულს შეუძლია ფოკუსირება მოახდინოს იმაზე, რის გაკეთებასაც აპირებს.
  • მოდულები არ აკეთებენ ვარაუდებს იმის შესახებ, თუ რას აკეთებენ სხვა სისტემები, მაგრამ ეყრდნობიან მათ კონტრაქტებს.
  • მოდულების შეცვლა არ მოქმედებს სხვა მოდულებზე.

მისაღებად დამატებითი ინფორმაციაშეხედე.

14 პასუხი

ძირითადად ნათქვამია:

  • აბსტრაქციები არასოდეს არ უნდა იყოს დამოკიდებული დეტალებზე. დეტალები უნდა იყოს დამოკიდებული აბსტრაქციებზე.

რაც შეეხება იმას, თუ რატომ არის ეს მნიშვნელოვანი, ერთი სიტყვით: ცვლილება სარისკოა და კონცეფციიდან გამომდინარე, ვიდრე განხორციელება, თქვენ ამცირებთ ცვლილების საჭიროებას ზარის საიტებზე.

ეფექტურად, DIP ამცირებს დაწყვილებას კოდის სხვადასხვა ნაწილებს შორის. იდეა იმაში მდგომარეობს, რომ მიუხედავად იმისა, რომ არსებობს მრავალი გზა, მაგალითად, ლოგის განსახორციელებლად, მისი გამოყენება დროთა განმავლობაში შედარებით სტაბილური უნდა იყოს. თუ თქვენ შეგიძლიათ ამოიღოთ ინტერფეისი, რომელიც წარმოადგენს ჟურნალის კონცეფციას, ეს ინტერფეისი დროთა განმავლობაში ბევრად უფრო სტაბილური უნდა იყოს, ვიდრე მისი განხორციელება, და გამოძახების საიტები გაცილებით ნაკლებად მგრძნობიარე უნდა იყოს იმ ცვლილებების მიმართ, რომლებიც შეიძლება შეიტანოთ ჟურნალის ამ მექანიზმის შენარჩუნებით ან გაფართოებით.

იმის გამო, რომ დანერგვა სპეციფიკურია ინტერფეისით, თქვენ გაქვთ შესაძლებლობა აირჩიოთ გაშვების დროს რომელი დანერგვა საუკეთესოდ შეეფერება თქვენს კონკრეტულ გარემოს. შემთხვევიდან გამომდინარე, ეს ასევე შეიძლება იყოს საინტერესო.

წიგნები Agile Software Development, Principles, Patterns and Practices და Agile Principles, Patterns and Practices in C# არის საუკეთესო რესურსები ორიგინალური მიზნებისა და მოტივაციის სრულად გასაგებად დამოკიდებულების ინვერსიის პრინციპის უკან. სტატია "დამოკიდებულების შებრუნების პრინციპი" ასევე კარგი რესურსია, მაგრამ იმის გამო, რომ ეს არის პროექტის შედედებული ვერსია, რომელიც საბოლოოდ დასრულდა ზემოთ აღნიშნულ წიგნებში, ის ტოვებს რამდენიმე მნიშვნელოვან დისკუსიას პაკეტის მფლობელობის კონცეფციის შესახებ. და ინტერფეისები, რომლებიც საკვანძოა ეს პრინციპი განსხვავდება უფრო ზოგადი რჩევებისაგან "ინტერფეისის პროგრამისთვის და არა განხორციელებისთვის", ნაპოვნი წიგნში Design Patterns (Gamma, et al.).

მოკლედ რომ ვთქვათ, დამოკიდებულების ინვერსიის პრინციპი უპირველეს ყოვლისა მიზნად ისახავს შეცვლატრადიციულად დამოკიდებულებების არხირება "უმაღლესი დონის" კომპონენტებიდან "ქვედა დონის" კომპონენტებზე, ისე, რომ "ქვედა დონის" კომპონენტები დამოკიდებულია ინტერფეისებზე, კუთვნილება"უმაღლესი დონის" კომპონენტებზე (შენიშვნა: "უმაღლესი დონის" კომპონენტი აქ ეხება კომპონენტს, რომელიც მოითხოვს გარე დამოკიდებულებებს/მომსახურებებს და არა აუცილებლად მის კონცეპტუალურ პოზიციას ფენიანი არქიტექტურაში.) თუმცა, ურთიერთობა არ არის მცირდებარამდენიც ის ცვლისკომპონენტებიდან, რომლებიც თეორიულად ნაკლებად ღირებულია, თეორიულად უფრო ღირებული კომპონენტებისკენ.

ეს მიიღწევა კომპონენტების შემუშავებით, რომელთა გარე დამოკიდებულებები გამოიხატება როგორც ინტერფეისი, რისთვისაც კომპონენტის მომხმარებელმა უნდა უზრუნველყოს განხორციელება. სხვა სიტყვებით რომ ვთქვათ, გარკვეული ინტერფეისები გამოხატავს იმას, რაც კომპონენტს სჭირდება და არა იმას, თუ როგორ იყენებთ კომპონენტს (მაგალითად, "INeedSomething" და არა "IDoSomething").

ის, რასაც დამოკიდებულების ინვერსიის პრინციპი არ ეხება, არის დამოკიდებულებების აბსტრაქციის მარტივი პრაქტიკა ინტერფეისების გამოყენებით (მაგ. MyService -> ). მიუხედავად იმისა, რომ ეს აშორებს კომპონენტს დამოკიდებულების კონკრეტული განხორციელების დეტალისგან, ის არ აბრუნებს ურთიერთობას მომხმარებელსა და დამოკიდებულებას შორის (მაგალითად, ⇐ Logger.

დამოკიდებულების ინვერსიის პრინციპის მნიშვნელობა შეიძლება ჩამოყალიბდეს ერთ მიზანზე - პროგრამული უზრუნველყოფის კომპონენტების ხელახლა გამოყენების შესაძლებლობა, რომლებიც ეყრდნობიან გარე დამოკიდებულებებს მათი ფუნქციონირების ნაწილისთვის (რეგისტრაცია, გადამოწმება და ა.შ.).

ხელახალი გამოყენების ამ ზოგადი მიზნის ფარგლებში, ჩვენ შეგვიძლია განვასხვავოთ ხელახალი გამოყენების ორი ქვეტიპი:

    პროგრამული კომპონენტის გამოყენება მრავალ აპლიკაციაში დამოკიდებულების განხორციელებით (მაგალითად, თქვენ შექმენით DI კონტეინერი და გსურთ შესთავაზოთ ჟურნალი, მაგრამ არ გსურთ თქვენი კონტეინერის მიბმა კონკრეტულ ლოგერთან, ამიტომ ყველამ, ვინც თქვენს კონტეინერს იყენებს, ასევე უნდა გამოიყენოს ჟურნალი ბიბლიოთეკა თქვენ ირჩევთ).

    პროგრამული უზრუნველყოფის კომპონენტების გამოყენება განვითარებად კონტექსტში (მაგალითად, თქვენ შექმენით ბიზნეს ლოგიკის კომპონენტები, რომლებიც იგივე რჩება აპლიკაციის სხვადასხვა ვერსიებში, სადაც განხორციელების დეტალები ვითარდება).

კომპონენტების ხელახლა გამოყენების პირველ შემთხვევაში მრავალ აპლიკაციაში, როგორიცაა ინფრასტრუქტურის ბიბლიოთეკა, მიზანია მიაწოდოს მომხმარებლებს ძირითადი ინფრასტრუქტურა თქვენი მომხმარებლების საკუთარი ბიბლიოთეკის დამოკიდებულებებთან მიბმის გარეშე, რადგან ასეთი დამოკიდებულებებიდან დამოკიდებულების მიღება მოითხოვს მომხმარებლებს ასევე მოითხოვონ იგივე დამოკიდებულებები. ეს შეიძლება იყოს პრობლემური, როდესაც თქვენი ბიბლიოთეკის მომხმარებლები გადაწყვეტენ გამოიყენონ სხვა ბიბლიოთეკა იმავე ინფრასტრუქტურის საჭიროებებისთვის (როგორიცაა NLog და log4net), ან თუ ისინი გადაწყვეტენ გამოიყენონ საჭირო ბიბლიოთეკის უფრო გვიანდელი ვერსია, რომელიც არ არის თავსებადი საჭირო ვერსიასთან. თქვენი ბიბლიოთეკის მიერ.

ბიზნეს ლოგიკის კომპონენტების ხელახლა გამოყენების მეორე შემთხვევაში (მაგ., „უმაღლესი დონის კომპონენტები“), მიზანია აპლიკაციის იმპლემენტაციის იზოლირება ძირითადი მასშტაბით თქვენი დანერგვის დეტალების ცვალებად მოთხოვნილებებისგან (მაგ. მუდმივი ბიბლიოთეკების შეცვლა/განახლება, ბიბლიოთეკების შეტყობინებების გაცვლა) . დაშიფვრის სტრატეგიები და ა.შ.). იდეალურ შემთხვევაში, განაცხადის განხორციელების დეტალების შეცვლამ არ უნდა დაარღვიოს ის კომპონენტები, რომლებიც ასახავს აპლიკაციის ბიზნეს ლოგიკას.

Შენიშვნა. ზოგიერთმა შეიძლება გააპროტესტა ამ მეორე შემთხვევის, როგორც ფაქტობრივი ხელახალი გამოყენების აღწერა, მიაჩნია, რომ კომპონენტები, როგორიცაა ბიზნეს ლოგიკის კომპონენტები, რომლებიც გამოიყენება ერთ განვითარებად აპლიკაციაში, მხოლოდ ერთ გამოყენებას წარმოადგენს. თუმცა, იდეა აქ არის ის, რომ აპლიკაციის დანერგვის დეტალების ყოველი ცვლილება წარმოადგენს ახალ კონტექსტს და, შესაბამისად, გამოყენების განსხვავებულ შემთხვევას, თუმცა საბოლოო მიზნები შეიძლება განვასხვავოთ, როგორც იზოლაცია და პორტაბელურობა.

მიუხედავად იმისა, რომ მეორე შემთხვევაში დამოკიდებულების ინვერსიის პრინციპის დაცვას შეიძლება ჰქონდეს გარკვეული სარგებელი, უნდა აღინიშნოს, რომ მისი მნიშვნელობა, როგორც გამოიყენება თანამედროვე ენებზე, როგორიცაა Java და C#, მნიშვნელოვნად შემცირდა, შესაძლოა იმ დონემდე, რომ ეს შეუსაბამოა. როგორც ადრე განვიხილეთ, DIP მოიცავს განხორციელების დეტალების სრულ გამოყოფას ცალკეულ პაკეტებად. თუმცა, განვითარებადი აპლიკაციის შემთხვევაში, ბიზნეს დომენის ტერმინებით განსაზღვრული ინტერფეისების უბრალოდ გამოყენება დაიცავს უფრო მაღალი დონის კომპონენტების ცვლილების საჭიროებისგან განხორციელების დეტალების კომპონენტების ცვალებადობის გამო, მაშინაც კი, თუ განხორციელების დეტალები საბოლოოდ იმავე პაკეტშია. . პრინციპის ეს ნაწილი ასახავს ასპექტებს, რომლებიც შესაბამისი იყო ენისთვის მისი კოდიფიკაციის დროს (მაგალითად, C++), რომლებიც არ არის რელევანტური უფრო ახალი ენებისთვის. თუმცა, დამოკიდებულების ინვერსიის პრინციპის მნიშვნელობა, პირველ რიგში, დაკავშირებულია მრავალჯერადი გამოყენების პროგრამული კომპონენტების/ბიბლიოთეკების შემუშავებასთან.

ამ პრინციპის უფრო დეტალური განხილვა, როგორც ეს ეხება ადვილად გამოსაყენებელიშეგიძლიათ იპოვოთ ინტერფეისები, დამოკიდებულების ინექცია და გაყოფილი ინტერფეისის ნიმუში.

როდესაც ჩვენ ვამუშავებთ პროგრამულ აპლიკაციებს, შეგვიძლია განვიხილოთ დაბალი დონის კლასები - კლასები, რომლებიც ახორციელებენ ძირითად და პირველად ოპერაციებს (დისკზე წვდომა, ქსელის პროტოკოლები და ა. .

ეს უკანასკნელი ეყრდნობა დაბალი დონის კლასებს. ასეთი სტრუქტურების განხორციელების ბუნებრივი გზა იქნება დაბალი დონის კლასების დაწერა და ყოველთვის, როცა იძულებულნი ვართ დავწეროთ რთული მაღალი დონის კლასები. ვინაიდან მაღალი დონის კლასები განისაზღვრება სხვების პერსპექტივიდან, როგორც ჩანს, ეს არის ლოგიკური გზა ამის გასაკეთებლად. მაგრამ ეს არ არის საპასუხო დიზაინი. რა მოხდება, თუ დაბალი დონის კლასის შეცვლა დაგვჭირდება?

დამოკიდებულების ინვერსიის პრინციპი ამბობს, რომ:

  • მაღალი დონის მოდულები არ უნდა იყოს დამოკიდებული დაბალი დონის მოდულებზე. ორივე უნდა იყოს დამოკიდებული აბსტრაქციებზე.

ეს პრინციპი მიზნად ისახავს „შეატრიალოს“ ჩვეულებრივი იდეა, რომელშიც მაღალი დონის მოდულებია პროგრამული უზრუნველყოფაუნდა იყოს დამოკიდებული ქვედა დონის მოდულებზე. აქ, მაღალი დონის მოდულები ფლობენ აბსტრაქციას (მაგალითად, ინტერფეისის მეთოდების გადაწყვეტა), რომლებიც განხორციელებულია ქვედა დონის მოდულების მიერ. ამრიგად, ქვედა დონის მოდულები დამოკიდებულია უფრო მაღალი დონის მოდულებზე.

დამოკიდებულების ინვერსიის ეფექტური გამოყენება უზრუნველყოფს მოქნილობას და სტაბილურობას თქვენი აპლიკაციის არქიტექტურაში. ეს საშუალებას მისცემს თქვენს აპლიკაციას უფრო უსაფრთხოდ და სტაბილურად განვითარდეს.

ტრადიციული ფენიანი არქიტექტურა

ტრადიციულად, ფენიანი არქიტექტურის მომხმარებლის ინტერფეისი დამოკიდებული იყო ბიზნეს ფენაზე, რაც, თავის მხრივ, დამოკიდებული იყო მონაცემთა წვდომის ფენაზე.

თქვენ უნდა გესმოდეთ ფენა, პაკეტი ან ბიბლიოთეკა. ვნახოთ, როგორ მიდის კოდი.

ჩვენ გვექნებოდა ბიბლიოთეკა ან პაკეტი მონაცემთა წვდომის ფენისთვის.

// DataAccessLayer.dll საჯარო კლასი ProductDAO ( )

// BusinessLogicLayer.dll DataAccessLayer-ის გამოყენებით; საჯარო კლასი ProductBO ( კერძო ProductDAO productDAO; )

ფენიანი არქიტექტურა დამოკიდებულების ინვერსიით

დამოკიდებულების ინვერსია მიუთითებს შემდეგზე:

მაღალი დონის მოდულები არ უნდა იყოს დამოკიდებული დაბალი დონის მოდულებზე. ორივე უნდა იყოს დამოკიდებული აბსტრაქციებზე.

აბსტრაქციები არ უნდა იყოს დამოკიდებული დეტალებზე. დეტალები უნდა იყოს დამოკიდებული აბსტრაქციებზე.

რა არის მაღალი და დაბალი დონის მოდულები? ფიქრის მოდულებზე, როგორიცაა ბიბლიოთეკები ან პაკეტები, მაღალი დონის მოდულები იქნება ის, რომლებსაც ტრადიციულად აქვთ დამოკიდებულებები და დაბალი დონის მოდულები, რომლებზეც ისინი დამოკიდებულნი არიან.

სხვა სიტყვებით რომ ვთქვათ, მოდულის მაღალი დონე იქნება მოქმედების გამოძახება და დაბალი დონე, სადაც მოქმედება შესრულდება.

ამ პრინციპიდან შეიძლება გონივრული დასკვნის გამოტანა: კვანძებს შორის არ უნდა არსებობდეს დამოკიდებულება, არამედ აბსტრაქციაზე. მაგრამ ჩვენი მიდგომის მიხედვით, ჩვენ შეიძლება არასწორად ვიყენებთ ინვესტიციის დამოკიდებულებას, მაგრამ ეს აბსტრაქციაა.

წარმოიდგინეთ, რომ ჩვენს კოდს ასე მოვირგებთ:

ჩვენ გვექნებოდა ბიბლიოთეკა ან პაკეტი მონაცემთა წვდომის ფენისთვის, რომელიც განსაზღვრავს აბსტრაქციას.

// DataAccessLayer.dll საჯარო ინტერფეისი IProductDAO საჯარო კლასი ProductDAO: IProductDAO( )

და სხვა ბიბლიოთეკის ან პაკეტის დონის ბიზნეს ლოგიკა, რომელიც დამოკიდებულია მონაცემთა წვდომის ფენაზე.

// BusinessLogicLayer.dll DataAccessLayer-ის გამოყენებით; საჯარო კლასი ProductBO ( კერძო IProductDAO productDAO; )

მიუხედავად იმისა, რომ ჩვენ დამოკიდებულნი ვართ აბსტრაქციაზე, ურთიერთობა ბიზნესსა და მონაცემთა წვდომას შორის იგივე რჩება.

დამოკიდებულების ინვერსიის მისაღწევად, მდგრადობის ინტერფეისი უნდა განისაზღვროს მოდულში ან პაკეტში, სადაც მდებარეობს მაღალი დონის ლოგიკა ან დომენი და არა დაბალი დონის მოდულში.

ჯერ განსაზღვრეთ რა არის დომენის ფენა და მისი კომუნიკაციის აბსტრაქცია განისაზღვრება მდგრადობით.

// Domain.dll საჯარო ინტერფეისი IProductRepository; DataAccessLayer-ის გამოყენებით; საჯარო კლასი ProductBO ( კერძო IProductRepository productRepository; )

მას შემდეგ, რაც მდგრადობის დონე დომენზეა დამოკიდებული, ახლა უკვე შესაძლებელია ინვერსია, თუ დამოკიდებულების განსაზღვრა მოხდება.

// Persistence.dll საჯარო კლასი ProductDAO: IPProductRepository( )

პრინციპის გაღრმავება

მნიშვნელოვანია კონცეფციის კარგად გაგება, მიზნისა და სარგებლის გაღრმავება. თუ დავრჩებით მექანიკაში და შევისწავლით ტიპურ საცავს, ვერ განვსაზღვრავთ, სად შეგვიძლია გამოვიყენოთ დამოკიდებულების პრინციპი.

მაგრამ რატომ ვაბრუნებთ დამოკიდებულებას? რა არის მთავარი მიზანი კონკრეტული მაგალითების მიღმა?

ეს ჩვეულებრივ საშუალებას აძლევს ყველაზე სტაბილურ ნივთებს, რომლებიც დამოუკიდებელია ნაკლებად სტაბილური საგნებისგან, უფრო ხშირად შეიცვალოს.

უფრო ადვილია შეცვალოთ გამძლეობის ტიპი, ან მონაცემთა ბაზა ან ტექნოლოგია იმავე მონაცემთა ბაზაში წვდომისთვის, ვიდრე დომენის ლოგიკა ან მოქმედებები, რომლებიც შექმნილია მდგრადობასთან კომუნიკაციისთვის. ეს იწვევს დამოკიდებულების შეცვლას, რადგან უფრო ადვილია გამძლეობის შეცვლა, თუ ეს ცვლილება მოხდება. ამ გზით ჩვენ არ მოგვიწევს დომენის შეცვლა. დომენის ფენა ყველაზე სტაბილურია, ამიტომ არაფერზე არ უნდა იყოს დამოკიდებული.

მაგრამ არსებობს მეტი, ვიდრე უბრალოდ შენახვის მაგალითი. არსებობს მრავალი სცენარი, რომლებშიც ეს პრინციპი გამოიყენება და არსებობს ამ პრინციპზე დაფუძნებული არქიტექტურები.

არქიტექტურა

არსებობს არქიტექტურები, რომლებშიც დამოკიდებულების ინვერსია არის მისი განმარტების გასაღები. ყველა დომენში ეს ყველაზე მნიშვნელოვანია და ეს არის აბსტრაქციები, რომლებიც განსაზღვრავს კომუნიკაციის პროტოკოლს დომენსა და დანარჩენ პაკეტებსა თუ ბიბლიოთეკებს შორის.

სუფთა არქიტექტურა

ჩემთვის ოფიციალურ სტატიაში აღწერილი დამოკიდებულების ინვერსიის პრინციპი

C++-ის პრობლემა ის არის, რომ სათაურის ფაილები ჩვეულებრივ შეიცავს პირადი ველების და მეთოდების დეკლარაციას. ასე რომ, თუ მაღალი დონის C++ მოდული შეიცავს სათაურის ფაილს დაბალი დონის მოდულისთვის, ეს დამოკიდებული იქნება რეალურზე განხორციელებაამ მოდულის დეტალები. და ეს აშკარად არ არის ძალიან კარგი. მაგრამ ეს არ არის პრობლემა უფრო თანამედროვე ენებში, რომლებიც დღეს ჩვეულებრივ გამოიყენება.

მაღალი დონის მოდულები არსებითად ნაკლებად გამოსაყენებელია, ვიდრე დაბალი დონის მოდულები, რადგან პირველი, როგორც წესი, უფრო აპლიკაციის/კონტექსტის სპეციფიკურია, ვიდრე ეს უკანასკნელი. მაგალითად, კომპონენტი, რომელიც ახორციელებს UI ეკრანს, არის უმაღლესი დონის და ასევე ძალიან (მთლიანად?) აპლიკაციის სპეციფიკური. ამგვარი კომპონენტის სხვა აპლიკაციაში ხელახლა გამოყენების მცდელობა კონტრპროდუქტიულია და შეიძლება გამოიწვიოს მხოლოდ გადაჭარბებული განვითარება.

ამრიგად, A კომპონენტის იმავე დონეზე ცალკეული აბსტრაქციის შექმნა, რომელიც დამოკიდებულია B კომპონენტზე (რომელიც არ არის დამოკიდებული A-ზე) შეიძლება გაკეთდეს მხოლოდ იმ შემთხვევაში, თუ კომპონენტი A რეალურად იქნება გამოსადეგი სხვადასხვა აპლიკაციებში ან კონტექსტში ხელახლა გამოყენებისთვის. თუ ეს ასე არ არის, მაშინ DIP აპლიკაცია ცუდი დიზაინი იქნება.

დამოკიდებულების ინვერსიის პრინციპის ჩამოყალიბების უფრო მკაფიო გზა:

თქვენი მოდულები, რომლებიც შეიცავს კომპლექსურ ბიზნეს ლოგიკას, პირდაპირ არ უნდა იყოს დამოკიდებული სხვა მოდულებზე, რომლებიც აერთიანებს ბიზნეს ლოგიკას. ამის ნაცვლად, ისინი მხოლოდ მარტივი მონაცემების ინტერფეისებზე უნდა იყვნენ დამოკიდებული.

ანუ, იმის ნაცვლად, რომ განახორციელოთ თქვენი Logic კლასი, როგორც ამას ჩვეულებრივ აკეთებენ ადამიანები:

კლასის დამოკიდებულების ( ... ) კლასის Logic ( private Dependency dep; int doSomething () ( // ბიზნეს ლოგიკის გამოყენებით dep აქ ) )

თქვენ უნდა გააკეთოთ მსგავსი რამ:

Class Dependency ( ... ) ინტერფეისი Data ( ... ) class DataFromDependency ახორციელებს Data ( private Dependency dep; ... ) class Logic ( int doSomething (მონაცემთა მონაცემები) ( // გამოთვლა რაღაც მონაცემებით ) )

Data და DataFromDependency უნდა ცხოვრობდეს იმავე მოდულში, როგორც Logic და არა დამოკიდებულებით.

Რატომ არის ეს?

კარგი პასუხები და კარგი მაგალითებიუკვე სხვებმა აქ.

დამოკიდებულების ინვერსიის მიზანია პროგრამული უზრუნველყოფის ხელახალი გამოყენება.

იდეა იმაში მდგომარეობს, რომ კოდის ორი ნაწილის ნაცვლად, რომლებიც ერთმანეთს ეყრდნობა, ისინი ეყრდნობიან რაღაც აბსტრაქტულ ინტერფეისს. შემდეგ შეგიძლიათ ხელახლა გამოიყენოთ ნებისმიერი ნაწილი მეორის გარეშე.

ეს ჩვეულებრივ მიიღწევა საკონტროლო კონტეინერის (IoC) ინვერსიით, როგორიცაა Spring Java-ში. ამ მოდელში ობიექტების თვისებები დაყენებულია XML კონფიგურაციის მეშვეობით, ვიდრე ობიექტების გასვლა და მათი დამოკიდებულების პოვნა.

წარმოიდგინეთ ეს ფსევდოკოდი...

საჯარო კლასი MyClass ( საჯარო სერვისი myService = ServiceLocator.service; )

MyClass პირდაპირ დამოკიდებულია სერვისის კლასზე და ServiceLocator კლასზე. ეს საჭიროა ორივესთვის, თუ გსურთ მისი გამოყენება სხვა აპლიკაციაში. ახლა წარმოიდგინე ეს...

საჯარო კლასი MyClass ( საჯარო IService myService; )

MyClass ახლა იყენებს ერთ ინტერფეისს, IService ინტერფეისს. ჩვენ დავუშვებთ IoC კონტეინერს რეალურად დააყენოს ამ ცვლადის მნიშვნელობა.

იყოს სასტუმრო, რომელიც სურსათის მწარმოებელს სთხოვს მის მარაგს. სასტუმრო საკვების (ვთქვათ ქათმის) სახელს ანიჭებს საკვების გენერატორს და გენერატორი უბრუნებს მოთხოვნილ საკვებს სასტუმროს. მაგრამ სასტუმროს არ აინტერესებს საკვების ტიპი, რომელსაც ის იღებს და ემსახურება. ამგვარად, გენერატორი აწვდის სასტუმროს საკვებს, სახელწოდებით „საკვები“.

ეს განხორციელება JAVA-ში

FactoryClass ქარხნული მეთოდით. კვების გენერატორი

საჯარო კლასის FoodGenerator ( საკვები საკვები; საჯარო საკვები getFood(სტრიქონის სახელი)( if(name.equals("fish"))( food = new Fish(); )else if(name.equals("ქათამი"))(საჭმელი = ახალი ქათამი (); )სხვა საკვები = null; დაბრუნება საკვები; ))

კლასის ანოტაცია/ინტერფეისი

საჯარო აბსტრაქტული კლასი Food ( //ბავშვთა კლასიდან არცერთი არ გადალახავს ამ მეთოდს ხარისხის უზრუნველსაყოფად... public void quality())( String fresh = "ეს არის ახალი " + getName(); String tasty = "ეს არის გემრიელი " + getName(); System.out.println(ახალი); System.out.println(გემრიელი); ) საჯარო აბსტრაქტული სტრიქონი getName();)

ქათამი ყიდის საკვებს (ბეტონის კლასი)

Public class Chicken extensions Food ( /* ყველა სახის საკვები უნდა იყოს ახალი და გემრიელი, ასე რომ * ისინი არ გადალახავს სუპერ კლასის მეთოდს "property()"*/ public String getName())( დააბრუნებს "Chicken"; ))

თევზი ყიდის საკვებს (სპეციფიკური კლასი)

Public class Fish extensions Food ( /* ყველა სახის საკვები უნდა იყოს ახალი და გემრიელი, ასე რომ * ისინი არ გადააჭარბებენ სუპერ კლასის მეთოდს "property()"*/ public String getName())( return "Fish"; ))

ბოლოს და ბოლოს

Სასტუმრო

საჯარო კლასის სასტუმრო ( public static void main(String args)( //Factory class გამოყენება.... FoodGenerator foodGenerator = new FoodGenerator(); //ქარხნული მეთოდი საკვების ინსტანციაციისთვის... Food food = foodGenerator.getFood( "ქათამი"); საკვები.ხარისხი(); ) )

როგორც ხედავთ, სასტუმრომ არ იცის, ქათამია თუ თევზი. ცნობილია მხოლოდ ის, რომ ეს არის საკვები პროდუქტი, ე.ი. სასტუმრო დამოკიდებულია კვების კლასზე.

თქვენ ასევე შეიძლება შეამჩნიოთ, რომ თევზი და ქათამი კლასი ახორციელებს საკვების კლასს და პირდაპირ არ არის დაკავშირებული სასტუმროსთან. იმათ. ქათამი და თევზი ასევე დამოკიდებულია კვების კლასზე.

ეს ნიშნავს, რომ მაღალი დონის კომპონენტი (სასტუმრო) და დაბალი დონის კომპონენტი (თევზი და ქათამი) დამოკიდებულია აბსტრაქციაზე (საჭმელზე).

ამას ჰქვია დამოკიდებულების ინვერსია.

დამოკიდებულების ინვერსიის პრინციპი (DIP) ამბობს, რომ

ი) მაღალი დონის მოდულები არ უნდა იყოს დამოკიდებული დაბალი დონის მოდულებზე. ორივე უნდა იყოს დამოკიდებული აბსტრაქციებზე.

ii) აბსტრაქციები არასოდეს არ უნდა იყოს დამოკიდებული დეტალებზე. დეტალები უნდა იყოს დამოკიდებული აბსტრაქციებზე.

საჯარო ინტერფეისი ICustomer ( string GetCustomerNameById(int id); ) public class Customer: ICustomer ( //ctor public Customer()() public string GetCustomerNameById(int id) ( return "Dummy Customer Name"; ) ) public class CustomerFactory ( public static ICustomer GetCustomerData() ( return new Customer(); ) ) public class CustomerBLL ( ICustomer _customer; public CustomerBLL() ( _customer = CustomerFactory.GetCustomerData(); ) საჯარო სტრიქონი GetCustomerNameById(int id.N.idmeCus); ) ) საჯარო კლასის პროგრამა ( static void Main() ( CustomerBLL customerBLL = new CustomerBLL(); int customerId = 25; string customerName = customerBLL.GetCustomerNameById(customerId); Console.WriteLine(customerName); Console);()Key

Შენიშვნა. კლასი უნდა იყოს დამოკიდებული აბსტრაქციებზე, როგორიცაა ინტერფეისი ან აბსტრაქტული კლასები, ვიდრე კონკრეტულ დეტალებზე (ინტერფეისის განხორციელება).

გაზიარება

ბოლო განახლება: 03/11/2016

დამოკიდებულების ინვერსიის პრინციპიდამოკიდებულების ინვერსიის პრინციპი გამოიყენება თავისუფლად დაწყვილებული ერთეულების შესაქმნელად, რომელთა გამოცდა, შეცვლა და განახლება მარტივია. ეს პრინციპი შეიძლება ჩამოყალიბდეს შემდეგნაირად:

უმაღლესი დონის მოდულები არ უნდა იყოს დამოკიდებული ქვედა დონის მოდულებზე. ორივე უნდა იყოს დამოკიდებული აბსტრაქციებზე.

აბსტრაქციები არ უნდა იყოს დამოკიდებული დეტალებზე. დეტალები უნდა იყოს დამოკიდებული აბსტრაქციებზე.

პრინციპის გასაგებად, განიხილეთ შემდეგი მაგალითი:

Class Book ( public string Text ( get; set; ) public ConsolePrinter Printer ( get; set; ) public void Print() ( Printer.Print(Text); ) ) class ConsolePrinter ( public void Print (string text) ( Console.WriteLine (ტექსტი); ))

Book კლასი, რომელიც წარმოადგენს წიგნს, იყენებს ConsolePrinter კლასს დასაბეჭდად. ამ განმარტებით, Book კლასი დამოკიდებულია ConsolePrinter კლასზე. უფრო მეტიც, ჩვენ მკაცრად განვსაზღვრეთ, რომ წიგნის დაბეჭდვა შესაძლებელია მხოლოდ კონსოლზე ConsolePrinter კლასის გამოყენებით. სხვა ვარიანტები, მაგალითად, პრინტერზე გამოტანა, ფაილში გამოტანა ან გრაფიკული ინტერფეისის ელემენტების გამოყენება - ეს ყველაფერი ამ შემთხვევაში გამორიცხულია. წიგნის ბეჭდვის აბსტრაქცია არ არის გამოყოფილი ConsolePrinter კლასის დეტალებისგან. ეს ყველაფერი დამოკიდებულების ინვერსიის პრინციპის დარღვევაა.

ახლა შევეცადოთ ჩვენი კლასები მივიყვანოთ დამოკიდებულების ინვერსიის პრინციპთან, გამოვყოთ აბსტრაქციები დაბალი დონის განხორციელებისგან:

ინტერფეისი IPrinter ( void Print(string text); ) class Book ( public string Text (get; set; ) public IPrinter Printer (get; set; ) public Book (IPrinter printer) ( this.Printer = printer; ) public void Print( ) ( Printer.Print(Text); ) ) class ConsolePrinter: IPrinter ( public void Print(string text) ( Console.WriteLine("Print to console"); ) ) class HtmlPrinter: IPrinter ( public void Print(string text) ( Console.WriteLine ("ბეჭდვა html-ში"); ) )

წიგნის ბეჭდვის აბსტრაქცია ახლა გამოყოფილია კონკრეტული განხორციელებისაგან. შედეგად, როგორც Book კლასი, ასევე ConsolePrinter კლასი დამოკიდებულია IPrinter აბსტრაქციაზე. გარდა ამისა, ახლა ჩვენ ასევე შეგვიძლია შევქმნათ IPrinter აბსტრაქციის დამატებითი დაბალი დონის იმპლემენტაციები და დინამიურად გამოვიყენოთ ისინი პროგრამაში:

Book book = new Book(new ConsolePrinter()); წიგნი.ბეჭდვა(); book.Printer = new HtmlPrinter(); წიგნი.ბეჭდვა();