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

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


ოპერაციის აღმნიშვნელი მიმაგრებული ობიექტის გამო x, არგუმენტის გადატანა არგ(მრავალი არგუმენტი შესაძლებელია, ან საერთოდ არ არსებობს). Smalltalk პროგრამისტები ამ შემთხვევაში საუბრობენ "ობიექტზე გადასვლაზე" xშეტყობინებები არგუმენტით არგ”, მაგრამ ეს მხოლოდ განსხვავებაა ტერმინოლოგიაში და, შესაბამისად, უმნიშვნელოა.

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

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

განმარტება: ტიპის დარღვევა

გაშვების ტიპის დარღვევა, ან უბრალოდ ტიპის დარღვევა მოკლედ, ხდება ზარის დროს x.f (არგ), სად xმიმაგრებულია ობიექტზე OBJთუ რომელიმე:

[x]არ არსებობს შესაბამისი კომპონენტი და გამოიყენება OBJ,

[x]არის ასეთი კომპონენტი, თუმცა, არგუმენტი არგმისთვის მიუღებელია.

აკრეფის პრობლემა არის მსგავსი სიტუაციების თავიდან აცილება:

OO სისტემების ტიპიფიკაციის პრობლემა

როდის აღმოვაჩენთ, რომ ტიპის დარღვევა შეიძლება მოხდეს OO სისტემის შესრულებისას?

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

სტატიკური და დინამიური აკრეფა

მიუხედავად იმისა, რომ შუალედური ვარიანტებია შესაძლებელი, აქ ორი ძირითადი მიდგომაა წარმოდგენილი:

[x] დინამიური აკრეფა: დაელოდეთ თითოეული ზარის დასრულების მომენტს და შემდეგ მიიღეთ გადაწყვეტილება.

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

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

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

განმარტება: სტატიკურად აკრეფილი ენა

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

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

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

აკრეფის წესები

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

[x]თითოეული ერთეულის ან ფუნქციის გამოცხადებისას, მისი ტიპი უნდა იყოს მითითებული, მაგალითად, acc: ანგარიში... თითოეულ ქვეჯგუფს აქვს 0 ან მეტი ოფიციალური არგუმენტი, რომელთა ტიპი უნდა იყოს მითითებული, მაგალითად: დააყენა (x: G; i: INTEGER).

[x]ნებისმიერ დავალებაში x: = yდა ნებისმიერი ქვერეგისტრალური ზარისთვის, რომელშიც yარის ოფიციალური არგუმენტის ფაქტობრივი არგუმენტი x, წყაროს ტიპი yუნდა იყოს თავსებადი სამიზნე ტიპთან x... თავსებადობის განსაზღვრა ემყარება მემკვიდრეობას: თავსებადი , თუ ის მისი შთამომავალია, დამატებულია ზოგადი პარამეტრების წესებით (იხ. ლექცია 14).

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

რეალიზმი

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

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

[x] სრულიად არასწორი ენაარომლის შექმნა ადვილია ნებისმიერი არსებული ენის აღებით და აკრეფის წესის დამატებით ნებისმიერისისტემა არასწორია. განმარტებით, ეს ენა აკრეფილია: ვინაიდან არ არსებობს სისტემები, რომლებიც შეესაბამება წესებს, არცერთი სისტემა არ გამოიწვევს ტიპის დარღვევას.

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

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

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

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

პესიმიზმი

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

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


თუ ჩვენ შევძლებთ მათ შესრულებას, ჩვენ დავინახავთ, რომ [A] ყოველთვის იმუშავებს, ვინაიდან ნებისმიერ რიცხვთა სისტემას აქვს ზუსტი გამოსახულება 0,0 რეალური რიცხვისა, რომელიც ერთმნიშვნელოვნად შეიძლება ითარგმნოს 0 მთელ რიცხვში. [B] თითქმის რა თქმა უნდა ასევე იმუშავებს. მოქმედების შედეგი [C] არ არის აშკარა (გვინდა თუ არა მივიღოთ ჯამი წილადის ნაწილის დამრგვალებით ან გაუქმებით?). [D] შეასრულებს თავის საქმეს, ისევე როგორც ოპერატორი:


თუ n ^ 2< 0 then n:= 3.67 end [E]

რომელიც მოიცავს მიუწვდომელ დავალებას ( n ^ 2არის რიცხვის კვადრატი n). ჩანაცვლების შემდეგ n ^ 2ჩართული nმხოლოდ დაწყების სერია მისცემს სწორ შედეგს. დავალება nდიდი რეალური მნიშვნელობა, რომელიც არ შეიძლება წარმოდგენილი იყოს მთელი რიცხვით, გამოიწვევს წარუმატებლობას.

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

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

სტატიკური აკრეფა: როგორ და რატომ

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

უპირატესობები

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

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

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

ბრინჯი 17.1.შეცდომების აღმოფხვრის შედარებითი ხარჯები (გამოქვეყნებულია ნებართვით)

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

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

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

არგუმენტები დინამიური აკრეფისათვის

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

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

დახასიათება: წარმატების პირობები

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

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

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

მოქნილი ტიპის სისტემის შექმნისას გადამწყვეტი სიტყვა ეკუთვნის მემკვიდრეობადა მასთან დაკავშირებული კონცეფცია თავსებადობა... ეს გადალახავს კლასიკური აკრეფილი ენების ძირითად შეზღუდვას, მაგალითად, პასკალსა და ადას, რომელშიც ოპერატორი x: = yმოითხოვს, რომ ტიპი xდა yიგივე იყო ეს წესი მეტისმეტად მკაცრია: ის კრძალავს ისეთი პირების გამოყენებას, რომელთაც შეუძლიათ აღნიშნონ მსგავსი ტიპის საგნები ( ᲨᲔᲛᲜᲐᲮᲕᲔᲚᲘ ᲐᲜᲒᲐᲠᲘᲨᲘდა ᲐᲜᲒᲐᲠᲘᲨᲘᲡ ᲨᲔᲛᲝᲬᲛᲔᲑᲐ). მემკვიდრეობით, ჩვენ მხოლოდ ტიპის თავსებადობას ვითხოვთ yტიპთან ერთად x, მაგალითად, xარის ტიპის ანგარიში, y - ᲨᲔᲛᲜᲐᲮᲕᲔᲚᲘ ᲐᲜᲒᲐᲠᲘᲨᲘდა მეორე კლასი არის პირველის მემკვიდრე.

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

ბრინჯი 17.2.მრავალჯერადი მემკვიდრეობა

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

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


კლასი SORTABLE_LIST ...

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

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

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

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

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

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

"ბავშვი აკრეფილია?"

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

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

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

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

აკრეფა და დაკავშირება

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

ორივე აკრეფა და კავშირი ეხება ძირითადი კონსტრუქტის სემანტიკას x.f (არგ)მაგრამ უპასუხე ორ განსხვავებულ კითხვას:

აკრეფა და დაკავშირება

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

[x] დამაკავშირებელი კითხვა: როდის უნდა ვიცოდეთ რა ოპერაციას იწყებს მოცემული ზარი?

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

ობიექტური მიდგომის ფარგლებში:

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

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

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

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

[x] Smalltalk და OO სხვა ენები შეიცავს დინამიურ კავშირს და აკრეფის დინამიურ საშუალებებს. ამავე დროს, უპირატესობა ენიჭება მოქნილობას ენის სანდოობის ხარჯზე.

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

[x]სტატიკური აკრეფის და დინამიური კავშირის იდეები განსახიერებულია ამ წიგნში მოცემულ აღნიშვნებში.

გაითვალისწინეთ C ++ ენის თავისებურება, რომელიც მხარს უჭერს სტატიკურ აკრეფას, თუმცა არ არის მკაცრი ტიპის ჩამოსხმის, სტატიკური სავალდებულო (ნაგულისხმევად), დინამიური შეკავშირების გამო ვირტუალური (როდესაც ვირტუალური) რეკლამები.

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

მემკვიდრეობის იერარქიის შემდეგი მაგალითი დაგეხმარებათ ამ ცნებების გარკვევაში:

ბრინჯი 17.3.თვითმფრინავების ტიპები

განვიხილოთ ზარი:


my_aircraft.lower_landing_gear

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

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

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

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

კოვარიაცია და ბავშვის დამალვა

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

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

კოვარიანობა

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

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

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

ბრინჯი 17.4.მოთხილამურეთა კლასიფიკაცია

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

აქ არის კლასის ესკიზი სკაიერი:


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

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


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

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


- მეზობელი ნომრით.

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


- მეზობელი ნომრით.
- აირჩიე სხვა მეზობლად.

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

ბრინჯი 17.5.წევრების იერარქია და ხელახალი განმარტებები

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

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

[x]ერთჯერადად დაკავშირებული სიის ერთეული LINKABLEუნდა იყოს დაკავშირებული სხვა თავისებურ ელემენტთან და მაგალითთან BI_LINKABLE- შენსავით ვინმესთან. კოვარიაციულად უნდა გადალახოს და არგუმენტი ჩაითვალოს დააყენა_ სწორი.

[x]ნებისმიერი ქვეპროგრამა კომპოზიციაში LINKED_LISTმსგავსი არგუმენტით LINKABLEროდესაც აპირებს TWO_WAY_LISTდასჭირდება არგუმენტი BI_LINKABLE.

[x]Პროცედურა set_alternateიღებს მოწყობილობა-არგუმენტი კლასში მოწყობილობადა პრინტერიარგუმენტი - კლასში პრინტერი.

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


- დააყენეთ ატრიბუტი v.

მასთან მუშაობა ატრიბუტიტიპი SOME_TYPE... ასეთი პროცედურები ბუნებრივად კოვარიაციაა, რადგან ნებისმიერი კლასი, რომელიც ცვლის ატრიბუტის ტიპს, შესაბამისად უნდა გადალახოს არგუმენტი. set_attrib... მიუხედავად იმისა, რომ წარმოდგენილი მაგალითები ჯდება ერთ სქემაში, კოვარიაცია ბევრად უფრო გავრცელებულია. მაგალითად, იფიქრეთ იმ პროცედურაზე ან ფუნქციაზე, რომელიც ასრულებს ერთმანეთთან დაკავშირებულ სიებს ( LINKED_LIST). მისი არგუმენტი უნდა განისაზღვროს, როგორც ორმაგად დაკავშირებული სია ( TWO_ WAY_LIST). უნივერსალური დამატების ოპერაცია infix "+"იღებს რიცხვითი-არგუმენტი კლასში რიცხვითი, უძრავი- კლასში უძრავიდა INTEGER- კლასში INTEGER... სატელეფონო მომსახურების პარალელურად იერარქია პროცედურამდე დაწყებაკლასში PHONE_SERVICEარგუმენტი შეიძლება იყოს საჭირო მისამართიწარმოადგენს აბონენტის მისამართს (ბილინგისთვის), ხოლო იგივე პროცედურა კლასში CORPORATE_SERVICEდასჭირდება მსგავსი არგუმენტი CORPORATE_ADDRESS.

ბრინჯი 17.6.საკომუნიკაციო მომსახურება

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

პარალელური იერარქიები

იმისათვის, რომ ქვა ქვაზე არ დატოვოთ, განიხილეთ მაგალითის ვარიანტი სკაიერიორი პარალელური იერარქიით. ეს საშუალებას მოგვცემს გავამყაროთ სიტუაცია, რომელიც უკვე გვხვდება პრაქტიკაში: TWO_ WAY_LIST> LINKED_LISTდა BI_LINKABLE> LINKABLE; ან იერარქია სატელეფონო მომსახურებით PHONE_SERVICE.

მოდით იერარქია კლასთან ერთად ოთახირომლის შთამომავალია GIRL_ROOM(Კლასი ბიჭიგამოტოვებული):

ბრინჯი 17.7.მოთხილამურეები და ოთახები

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


description: "ახალი ვარიანტი პარალელური იერარქიით"
განთავსება (r: ROOM) არის ... მოითხოვს ... აკეთებს

კოვარიანტული გადალახვა აქაც საჭიროა: კლასში გოგო 1როგორ განსახლებადა ქვეგანაკვეთის არგუმენტი მოთავსებაუნდა შეიცვალოს ტიპით GIRL_ROOM, კლასში ბიჭი 1- ტიპი ბიჭუნადა ა.შ. (გახსოვდეთ, ჩვენ ჯერ კიდევ ვმუშაობთ ტიპის დამაგრების გარეშე.) როგორც წინა მაგალითში, კონტრავარიანობა აქაც უსარგებლოა.

პოლიმორფიზმის ნებაყოფლობითობა

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


შექმნა ბ; შექმნა g; - ბიჭისა და გოგოს ობიექტების შექმნა.

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

პარალელური იერარქიის სქემა ისეთივე მარტივია: შეცვლა სკაიერიჩართული SKIER1, დარეკე გაზიარება- დარეკვა s. მისაღები (გრ), სად გრ- ერთეულის ტიპი GIRL_ROOM... შედეგი იგივეა.

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

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

ბავშვის დამალვა

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

ბრინჯი 17.8.ბავშვის დამალვა

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


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

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


შექმნა r; - RECTANGLE ობიექტის შექმნა.
p: = r; - პოლიმორფული დავალება.

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

სისტემებისა და კლასების სისწორე

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

სისტემას ჰქვია სისტემა ძალაშიათუ არ არსებობს რაიმე სახის დარღვევა მისი შესრულებისას.

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

პრაქტიკული ასპექტი

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

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

შემდეგი, ჩვენ შევეხებით ძალიან დახვეწილ და არც თუ ისე ხშირად შეგრძნებებს ობიექტის მიდგომის ასპექტებზე. წიგნის პირველად კითხვისას შეგიძლიათ გამოტოვოთ ამ ლექციის დარჩენილი ნაწილები. თუ თქვენ ახლახანს აითვისეთ OO ტექნოლოგია, მაშინ უკეთ უნდა დაეუფლოთ ამ მასალას მას შემდეგ რაც შეისწავლით ლექციებს 1-11 კურსზე "ობიექტზე ორიენტირებული დიზაინის საფუძვლები" მემკვიდრეობის მეთოდოლოგიაზე და განსაკუთრებით ლექცია მე -6 კურსზე "საფუძვლები ობიექტზე ორიენტირებული დიზაინი ”მეთოდოლოგიის მემკვიდრეობაზე.

სისტემების სისწორე: პირველი მიახლოება

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

კონტრავარიანობა და არა ვარიაცია

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

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

ზოგადი პარამეტრების გამოყენება

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


კლასის SKIER1 ფუნქცია
განთავსება (r: G) არის ... მოითხოვეთ ... გააკეთეთ განსახლება: = r დასასრული

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

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

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

ტიპიური ცვლადები

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

[x]ნაცვლად კოვარიაანტის გადაფარვისა, დაუშვით ტიპის დეკლარაციები ტიპის ცვლადების გამოყენებით;

[x]გააფართოვოს ტიპის თავსებადობის წესები ასეთი ცვლადების გასაკონტროლებლად;

[x]უზრუნველყოფს ენის ტიპების მნიშვნელობებად ცვლადების ტიპების მინიჭებას.

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

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

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

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

ეყრდნობა ტიპის ჩამაგრებას

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

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


წილი (სხვა: მიმდინარეობის მსგავსად) არის ... მოითხოვე ... გააკეთე
განსახლება (რ: განსახლების მსგავსად) არის ... მოითხოვე ... გააკეთე

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

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

მართალია, მაგალითების ორიგინალური ვერსიები უარყოფილი იქნება. დაე იყოს:


შექმნა b; შექმნა g; - ბიჭისა და გოგოს ობიექტების შექმნა.
s: = b; - პოლიმორფული დავალება.

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

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


s: SKIER; ბ: ბიჭი; g: მოსწონს s; ფაქტობრივი_გ: გოგო;
შექმნა ბ; create actual_g - ქმნის BOY და GIRL ობიექტებს.
s: = ფაქტობრივი_გ; g: = s - გამოიყენეთ s, რათა დაამატოთ g გოგოზე.
s: = b - პოლიმორფული დავალება.

შედეგად, უკანონო ზარი გადის.

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


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

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


ვინაიდან კლასი - მშობელი გამოიყურებოდა


სად Yშეესაბამება Xშემდეგ ახლა კომპონენტის უპირატესობა ასე გამოიყურება:


რჩება მხოლოდ კლასში გადაფარვის ტიპი შენი_ წამყვანი.

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

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

[x]ეს მარტივი და ელეგანტური გადაწყვეტა ადვილი ასახსნელია, თუნდაც დამწყებთათვის.

[x]ის მთლიანად გამორიცხავს სისტემის კორექტულობის დარღვევის შესაძლებლობას კოვარიანტიულად აგებულ სისტემებში.

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

[x]ის მოითხოვს ენის უმნიშვნელო ცვლილებას - მატჩის წესში ასახული ერთი საკვანძო სიტყვის დამატებას და არ გულისხმობს განხორციელების აღქმულ სირთულეებს.

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

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

[x]ერთეული პოტენციურად პოლიმორფულია: ახლა თუ გვიან (პარამეტრების გავლით ან დავალებით) ის შეიძლება დაერთოს ობიექტს, რომლის ტიპი განსხვავდება დეკლარირებულისაგან. პირველადი ერთეულის ტიპი არ შეიძლება შეიცვალოს კლასის რომელიმე შთამომავლის მიერ.

[x]ერთეული ექვემდებარება ტიპურ უგულებელყოფას, ანუ ის ან არის მიმაგრებული, ან თავად არის მბრუნავი.

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

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

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

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

გლობალური ანალიზი

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

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


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


პრობლემა იწყება ორი დამოუკიდებელი და სრულიად უდანაშაულო ოპერატორის გაერთიანებით.

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

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

ჩვენს მაგალითებში, ოპერატორი s: = ბმიუთითებს, რომ კლასი ბიჭიმიეკუთვნება ტიპების ერთობლიობას (რადგან შექმნის განცხადების შესრულების შედეგად შექმნა ბის მიეკუთვნება საბეჭდ ჯგუფს ). გოგო, ინსტრუქციების არსებობის გამო შექმნა გ, მიეკუთვნება ტიპების ერთობლიობას for ... მაგრამ შემდეგ ზარი გაზიარებაარასწორი იქნება იმ მიზნით ტიპი ბიჭიდა არგუმენტი ტიპი გოგო... ანალოგიურად შეცვალეარის საბეჭდი ჯგუფისთვის გვ, რაც განპირობებულია პოლიმორფული დანიშნულებით, თუმცა, ზარი add_vertexამისთვის გვტიპი შეცვალეაღმოჩნდება არასწორი.

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

სისტემის სისწორის წესი

დარეკეთ x.f (არგ)არის სისტემის სწორი თუ და მხოლოდ იმ შემთხვევაში, თუ ის კლასისთვის სწორია xდა არგნებისმიერი ტიპის მათი შესაბამისი ნაკრებიდან.

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

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

1 თითოეული ერთეულისთვის, ტიპების საწყისი ნაკრები ცარიელია.

2 ფორმის შემდგომი ინსტრუქციის შემდგომ შექმნა (SOME_TYPE) a, დაამატე SOME_TYPEტიპების ერთობლიობაში ... (სიმარტივისთვის, ჩვენ ვივარაუდებთ, რომ ნებისმიერი ინსტრუქცია შექმენიშეიცვლება ინსტრუქციით შექმნა (ATYPE) a, სად ᲢᲘᲞᲘ- ერთეულის ტიპი .)

3 შეასრულა ფორმის შემდგომი დავალება a: = b, დაამატეთ ტიპების ნაკრებებს .

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

5 ჩვენ ვიმეორებთ ნაბიჯებს (3) და (4) სანამ ტიპების ნაკრები არ შეწყვეტს ცვლილებას.

ეს ფორმულირება არ ითვალისწინებს უნივერსალურობის მექანიზმს, მაგრამ შესაძლებელია წესის გაგრძელება საჭიროებისამებრ უპრობლემოდ. ნაბიჯი (5) აუცილებელია ჯაჭვების დანიშვნისა და გადაცემის შესაძლებლობის გამო (დან დან , დან დან და ა.შ.). ადვილი გასაგებია, რომ სასრული ნაბიჯების შემდეგ ეს პროცესი შეჩერდება.

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


შექმნა (TYPE1) t; s: = t; შექმნა (TYPE2) t

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


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

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

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

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

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

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

გაუფრთხილდით პოლიმორფულ კატალოგებს!

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

დაბრუნება იალტაში

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

როგორც ადრე, ჩვენ კოვარიანობის საკითხს ორ ოპერაციამდე მივყვებით. ჩვენს მთავარ მაგალითში, ეს არის პოლიმორფული დავალება: s: = ბდა კოვარიანტული ქვეპროგრამის გამოძახება: s.share (g)... გავაანალიზებთ ვინ არის დარღვევების რეალური დამნაშავე, ჩვენ გამოვრიცხავთ არგუმენტს ეჭვმიტანილთაგან. ნებისმიერი ტიპის არგუმენტი სკაიერიან მისგან წარმოქმნილი, ის არ გვეფერება პოლიმორფიზმის გამო და კოვარიანობა გაზიარება... ამიტომ, თუ სტატიკურად აღწერთ ერთეულს სხვაროგორ სკაიერიდა დინამიურად მიამაგრეთ ობიექტზე სკაიერიშემდეგ დარეკე s.hare (სხვა)სტატიკურად იტოვებს იდეალურობის შთაბეჭდილებას, მაგრამ პოლიმორფულად მინიჭების შემთხვევაში გამოიწვევს ტიპის დარღვევას მნიშვნელობა .

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

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

ერთი წესი და რამდენიმე განმარტება

Catcall– ის გადაწყვეტის ტიპის წესი მარტივია:

კატალოგის ტიპის წესი

პოლიმორფული კატალოგები არასწორია.

იგი ემყარება ისევე მარტივ განმარტებებს. უპირველეს ყოვლისა, პოლიმორფული ერთეული:

განმარტება: პოლიმორფული ერთეული

არსი xსაცნობარო (გაფართოებული) ტიპი არის პოლიმორფული, თუ მას აქვს ერთი შემდეგი თვისება:

1 ხდება დავალებაში x: = yსად არის არსი yარის სხვადასხვა ტიპის ან პოლიმორფულია რეკურსიით.

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

3 არის ფორმალური არგუმენტი ქვეგანაკვეთზე.

4 ეს არის გარეგანი ფუნქცია.

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

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

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

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

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

განმარტება: პოლიმორფული ზარი

ზარი პოლიმორფულია, თუ მისი სამიზნე პოლიმორფულია.

ჩვენს მაგალითებში ორივე ზარი პოლიმორფულია: s.share (g)პოლიმორფიზმის გამო , p.dd_ vertex (...)პოლიმორფიზმის გამო გვ... განმარტებით, მხოლოდ კვალიფიციური ზარები შეიძლება იყოს პოლიმორფული. (არაკვალიფიციური ზარის მიცემით ვ (...)ერთგვარი კვალიფიციური მიმდინარე.ფ (...), ჩვენ არ ვცვლით საკითხის არსს, მას შემდეგ მიმდინარერომელსაც არაფრის მინიჭება არ შეუძლია იყოს პოლიმორფული ობიექტი.)

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

განმარტება: CAT რუტინები

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

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

განმარტება: Catcall

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

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

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

კლასი

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

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

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

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

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

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

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

სრული შესაბამისობა

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

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

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


მორგებული (სხვა: ზოგადი): ისევე როგორც სხვა არის
- მიმდინარე ობიექტი (მიმდინარე), თუ მისი ტიპი შეესაბამება ობიექტის ტიპს,
- მიმაგრებულია სხვა, სხვაგვარად ბათილია.
თუ სხვა / = ბათილია და შემდეგ შეესაბამება_ (სხვას) მაშინ

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

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

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


- შეარჩიეთ, საჭიროების შემთხვევაში, სხვა, როგორც მეზობელი ნომრით.
- გენდერული_დადგენილი - დადგენილი სქესი
გენდერული_დამოკიდებული_ სხვა: როგორც მიმდინარე
გენდერული_დამოკიდებული_ სხვა: = სხვა. მორგებული (მიმდინარე)
თუ სქესი_დასკვნის_ სხვა / = ძალადაკარგულია მაშინ
გაზიარება (სქესის_დადგენა_ სხვა)
"დასკვნა: სხვა ადამიანებთან კოლოლაცია შეუძლებელია"

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

თუ არსებობს მოთხილამურეთა ორი პარალელური სია, რომლებიც წარმოადგენენ დაგეგმილ საცხოვრებელს:


ოკუპანტი 1, ოკუპანტი 2: სია

თქვენ შეგიძლიათ მოაწყოთ მარყუჟი ყოველ ნაბიჯზე დარეკვით:


Occant1.item.safe_share (Occup2.item)

სიის ერთეულების შესატყვისი მხოლოდ და მხოლოდ იმ შემთხვევაში, თუ მათი ტიპები სრულად თავსებადია.

ძირითადი ცნებები

[x]სტატიკური აკრეფა არის საიმედოობის, წაკითხვისა და ეფექტურობის გასაღები.

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

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

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

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

ბიბლიოგრაფიული ჩანაწერები

ამ ლექციის არაერთი მასალა წარმოდგენილია ანგარიშებში ფორუმებზე OOPSLA 95 და TOOLS PACIFIC 95, ასევე გამოქვეყნებულია. რამოდენიმე განხილვის მასალა ნასესხებია სტატიიდან.

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

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

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

ISE გზამკვლევი მოიცავს პოლიმორფიზმის, კოვარიანობის და ბავშვის ერთად დამალვის პრობლემების გაცნობას. ამ წიგნის პირველ გამოცემაში სათანადო ანალიზის არარსებობამ გამოიწვია არაერთი კრიტიკული დისკუსია (რომელთაგან პირველი იყო ფილიპ ელინკის კომენტარი ბაკალავრის ნაშრომში "De la Conception-Programmation par Objets", Memoire de license, Universite Libre დე ბრუქსელი (ბელგია), 1988), გამოხატული ი. კუკის სტატიაში მოცემულია რამდენიმე მაგალითი, რომელიც დაკავშირებულია კოვარიაციის პრობლემასთან და მისი გადაჭრის მცდელობას. გადაწყვეტა, რომელიც დაფუძნებულია ტიპურ პარამეტრებზე კოვარიანტი ერთეულებისთვის TOOLS EUROPE 1992– ზე, შემოთავაზებულია ფრანც ვებერის მიერ. სისტემის სისწორის ცნებების ზუსტი განმარტებები, ისევე როგორც კლასობრივი სისწორე, მოცემულია იქ, სადაც შემოთავაზებულია გამოსავალი სისტემის სრული ანალიზის გამოყენებით. Catcall– ის გადაწყვეტა პირველად შემოთავაზებულ იქნა; იხილეთ ასევე .

სავალდებულო გამოსავალი წარმოდგენილი იყო ჩემს პრეზენტაციაში TOOLS EUROPE 1994 სემინარზე. თუმცა, იმ დროს მე ვერ ვხედავდი ამის აუცილებლობას წამყვანი- დეკლარაციები და შესაბამისი თავსებადობის შეზღუდვები. პოლ დიუბუა და ამირამ იეჰუდაი სწრაფად აღნიშნავდნენ, რომ ამ პირობებში კოვარიანობის პრობლემა კვლავ რჩება. მათ, რაინჰარდ ბუდესთან, კარლ-ჰაინზ სილასთან, კიმ უოლდენთან და ჯეიმს მაკკიმთან ერთად, ბევრი კრიტიკული წერტილი დაუსვეს მუშაობას, რამაც გამოიწვია ამ ლექციის დაწერა.

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

ზოგადი ცვლადების საფუძველზე ხრიკები აღწერილია ,,.

კონტრავარიანობა განხორციელდა Sather ენაზე. ახსნა -განმარტებები მოცემულია.

მიუხედავად იმისა, რომ შუალედური ვარიანტებია შესაძლებელი, აქ ორი ძირითადი მიდგომაა წარმოდგენილი:

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

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

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

განმარტება: სტატიკურად აკრეფილი ენა

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

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

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

აკრეფის წესები

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

  • თითოეული ერთეულის ან ფუნქციის გამოცხადებისას, მისი ტიპი უნდა იყოს მითითებული, მაგალითად, acc: ანგარიში... თითოეულ ქვეჯგუფს აქვს 0 ან მეტი ფორმალური არგუმენტი, რომელთა ტიპი უნდა იყოს მითითებული, მაგალითად: put (x: G; i: INTEGER).
  • ნებისმიერ დავალებაში x: = y და ნებისმიერ ქვერეგისტრალურ ზარში, რომელშიც y არის ფაქტობრივი არგუმენტის x ფაქტობრივი არგუმენტი, წყაროს ტიპი y უნდა იყოს თავსებადი სამიზნე x– თან. თავსებადობის განმარტება ემყარება მემკვიდრეობას: B თავსებადია A- სთან, თუ ის მისი შთამომავალია, დამატებულია ზოგადი პარამეტრების წესებით (იხ. "მემკვიდრეობის შესავალი").
  • ზარი x.f (arg) მოითხოვს f იყოს საბაზო კლასის კომპონენტი სამიზნე ტიპის x- ისთვის, და f უნდა იყოს ექსპორტირებული იმ კლასში, რომელშიც ზარი ჩანს (იხ. 14.3).

რეალიზმი

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

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

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

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

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

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

პესიმიზმი

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

განვიხილოთ რეგულარული, არაობიექტური, პასკალის მსგავსი ენა სხვადასხვა უძრავი და ინტეგრირებული ტიპებით. N აღწერისას: INTEGER; r: რეალური ოპერატორი n: = r იქნება უარყოფილი, როგორც წესების დარღვევა. ამრიგად, შემდგენელი უარყოფს ყველა შემდეგ განცხადებას:

n: = 0.0 [A] n: = 1.0 [B] n: = -3.67 [C] n: = 3.67 - 3.67 [D]

თუ ჩვენ შევძლებთ მათ შესრულებას, ჩვენ დავინახავთ, რომ [A] ყოველთვის იმუშავებს, ვინაიდან ნებისმიერ რიცხვთა სისტემას აქვს ზუსტი გამოსახულება 0,0 რეალური რიცხვისა, რომელიც ერთმნიშვნელოვნად შეიძლება ითარგმნოს 0 მთელ რიცხვში. [B] თითქმის რა თქმა უნდა ასევე იმუშავებს. მოქმედების შედეგი [C] არ არის აშკარა (გვინდა თუ არა მივიღოთ ჯამი წილადის ნაწილის დამრგვალებით ან გაუქმებით?). [D] შეასრულებს თავის საქმეს, ისევე როგორც ოპერატორი:

თუ n ^ 2< 0 then n:= 3.67 end [E]

სადაც მიდის მიუწვდომელი დავალება (n ^ 2 არის n- ის კვადრატი). N ^ 2 n– ით ჩანაცვლების შემდეგ, მხოლოდ რამოდენიმე გარბენი იძლევა სწორ შედეგს. N არა რიცხვითი მოცულობის მცურავი წერტილის მნიშვნელობის მინიჭება n ვერ მოხერხდება.

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

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

სტატიკური აკრეფა: როგორ და რატომ

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

უპირატესობები

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

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

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


ბრინჯი 17.1.

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

საბოლოოდ, ეფექტურობაშეუძლია განსაზღვროს ობიექტის ტექნოლოგიის წარმატება ან წარუმატებლობა პრაქტიკაში. არარსებობისას სტატიკური აკრეფა x.f (arg) შეიძლება ნებისმიერ დროს დასჭირდეს შესრულება. ამის მიზეზი ის არის, რომ გაშვების დროს, თუ f არ მოიძებნება სამიზნე x- ის ძირითად კლასში, ძებნა გაგრძელდება მის შთამომავლებზე, რაც არაეფექტურობისკენ მიმავალი გზაა. პრობლემის შემსუბუქება შეგიძლიათ იერარქიაში კომპონენტის ძიების გაუმჯობესებით. Self– ის ავტორებმა დიდი სამუშაო გააკეთეს, ცდილობდნენ შექმნან საუკეთესო კოდი დინამიურად აკრეფილი ენისთვის. მაგრამ ეს არის ზუსტად სტატიკური აკრეფანებადართულია ასეთი OO პროდუქტი მიუახლოვდეს ან გაუტოლდეს ტრადიციული პროგრამული უზრუნველყოფის ეფექტურობას.

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

არგუმენტები დინამიური აკრეფისათვის

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

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

დახასიათება: წარმატების პირობები

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

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

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

მოქნილი ტიპის სისტემის შექმნისას გადამწყვეტი სიტყვა ეკუთვნის მემკვიდრეობადა მასთან დაკავშირებული კონცეფცია თავსებადობა... ეს გადალახავს კლასიკური აკრეფილი ენების ძირითად შეზღუდვას, მაგალითად, პასკალსა და ადას, რომელშიც ოპერატორი x: = y მოითხოვს, რომ x და y ტიპები ერთნაირი იყოს. ეს წესი მეტისმეტად მკაცრია: ის კრძალავს ისეთი ობიექტების გამოყენებას, რომელთაც შეუძლიათ აღნიშნონ მსგავსი ტიპის ობიექტები (SAVINGS_ACCOUNT და CHECKING_ACCOUNT). მემკვიდრეობით, ჩვენ მხოლოდ ვითხოვთ ტიპის თავსებადობა y ტიპი x– ით, მაგალითად, x არის ACCOUNT– ის ტიპის, y არის SAVINGS_ACCOUNT, ხოლო მეორე კლასი მემკვიდრეობით იღებს პირველს.

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


ბრინჯი 17.2.

მრავალმხრივობააუცილებელია, მაგალითად, მოქნილი, მაგრამ უსაფრთხო კონტეინერის მონაცემთა სტრუქტურის აღსაწერად (მაგალითად კლასის სია [G] ...). ნუ იქნები ეს მექანიზმი სტატიკური აკრეფადასჭირდება განსხვავებული კლასების გამოცხადება სხვადასხვა ელემენტების ტიპების მქონე სიებისათვის.

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

კლასი SORTABLE_LIST ...

ნებისმიერი რეალური generic SORTABLE_LIST უნდა იყოს COMPARABLE კლასის შთამომავალი, რომელსაც გააჩნია საჭირო კომპონენტი.

კიდევ ერთი სავალდებულო მექანიზმია დავალების მცდელობა- ორგანიზებას უწევს იმ ობიექტებზე წვდომას, რომელთა ტიპს პროგრამული უზრუნველყოფა არ აკონტროლებს. თუ y არის მონაცემთა ბაზის ობიექტი ან ობიექტი, რომელიც მოიპოვება ქსელში, მაშინ x? = Y მიანიჭებს x- ს y- ს, თუ y თავსებადი ტიპია, ან თუ ის არ არის, x- ს ვაძლევთ Void- ს.

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

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

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

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

"ბავშვი აკრეფილია?"

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

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

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

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

აკრეფა და დაკავშირება

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

ორივე აკრეფა და სავალდებულო ეხება Core Construct x.f (arg) სემანტიკას, მაგრამ უპასუხეთ ორ განსხვავებულ კითხვას:

აკრეფა და დაკავშირება

  • აკრეფის კითხვა: როდის უნდა ვიცოდეთ ზუსტად, რომ გაშვების დროს იქნება f– ს შესაბამისი ოპერაცია, რომელიც გამოიყენება ერთეულებზე x (arg პარამეტრით) მიმაგრებულ ობიექტზე?
  • დამაკავშირებელი კითხვა: როდის უნდა ვიცოდეთ რა ოპერაციას იწყებს მოცემული ზარი?

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

ობიექტური მიდგომის ფარგლებში:

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

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

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

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

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

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

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

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



ტიპი არის შესაძლო მნიშვნელობების კრებული. მთელ რიცხვს შეიძლება ჰქონდეს მნიშვნელობა 0, 1, 2, 3 და ასე შემდეგ. ბული შეიძლება იყოს ჭეშმარიტი ან მცდარი. თქვენ შეგიძლიათ შეადგინოთ თქვენი საკუთარი ტიპი, მაგალითად, ტიპი "GiveFive", რომელშიც შესაძლებელია მნიშვნელობები "მისცეს" და "5" და სხვა არაფერი. ეს არ არის სტრიქონი ან რიცხვი, ეს არის ახალი, ცალკეული ტიპი.


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


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


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

სტატისტიკურად აკრეფილი ენები

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


ბევრი სტატიკურად აკრეფილი ენა მოითხოვს ტიპის აღნიშვნას. Java ფუნქცია public int add (int x, int y) იღებს ორ მთელ რიცხვს და აბრუნებს მესამე მთელ რიცხვს. სხვა სტატიკურად აკრეფილ ენებს შეუძლიათ ავტომატურად განსაზღვრონ ტიპი. იგივე დამატების ფუნქცია ჰასკელში ასე გამოიყურება: დაამატეთ x y = x + y. ჩვენ არ ვეუბნებით ენას ტიპების შესახებ, მაგრამ მას შეუძლია თავად გაარკვიოს, რადგან იცის რომ + მუშაობს მხოლოდ რიცხვებზე, ამიტომ x და y უნდა იყოს რიცხვები, ასე რომ დამატება ორ რიცხვს იღებს არგუმენტად.


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

დინამიურად აკრეფილი ენები

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


def f (x, y): დაბრუნება x + y

შეუძლია დაამატოთ ორი მთელი რიცხვი, წებოს სტრიქონები, სიები და ასე შემდეგ და ჩვენ ვერ ვხვდებით რა ხდება მანამ, სანამ პროგრამას არ გავუშვებთ. ალბათ, რაღაც მომენტში f ფუნქციას დაერქმევა ორი სტრიქონი, ხოლო მეორე რიცხვში - სხვა მომენტში. ამ შემთხვევაში, x და y შეიცავს სხვადასხვა ტიპის მნიშვნელობებს სხვადასხვა დროს. ამიტომ, ნათქვამია, რომ დინამიურ ენებში ღირებულებებს აქვთ ტიპი, მაგრამ ცვლადებს და ფუნქციებს არა. 1 მნიშვნელობა ნამდვილად არის მთელი რიცხვი, მაგრამ x და y შეიძლება იყოს არაფერი.

შედარება

დინამიური ენების უმეტესობა შეცდომას დაუშვებს, თუ ტიპები არასწორად გამოიყენება (JavaScript არის ცნობილი გამონაკლისი; ის ცდილობს დააბრუნოს მნიშვნელობა ნებისმიერი გამოთქმისთვის, მაშინაც კი, როცა ამას აზრი არ აქვს). დინამიურად აკრეფილი ენების გამოყენებისას, თუნდაც ისეთი მარტივი შეცდომა, როგორიცაა "a" + 1 შეიძლება წარმოიშვას წარმოების გარემოში. სტატიკური ენები ხელს უშლის ასეთ შეცდომებს, მაგრამ რა თქმა უნდა პრევენციის ხარისხი დამოკიდებულია ტიპის სისტემის კარდინალურობაზე.


სტატიკური და დინამიური ენები აგებულია პროგრამის სისწორის ფუნდამენტურად განსხვავებულ იდეებზე. დინამიურ ენაზე "a" + 1, ეს არის სწორი პროგრამა: კოდი გააქტიურდება და შეცდომა გამოჩნდება გაშვების გარემოში. თუმცა, სტატისტიკურად აკრეფილ ენებზე, გამოთქმა "a" + 1 არის არა პროგრამა: არ იქნება შედგენილი და არ გაუშვებს. ეს არ არის სწორი კოდი, ისევე როგორც რამოდენიმე შემთხვევითი სიმბოლო! &% ^ @ * &% ^ @ * არის არასწორი კოდი. სიზუსტისა და სისწორის ამ დამატებით კონცეფციას არ აქვს ექვივალენტი დინამიურ ენებში.

ძლიერი და სუსტი აკრეფა

"ძლიერი" და "სუსტი" ცნებები ძალიან ორაზროვანია. აქ მოცემულია მათი გამოყენების რამდენიმე მაგალითი:

    ზოგჯერ "ძლიერი" ნიშნავს "სტატიკურს".
    ეს მარტივია, მაგრამ უმჯობესია გამოიყენოთ ტერმინი "სტატიკური", რადგან ადამიანების უმეტესობა იყენებს და ესმის.

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

    ზოგჯერ "ძლიერი" ნიშნავს იმას, რომ არ არსებობს გზა ენაზე ძლიერი აკრეფის წესების გარშემო.

  • ზოგჯერ "ძლიერი" ნიშნავს მეხსიერებას.
    C არის მეხსიერებაში არასაიმედო ენის მაგალითი. თუ xs არის ოთხი რიცხვის მასივი, მაშინ C სიხარულით შეასრულებს xs ან xs კოდს, დააბრუნებს გარკვეულ მნიშვნელობას მეხსიერებიდან xs– ის უკან.

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



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


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


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



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

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

თანდათანობითი აკრეფა

შეიძლება თუ არა სტატიკური ტიპების დამატება დინამიურ ენებზე? ზოგიერთ შემთხვევაში კი. სხვებში ეს რთულია ან შეუძლებელია. ყველაზე აშკარა პრობლემაა დინამიური ენების ევალური და სხვა მსგავსი შესაძლებლობები. პითონში 1 + ევალის გაკეთება ("2") იძლევა 3. მაგრამ რას იძლევა 1 + ევალი (წაკითხული_სიტყვიდან ())? ეს დამოკიდებულია იმაზე, თუ რა არის ქსელში შესრულების დროს. თუ მივიღებთ რიცხვს, მაშინ გამოთქმა სწორია. თუ სიმებიანი, მაშინ არა. გაშვებამდე შეუძლებელია ამის ცოდნა, ამიტომ შეუძლებელია ტიპის სტატისტიკურად გაანალიზება.


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


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


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


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

სტატიკურად აკრეფილი კოდის შედგენა

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


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

არგუმენტები სტატიკური და დინამიური ტიპებისთვის

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


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


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


JavaScript ცდილობს გააგრძელოს მუშაობა მაშინაც კი, თუ ეს ნიშნავს უაზრო გარდაქმნას (მაგალითად, "a" +1 "a1"). პითონი, თავის მხრივ, ცდილობს იყოს კონსერვატიული და ხშირად უბრუნებს შეცდომებს, როგორც ეს ხდება "a" + 1 შემთხვევაში.


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



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


დინამიური და სტატიკური ენების მრავალი ვარიაცია არსებობს. ნებისმიერი უდავო განცხადება, როგორიცაა "სტატიკური ენები უკეთესია ვიდრე დინამიური ენები, როდესაც საქმე X- ს ეხება" თითქმის გარანტირებული სისულელეა. ეს შეიძლება მართალი იყოს კონკრეტული ენებისთვის, მაგრამ მაშინ უმჯობესია ვთქვათ "Haskell უკეთესია ვიდრე Python როდესაც საქმე ეხება X".

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

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


Go სპეციალურად შეიქმნა ისე, რომ თქვენ არ შეგიძლიათ განსაზღვროთ ტიპები, როგორიცაა MyList. საუკეთესო რამ არის შექმნათ "ცარიელი ინტერფეისების" MyList: MyList შეიძლება შეიცავდეს ობიექტებს, მაგრამ შემდგენელმა უბრალოდ არ იცის მათი ტიპი. როდესაც ვიღებთ ობიექტებს MyList– დან, ჩვენ უნდა ვუთხრათ შემდგენელს მათი ტიპი. თუ ჩვენ ვამბობთ "მე ვიღებ სტრიქონს", მაგრამ სინამდვილეში მნიშვნელობა არის რიცხვი, მაშინ იქნება შესრულების შეცდომა, როგორც ეს დინამიური ენების შემთხვევაშია.


Go– ს ასევე აკლია მრავალი სხვა მახასიათებელი, რომლებიც გვხვდება თანამედროვე სტატიკურად აკრეფილ ენებში (ან თუნდაც 1970 – იანი წლების ზოგიერთი სისტემა). Go- ს შემქმნელებს ჰქონდათ საკუთარი მიზეზები ამ გადაწყვეტილებების მისაღებად, მაგრამ ამ საკითხთან დაკავშირებით უცხოთა მოსაზრებები ზოგჯერ შეიძლება მკაცრად ჟღერდეს.


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


ჰასკელს შეუძლია ბევრად უფრო რთული იდეების გამოხატვა უშუალოდ ტიპებით. მაგალითად, Num a => MyList a ნიშნავს "ღირებულებების MyList, რომლებიც ერთი და იგივე ტიპის რიცხვებია." ეს შეიძლება იყოს მთელი რიცხვების, მცურავების ან ფიქსირებული ზუსტი ათობითი რიცხვების სია, მაგრამ ის ნამდვილად არასოდეს იქნება იმ სტრიქონების სია, რომლებიც გადამოწმებულია შედგენის დროს.


თქვენ შეგიძლიათ დაწეროთ დამატების ფუნქცია, რომელიც მუშაობს ნებისმიერ ციფრულ ტიპზე. ამ ფუნქციას ექნება ტიპი Num a => (a -> a -> a). Ეს ნიშნავს:

  • a შეიძლება იყოს ნებისმიერი რიცხვითი ტიპი (Num a =>).
  • ფუნქცია იღებს a ტიპის ორ არგუმენტს და აბრუნებს ტიპს a (a -> a -> a).

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


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


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


შეადარეთ ეს ძალა Go, რომელსაც არ შეუძლია გამოხატოს MyList– ის მარტივი იდეა, რომ აღარაფერი ვთქვათ „ფუნქციაზე, რომელიც იღებს ორ არგუმენტს, ისინი ორივე რიცხვითია და ერთი და იგივე ტიპისა და აკეთებს I / O- ს“.


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


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


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

აკრეფის სისტემების შესაძლებლობებში განსხვავებების კონკრეტული მაგალითები

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


Go- ში შეგიძლიათ თქვათ "დამატების ფუნქცია იღებს ორ რიცხვს" და აბრუნებს მთელ რიცხვს ":


ფუნქციის დამატება (x int, y int) int (დაბრუნება x + y)

ჰასკელში შეგიძლიათ თქვათ "ფუნქცია იღებს ნებისმიერირიცხვითი ტიპი და აბრუნებს იმავე ტიპის რიცხვს ":


f :: Num a => a -> a -> a დამატება x y = x + y

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


დამატება: (x: Nat) -> (y: Nat) -> (ავტომატურად მცირე: LT x y) -> Nat დამატება x y = x + y

თუ თქვენ ცდილობთ დარეკოთ add 2 1 ფუნქცია, სადაც პირველი არგუმენტი მეორეზე დიდია, შემდგენელი უარყოფს პროგრამას შედგენის დროს... შეუძლებელია პროგრამის დაწერა, სადაც პირველი არგუმენტი მეორეზე დიდია. იშვიათ ენას აქვს ეს უნარი. უმეტეს ენაზე, ეს შემოწმება ხდება გაშვების დროს: ჩვენ დავწერდით რაღაცას, თუ x> = y: raise SomeError ().


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

ზოგიერთი სტატიკური ენის აკრეფის სისტემები

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

  • C (1972), Go (2009): ეს სისტემები საერთოდ არ არის ძლიერი, ზოგადი ტიპის მხარდაჭერის გარეშე. შეუძლებელია MyList ტიპის დაყენება, რაც ნიშნავს "მთელი რიცხვების ჩამონათვალს", "სტრიქონების სიას" და ა.შ. ამის ნაცვლად, თქვენ უნდა შეადგინოთ "დაუნიშნავი ღირებულებების სია". პროგრამისტმა ხელით უნდა შეატყობინოს "ეს არის სტრიქონების სია" ყოველ ჯერზე, როდესაც სტრიქონი ამოღებულია სიიდან და ამან შეიძლება გამოიწვიოს მუშაობის ხანგრძლივობის შეცდომა.
  • ჯავა (1995), C # (2000): ორივე ენა მხარს უჭერს ზოგად ტიპებს, ასე რომ თქვენ შეგიძლიათ თქვათ MyList და მიიღეთ იმ სტრიქონების სია, რომლებიც შემდგენელმა იცის და შეუძლია შეასრულოს ტიპის წესები. ელემენტები სიიდან იქნება String ტიპის, შემდგენელი შეასრულებს წესებს შედგენისას ჩვეულებისამებრ, ამიტომ გაშვების შეცდომები ნაკლებად სავარაუდოა.
  • Haskell (1990), Rust (2010), Swift (2014): ყველა ამ ენას აქვს რამდენიმე მოწინავე ფუნქცია, მათ შორის გენერიკული ტიპები, მონაცემთა ალგებრული ტიპები (ADT) და ტიპის კლასები ან მსგავსი რამ (შესაბამისად, კლასების ტიპები, მახასიათებლები და პროტოკოლები). Rust და Swift უფრო პოპულარულია ვიდრე Haskell და ხელს უწყობს დიდი ორგანიზაციები (შესაბამისად Mozilla და Apple).
  • აგდა (2007), იდრისი (2011): ეს ენები მხარს უჭერენ დამოკიდებულ ტიპებს, რაც საშუალებას გაძლევთ შექმნათ ისეთი ტიპები, როგორიცაა "ფუნქცია, რომელიც იღებს ორ მთელ რიცხვს x და y, სადაც y უფრო დიდია ვიდრე x". "Y არის x- ზე მეტი" შეზღუდვაც კი გამოიყენება შედგენის დროს. დასრულების შემდეგ, y არასოდეს იქნება x– ზე ნაკლები ან ტოლი, რაც არ უნდა მოხდეს. სისტემის ძალიან დახვეწილი, მაგრამ მნიშვნელოვანი თვისებები შეიძლება შემოწმდეს სტატისტიკურად ამ ენებზე. ძალიან ცოტა პროგრამისტი სწავლობს მათ, მაგრამ ეს ენები იწვევს მათ შორის დიდ ენთუზიაზმს.

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


მეორე ჯგუფი (Java და C #) არის ძირითადი ენები, ზრდასრული და ფართოდ გამოიყენება.


მესამე ჯგუფი მეინსტრიმში შესვლის ზღვარზეა, დიდი მხარდაჭერით Mozilla (Rust) და Apple (Swift).


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

ეს სტატია შეიცავს იმ აუცილებელ მინიმუმს, რაც თქვენ უბრალოდ უნდა იცოდეთ აკრეფის შესახებ, რათა დინამიურ აკრეფას არ უწოდოთ ბოროტება, Lisp– ს დაუწერელი ენა და C მკაცრად აკრეფილი ენა.

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

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

მოკლე ვერსია

აკრეფით, პროგრამირების ენები ჩვეულებრივ იყოფა ორ დიდ ბანაკად - აკრეფილი და დაუწერელი (ტიპური). პირველი მოიცავს, მაგალითად, C, Python, Scala, PHP და Lua, ხოლო მეორე მოიცავს ასამბლეის ენას, Forth და Brainfuck.

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

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

    მაგალითები:
    სტატიკური: C, Java, C #;
    დინამიური: პითონი, JavaScript, Ruby.

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

    მაგალითები:
    ძლიერი: Java, Python, Haskell, Lisp;
    სუსტი: C, JavaScript, Visual Basic, PHP.

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

    მაგალითები:
    აშკარა: C ++, D, C #
    ნაგულისხმევი: PHP, Lua, JavaScript

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

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

დეტალური ვერსია

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

უსახელო აკრეფა

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

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

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

დიახ, ეს არსებობს. მაგალითად, ასამბლეის ენაზე (x86 / x86-64 არქიტექტურისთვის, მე არ ვიცი სხვები), თქვენ ვერ ააწყობთ პროგრამას, თუ თქვენ ცდილობთ მონაცემების ჩატვირთვას rax რეგისტრიდან (64 ბიტი) cx რეგისტრში (16 ბიტი ).

mov cx, eax; შეკრების დროის შეცდომა

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

სტატიკური და დინამიური აკრეფა

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

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

მოდი გავარკვიოთ.

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

განზოგადებული პროგრამირება

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

როგორ ვაპირებთ მის მოგვარებას? მოდით გადავწყვიტოთ ის 3 სხვადასხვა ენაზე: ერთი დინამიური აკრეფით და ორი სტატიკური აკრეფით.

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

დინამიური გადაწყვეტა (პითონი):

Def find (required_element, list): for (index, element) inumerate (list): if element == required_element: return index return (-1)

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

სტატიკური ხსნარი (C):

ხელმოუწერელი int find_int (int required_element, int array, unsigned int size) (for (unsigned int i = 0; i< size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_float(float required_element, float array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_char(char required_element, char array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); }

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

Კი და არა. არსებობს პროგრამირების რამდენიმე ტექნიკა, რომელთაგან ერთს ახლა განვიხილავთ. მას უწოდებენ ზოგად პროგრამირებას და C ++ ენა მას საკმაოდ კარგად უჭერს მხარს. მოდით შევხედოთ ახალ ვერსიას:

სტატიკური გადაწყვეტა (ზოგადი პროგრამირება, C ++):

თარგი ხელმოუწერელი int find (T required_element, std :: vector) მასივი) (for (ხელმოუწერელი int i = 0; i< array.size(); ++i) if (required_element == array[i]) return i; return (-1); }

ᲙᲐᲠᲒᲘ! ის არ გამოიყურება ბევრად უფრო რთული, ვიდრე პითონის ვერსია და არ სჭირდება ბევრი რამის დაწერა. გარდა ამისა, ჩვენ მივიღეთ განხორციელება ყველა მასივისთვის და არა მხოლოდ 3 ის, რაც საჭიროა პრობლემის გადასაჭრელად!

როგორც ჩანს, ეს ვერსია არის ზუსტად ის, რაც გჭირდებათ - ჩვენ ვიღებთ როგორც სტატიკური აკრეფის უპირატესობებს, ასევე დინამიკის ზოგიერთ უპირატესობას.

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

სტატიკური დინამიკა

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

  • C # მხარს უჭერს დინამიურ ფსევდო ტიპს.
  • F # მხარს უჭერს სინტაქსურ შაქარს? ოპერატორის სახით, რომელიც შეიძლება გამოყენებულ იქნას დინამიური აკრეფის სიმულაციისთვის.
  • Haskell - დინამიური აკრეფა უზრუნველყოფილია Data.Dynamic მოდულით.
  • დელფი - სპეციალური ტიპის ვარიანტის საშუალებით.

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

  • Common Lisp - ტიპის დეკლარაციები.
  • Perl - 5.6 ვერსიიდან, საკმაოდ შეზღუდულია.

ძლიერი და სუსტი აკრეფა

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

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

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

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

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

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

გსურთ იცოდეთ რომელი?

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

კარგი, ჩვენ გავარკვიეთ, გამოდის, რომ სუსტ აკრეფას ასევე აქვს უპირატესობები! არსებობს გზები სუსტი აკრეფის სარგებელის ძლიერ აკრეფაზე გადატანისთვის?

გამოდის, რომ ორიც კი არსებობს.

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

უჰ ... საკმაოდ გრძელი წერტილი. ნება მომეცით შემდგომში შევამცირო ის "შეზღუდული ნაგულისხმევი გარდაქმნით". რას ნიშნავს ერთმნიშვნელოვანი მდგომარეობა და მონაცემთა დაკარგვა?

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

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

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

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

კარგი, გავიხსენოთ შეზღუდული ნაგულისხმევი გარდაქმნა. არსებობს ასეთი ენები? დიახ, მაგალითად პასკალში შეგიძლიათ გადაიყვანოთ მთელი რიცხვი რეალურ რიცხვზე, მაგრამ არა პირიქით. ასევე არსებობს მსგავსი მექანიზმები C #, Groovy და Common Lisp– ში.

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

მე ამას ავხსნი მშვენიერი ჰასკელის ენის მაგალითის გამოყენებით.

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

მაგალითად, გამოთქმაში pi + 1, თქვენ არ გსურთ დაწეროთ pi + 1.0 ან pi + float (1). მინდა დავწერო მხოლოდ pi + 1!

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

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

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

ამრიგად, ჩვენ შეგვიძლია ვთქვათ, რომ საუკეთესო გამოსავალი იქნება დაბალანსება ზღვარზე, ძლიერ და სუსტ აკრეფას შორის. ჯერჯერობით არცერთი ენა არ არის სრულყოფილ ბალანსში, ამიტომ მიდრეკილი ვარ უფრო მეტად მივმართო ძლიერ აკრეფილ ენებს (როგორიცაა Haskell, Java, C #, Python) და არა სუსტად აკრეფილ ენებს (როგორიცაა C, JavaScript, Lua, PHP) რა

აშკარა და ნაგულისხმევი აკრეფა

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

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

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

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

სარგებელი აშკარა აკრეფით
  • ხელმოწერის არსებობა თითოეული ფუნქციისთვის (მაგალითად int add (int, int)) საშუალებას გაძლევთ მარტივად განსაზღვროთ რას აკეთებს ფუნქცია.
  • პროგრამისტი დაუყოვნებლივ წერს რა ტიპის მნიშვნელობის შენახვა შეიძლება კონკრეტულ ცვლადში, რაც ხსნის ამის დამახსოვრების აუცილებლობას.
იმპლიციტური აკრეფის უპირატესობები
  • მოკლე დამატება def add (x, y) აშკარად უფრო მოკლეა ვიდრე int add (int x, int y).
  • ცვლილებებისადმი გამძლეობა. მაგალითად, თუ ფუნქციის დროებითი ცვლადი იყო იგივე ტიპის, როგორც შეყვანის არგუმენტი, მაშინ აშკარად აკრეფილ ენაზე, როდესაც შეიცვლება არგუმენტის ტიპი, დროებითი ცვლადის ტიპიც უნდა შეიცვალოს.

კარგი, თქვენ ხედავთ, რომ ორივე მიდგომას აქვს დადებითი და უარყოფითი მხარეები (ვინ ელოდა სხვას?), ასე რომ, მოდით ვეძებოთ გზები, რომ გავაერთიანოთ ეს ორი!

აშკარა აკრეფა არჩევანის მიხედვით

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

აშკარა ტიპის აღნიშვნის გარეშე დაამატეთ (x, y) = x + y - აშკარა ტიპის მითითება add :: (მთელი რიცხვი, მთელი რიცხვი) -> მთელი რიცხვის დამატება (x, y) = x + y

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

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

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

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

სურვილისამებრ ნაგულისხმევი აკრეფა

C ++ ენის ახალმა სტანდარტმა, სახელწოდებით C ++ 11 (ადრე C ++ 0x) წარმოადგინა ავტო საკვანძო სიტყვა, რომელიც შემდგენელს საშუალებას აძლევს დაასკვნას ტიპი კონტექსტიდან გამომდინარე:

შევადაროთ: // ხელმოუწერელი ტიპის ხელით სპეციფიკაცია int a = 5; ხელმოუწერელი int b = a + 3; // ხელმოუწერელი ტიპის ავტომატური დასკვნა int a = 5; ავტო b = a + 3;

Ცუდი არაა. მაგრამ ჩანაწერი დიდად არ შემცირებულა. მოდი ვნახოთ მაგალითი გამეორებლებით (თუ არ გესმით, ნუ გეშინიათ, მთავარია აღინიშნოს, რომ ჩანაწერი ავტომატურად გამოდის მნიშვნელოვნად შემცირდა):

// std :: ვექტორის ტიპის მექანიკური დაზუსტება vec = randomVector (30); for (std :: vector :: const_iterator it = vec.cbegin (); ...) (...) // ავტომატური ტიპის დასკვნა auto vec = randomVector (ოცდაათი); for (auto it = vec.cbegin (); ...) (...)

Ვაუ! ეს არის აბრევიატურა. კარგი, მაგრამ შეგიძლია რამე გააკეთო ჰასკელის სულისკვეთებით, სადაც დაბრუნების ტიპი დამოკიდებული იქნება არგუმენტების ტიპებზე?

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

// ტიპის int გაყოფის მექანიკური დაზუსტება (int x, int y) (...) // ავტომატური ტიპის გამოქვითვის ავტომატური გაყოფა (int x, int y) -> decltype (x / y) (...)

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

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

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

JavaScript - დინამიური / სუსტი / იმპლიციტური რუბი - დინამიური / ძლიერი / იმპლიციტური პითონი - დინამიური / ძლიერი / იმპლიციტური ჯავა - სტატიკური / ძლიერი / გამოკვეთილი PHP - დინამიური / სუსტი / იმპლიციტური C - სტატიკური / სუსტი / გამოკვეთილი C ++ - სტატიკური / ნახევრად- ძლიერი / აშკარა Perl - დინამიური / სუსტი / იმპლიციტური მიზანი -C - სტატიკური / სუსტი / გამოკვეთილი C # - სტატიკური / ძლიერი / აშკარა Haskell - სტატიკური / ძლიერი / იმპლიციტური საერთო Lisp - დინამიური / ძლიერი / იგულისხმება

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

აკრეფა - ინფორმაციული ერთეულებისათვის ტიპის მინიჭება.

ყველაზე გავრცელებული პრიმიტიული მონაცემებია:

  • რიცხვითი
  • პერსონაჟი
  • ლოგიკური

მონაცემთა ტიპის სისტემის ძირითადი ფუნქციები:

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

კლასიფიკაცია

პროგრამირების ენების აკრეფის მრავალი კლასიფიკაცია არსებობს, მაგრამ მთავარია მხოლოდ 3:

სტატიკური / დინამიური აკრეფა

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

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

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

დინამიური აკრეფა

Var luckyNumber = 777; var siteName = "Tyapk"; // ჩვენ ვგულისხმობთ რიცხვს, ვწერთ სტრიქონს var wrongNumber = "999";

სტატიკური აკრეფა

მოდით luckyNumber: ნომერი = 777; ნება siteName: string = "Tyapk"; // წამოაყენებს შეცდომას letnNumber: number = "999";

  • სტატიკური: Java, C #, TypeScript.
  • დინამიური: პითონი, რუბი, JavaScript.

აშკარა / ნაგულისხმევი აკრეფა.

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

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

ნაგულისხმევი აკრეფა

მოდით stringVar = "777" + 99; // მიიღეთ "77799"

აშკარა აკრეფა (გამოგონილი JS მსგავსი ენა)

მოდით wrongStringVar = "777" + 99; // აგდებს შეცდომას let stringVar = "777" + სიმებიანი (99); // მიიღეთ "77799"

ძლიერი / ფხვიერი აკრეფა

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

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

ძლიერი აკრეფა (გამოგონილი JS მსგავსი ენა)

დავუშვათ არასწორი რიცხვი = 777; wrongNumber = არასწორი რიცხვი + "99"; // ჩვენ ვიღებთ შეცდომას, რომ სტრიქონი ემატება რიცხვით ცვლადს wrongNumber let trueNumber = 777 + ნომერი ("99"); // მიიღეთ 876

ფხვიერი აკრეფა (როგორც js– შია)

მოდით არასწორი რიცხვი = 777; wrongNumber = არასწორი რიცხვი + "99"; // მიიღო სტრიქონი "77799"

  • მკაცრი: Java, Python, Haskell, Lisp.
  • Lax: C, JavaScript, Visual Basic, PHP.
გაუზიარე ეს