Skip to content

Task 2: Questions

nikagogibedashvili edited this page Apr 1, 2015 · 35 revisions

კითხვები

1. დაასაბუთეთ რატომ არის მიზანშეწონლი iAlive()და kill მეთოდების final-ად გამოცხადება? შესაძლებელია თუ არა არსებული დიზაინის ფარგლებში "უკვდავი" არსების შექმნა?

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

2. იპოვეთ სად არის გამოყენებული 'Composite' და რა მიზნით?

CompositePopulator კლასი არის 'Composite'-ის სტანდარტული მაგალითი. Populator-ის ფუნქცია არის სამყაროში ახალი Being-ის ჩამატება. მე შევქმენი Populator ინტერფეისის მქონე ორი კლასი MovingPoisonPopulator და MovingMousePopulator. პირველი ქმნის მოძრავ, გარკვეული თვისებების მქონე საწამლავს, მეორე მოძრავ კონკრეტული ტიპის თაგვს. ჩემს მიერ დასამატებელ level-ში უნდა ყოფილიყო ორივე ტიპის Being. ამიტომ საჭირო გახდა ჩემს მიერ შექმნილი Populator-ების კომპოზიციის გაკეთება. როცა უნდათ, რომ ერთი ინტერფეისის რამდენიმე განსხვავებული იმპლემენტაციის შერწყმა მოახდინონ, იყენებენ 'Composite'-ს. CompositePopulator კლასი იძახებს ყველა გადაცემული Populator-ის populate() მეთოდს და იქმნება ყველა საჭირო Being, ჩემს შემთხვევაში MovingMouseBeing და MovingPoisonBeing.

3. იპოვეთ სად არის გამოყენებული Singleton და რა მიზნით?

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

4. იპოვეთ სად არის გამოყენებული Facade და რა მიზნით?

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

5. იპოვეთ Template Method-ის ყველაზე თვალსაჩინო გამოყენება. დაასაბუთეთ საჭიროება.

TODO - აქ ჩაწერეთ თქვენი პასუხი

6. იპოვეთ სად არის გამოყენებული Abstract Factory და რა მიზნით?

Abstract Factory გამოყენებულია ViewFactory-ის სახით. ეს საშუალებას გვაძლევს რომ საჭიროების შემთხვევაში შევქმნათ კონკრეტული ViewFactory, მაგალითად TerminalViewFactory ან SwingViewFactory ისე რომ ViewFactory-ის გამომყენებელ ობიექტს არ დასჭირდეს რაიმე ცვლილება. ასევე მას არ "ეცოდინება" ეს Factory-ები კონკრეტულად რა ობიექტებს "დაამზადებენ". ამ შემთხვევაში ViewController-ს არ ექნება რაღაც კონკრეტული ViewFactory და არ დაგვჭირდება მისი ცვლილება ის გამოიყენებს იმ ViewFactory-ის რომელსაც მივაწვდით.

7. იპოვეთ სად არის გამოყენებული Observer და რა მიზნით?


ობსერვერი გამოყენებულია MazePresenter (Observable) - ისა და CellUpdateListener (Observer) - ის შვილობილი კლასების (TerminalMazeViewUpdater, SwingMazeViewUpdater) საურთიერთობოდ. MazePresenter - ში შესაძლებელია setCellUpdateListener(CellUpdateListener listener) მეთოდის გამოძახებით დავარეგისტრიროთ CellUpdateListener (Observer) - ები შემდგომ კი პროგრამის მუშაობისას პრეზენტერი ობსერვერებს შეატყობინებს კონკრეტული უჯრის შიგთავსის მდგომარეობას, დამკვირვებლის (Observer) ვალდებულება კი არის ის, რომ მოახდინოს ამ ცვლილების გრაფიკაში გარდასახვა. ამ შემთხვევაში ობსერვერი გამოყენებულია იმ მიზნით, რომ გრაფიკას თავად არ "ეწვალა" ცვლილებაზე ინფორმაციის მისაღებად და წამდაუწუმ არ ეგზავნა MazePresenter - ისთვის მოთხოვნები კონკრეტული უჯრების შესახებ. ასეთი რეალიზაციით, როგორც უკვე ვთქვი გარკვეული ცვლილების შემთხვევაში თავადვე მოუვა დამკვირვებელს (Observer) ინფორმაცია და იგი ვალდებული იქნება მოახდინოს სათანადო რეაგირება. ასევე Observer - ი ერთ-ერთ მთვარ ნაწილს წარმოადგენს ჩვენთვის კარგად ნაცნობ Model–View–Controller (MVC) არქიტექტურულ პატერნში, რომელის მიხედვითაც არის ეს პროექტი დაგეგმილი.

8. დააკვირდით რორგორ გამოიყენება Level და Topology ახსენით დანიშნულება. აღწერეთ მსგავსება (თუ შეინიშნება) Strategy პატერნთან.

Level Class გვევლინება ე.წ Context კლასად რომელსაც აქვს Strategy ობიექტზე Reference . სტრატეგია Level Class-ს კონსტრუქტორში ეთითება: Level(String name, Topology topology, Populator generator). გვაქვს Strategy interface სახელად Topology რომელსაც მხოლოდ 1 მეთოდი getNextTo() აქვს. გვაქვს ასევე მისი კონკრეტული იმპლემენტაცია EndlessTopology, რომლის კონკრეტული იმპლემენტაციაც არის SphericTopology და სწორედ ეს სტრატეგია ეთითება App-ის main მეთოდში Level-ს შექმნისას: Level level1 = new Level("Very Simple Level",new SphericTopology(),new SingleMousePopulator()); თავდაპირველ Snake-ში მეტი Strategy-ს იმპლემენტაცია არ გვაქვს თუმცა ამ პრობლემის ამგვარი გადაწყვეტა შესაძლებლობას გვაძლევს ნებისმიერ დროს განვავრცოთ ამოცანა და დავუმატოთ სხვა Topology Strategy -ები სხვა Level-ებისთვის, გავართულოთ თამაში და გავამრავალფეროვნოთ

9. იპოვეთ სად არის გამოყენებული View Controller და რა მიზნით?

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

10. რატომ არის აუცილებელი Point კლასზე equals() და hashCode() მეთოდების იმპლემენტაცია?

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

სანამ უშუალოდ ამ კითხვას ვუპასუხებდე გავაკეთებ მცირე აღნიშვნას , რომ თუ ორი ობიექტი არის ერთი და იგივე ისინი მუდამ ერთი და იგიივე შედეგს უნდა აბრუნებდნენ როცა ვიძახებთ equals() და hashCode() მეთოდებს . ასე რომ ამ რი მეთოდის გადატვირთვა აუცილებელი იყო ჩვენთვის რათა ზუსტად გვცოდნოდა რომელიმე Point-ის არსებობის ან არ არსებობის შესახებ . უფრო ზუსტად: ამ მეთოდების არ გადატვირთვის შემთხვევაში ჩვენ მოგვიწევდა გამოგვეყენებინა Object კლასის ეს ორი მეთოდი . ეს კი გარკვეულ პრობლემებს წარმოქმნიდა რამეთუ default იმპლემენტაციით equals() მეთოდი აბრუნებს true -ს მხოლოდ იმ შემთხვევაში , როცა ორი ობიექტი მეხსიერების ზუსტად ერთი და იგივე მისამართზე მიუთითებს , hashCode() მეთოდი კი ყველა ობიექტისთვის უნიკალურ hash-code აბრუნებს . ერთი შეხედვით საკმარისი იქნებოდა მხოლოდ equals() მეთოდის გადატვირთვა , მაგრად ეს ასე არ არის ჩვენ შეიძლება გვქონდეს ერთი და იგივე ობიექტი თუმცა ამის შესახებ ვერ ვერაფერი შევიტყოთ რადგან ამ ორ ობიექტს სრულიად განსხვავებული hash-code აქვს . ასე რომ ჩვენ ვალდებულები ვართ გადავტვირთოთ hashCode() მეთოდიც , რათა დავიცვათ წესი , რომ თუ ორი ერთმანეთის ტოლია მაშინ მათი hash-code -ებიც ტოლი უნდა იყოს . რაც შეეხება ამ მეთოდების არ გადატვირთვას Direction და CellPosition -ზე შეგვიძლია ვთქვათ რომ ეს არის იყო აუცილებელი , რადგან default მნიშვნელობებიც რომელიც ზემოთ ავღწერეთ წარმატებით ასრულებენ დაკისრებულ მოვალებოას კერძოდ equals() მეთოდის ნაცვლად ვიყენებთ == ოპერატორს , ხოლო მათი hash-code საერთოდ არ გვაინტერესებს

11. ჩამოთვალეთ კლასები და მეთოდები რომლებიც ზედმეტი გახდებოდა View-სა და Model-ს შორის Relaxed Layering-ი რომ იყოს დაშვებული. დაასაბუთეთ რატომ?

Relaxed layering ის შემთხვევაში ზედმეტი კლასები გახდებოდნენ : CellContent , CellPosition , DirectionKey და ზედმეტი მეთოდები : convertToCellContent(), convertToDirection(). რადგან ჩვენ რეალურად ვქმნით BeingKind, Point, Direcgtion კლასის ასლებს და მათ კონვერტორებს View layer-ში, რომ ავირიდოთ Model layer- ის კლასებზე დამოკიდებულება.

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

მოიყვანეთ ორ-ორი კონკრეტული მაგალითი Snake-იდან 2 x Composition, 2 x Aggregation. დაასაბუთეთ თუ რატომ არ იქნებოდა თითოეულ კონკრეტულ შემთხვევაში საპირისპიროს გამოყენება მიზანშეწონილი. მაგ. Universe o-- Being თუ Universe *-- Being? რატომ ერთი და არა მეორე?

აგრეგაციის 2 კონკრეტული მაგალითია:

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

Level o-- Topology === ამ მაგალითშიც იგივე ლოგიკის გამოყენება შეგვიძლია და ასევე კიდევ უფრო დაკონკრეტება,რომ თუნდაც Universe კლასიც იყენებს Topology კლასს,რაც უკვე ამტკიცებს Topology-ის უნარს იარსებოს Level-სგან დამოიკუდებლად.

კომპოზიციის 2 კონკრეტული მაგალითია:

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

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

13. რომელი კლასების შეცვლა გახდებოდა საჭირო model-ში Snake-ის 3D სამგანზომილებიან ვარიანტად გადასაკეთებლად?

შეაფასეთ მიღებული შედეგი Single Responsibility Principle და Separation of Concerns პრინციპების თვალსაზრისით (რამდენად დაცულია, რატომ)?

სახელი შეიცვლება კომენტარი
Being.java -
BeingKind.java -
CompositePopulator.java -
Configuration.java -
Direction.java + დაემატება მესამე განზომილების მიმართულებები, შეიცვლება გამოთვლის ლოგიკა
EndlessTopology.java -
FixedBeing.java -
GameFacade.java + გველის თავის კოორდინატის გამოთვლისას დაემატება Z კოორდინატი
Level.java -
MouseBeing.java -
MovingBeing.java -
Point.java + მხოლოდ დაემატება Z ცვლადი
PoisonBeing.java -
Populator.java -
RandomPositionPopulator.java + დაემატება Z ღერძის შესაბამისი გამოთვლები, კერძოდ შემთხვევითი Z კოორდინატი
SingleMousePopulator.java -
Size.java ? შეიძლება ესეც გახდეს შესაცვლელი, თუ ცალკე კლასს არ გავაკეთებთ სადაც მესამე განზომილება იქნება გათვალისწინებული
SphericTopology.java + დაემატება Z ღერძის შესაბამისი გამოთვლები
Topology.java -
Universe.java -

როგორც ჩანს შესაცვლელი მხოლოდ 6 კლასია შესაცვლელი და ყველა ცვლილება გამოწვეულია ფუნქციონალის დამატებით (და არა ერტ-ერთი კლასის ცვლილებით), შესაბამისად Single responsibility principle დაცულია. რაც შეეხება Separation of Concerns: Populator და Being არის ნათელი მაგალითი იმისა, რომ ინტერესთა გამიჯნვის პრინციპი დაცულია.

14. GameFacade-ის კონსტრუქტორში ხდება Snake და Universe კლასების შექმნა. რა შეზღუდვებს ადებს ეს თამაშის გაფართოებადობას და ტესტირებადობას. როგორ შეიძლება ამის შეცვლა?

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

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

TODO - აქ ჩაწერეთ თქვენი პასუხი

16. რა მთავარი უპირატესობა აქვს აქ გამოყენებულ Model–view–presenter-პატერნს? გადახედეთ Issue-ებს და მოახდინეთ იმ Issue-ების იდენტიფიკაცია რომლების განხორციელებაც ვერ მოხერხდებოდა (ან გაძნელდებოდა) ამ პატერნის არ გამოყენების შემთხვევაში. დაასაბუთეთ რატომ?

მოცემული პატერნი ერთმანეთისგან აცალკევებს თამაშის ლოგიკას და მის გამოსახულებას, რაც საშუალებას გვაძლევს ერთმანეთისგან დამოუკიდებლად ვცვალოთ ისინი ზედმეტი ძალისხმევის გარეშე. Model-view-presenter პატერნის გამოუყენებლობის შემთხვევაში, სავარაუდოდ ყველა იმ issue-ს რეალიზაცია გართულდებოდა, რომელიც არც თუ უმნიშვნელოდ ცვლის თამაშის ლოგიკას ან გრაფიკას. მაგალითად Moving Mouse and Poisone, Evil Snake, Ghost Mouse and Poison, Escaping Mouse, Hungry Snake მოითხოვს თამაშის ლოგიკის შეცვლას, სამყაროს ემატება ისეთი კომპონენტები, რომლებიც სხვებს გვანან ვიზუალით, მაგრამ მათი საქციელი განსხვავებულია. მათი რექლიზაცია ჩემი აზრით საკმაოდ რთული უნდა იყოს Model-view-presenter-ის გარეშე, რადგან ყოველი ორი არსების ურთიერთქმედებისას საჭირო იქნებოდა გაგვერკვია რა არსებები არიან ისინი და გვექნებოდა უამრავი ვარიანტი, რომლებისთვისაც განსხვავებული ლოგიკა არის საჭირო. ასევე საკმაოდ რთული იქნებოდა Huge Map-ის რეალიზაცია, Swing კომპონენტების იმპლემენტაციას კი ალბათ მთლიანი თამაშის გადაწერა დასჭირდებოდა.

20. გაეცანით Liskov substitution principle-ს მოძებნეთ მაგალითები ჩვენი კოდიდან სადაც ის დაცულია. მოიყვანეთ აგრეთვე (შესაძლო) ნეგატიური მაგალითი ჩვენი კოდიდან. ანუ აღწერეთ ისეთი შესაძლო დარღვევა, რომელიც პრობლემებს გამოიწვევდა.

ჩვენი თამაში აქტიურად იყენებს Liskov substitution principle-ის ძირითად პრინციპებს. პირველ რიგში, ვთქვათ Being interface-ზე. ის თავიდანვე ორ ნაწილადაა გაყოფილი და MovingBeing და FixedBeing ერთმანეთისგან გამიჯნულია. მათ ფუნქციონალში არსებული ცვლილება აუცილებელს ხდის ერთმანეთისგან გამიჯვნას Liskov substitution principle-ის მიხედვით, რადგან moveTo არის ფუნქციონალის მნიშვნელოვანი ასპექტი. ასევე არის დაყოფილი populator-ები და presenter-ები. პოპულატორ ინტერფეისს ჯერ აიმპლმენტირებს RandomPositionPopulator, რაც ამ ამოცანაში აბსტრაგირებისთვის აუცილებელია, ხოლო შემდეგ კი პოპულატორები უკვე ერთმანეთისგან დამოუკიდებელი იერარქიით ვითარდება. იგივე შეიძლება ითქვას პრეზენტერებზე, საწყისი View ინტერფეისი დაყოფილია რამდენიმე abstract class-ად, რათა შესაძლებელი იყოს მათი რამდენიმე სხვადასხვა იმპლემენტაცია: swing, terminal. ცალკე ვიტყოდი snake-ებზე და SnakeFactory-ებზე. ჩვეულებრივი snake-ისა და hungrySnake-ის კონსტრუქტროები ერთმანეთისგან განსხვავებულია, თუმცა მათი ფაქტორებს ერთი მშობელი ჰყავთ ერთადერთი აბსტრაქტული მეთოდით. ამ იმპლემენტაციის პირობებში ეს დასაშვებია, რადგან hungrySnake-ს ლიმიტად მუდმივად 80 გაადეცემა ისე, რომ ამ ცვლადის გარედან მიწოდება საჭირო არ არის. თუმცა, იუზერს ამ ცვლადის მიწოდება რომ შეეძლოს, მაშინ createSnake აბსტრაქტული მეთოდი ორივე კლასისთვის აღარ გამოდგებოდა, რადგან არგუმენტები და ყოფაქცევა სხვადასხვა ექნებოდათ, რაც ლისკოვის პრინციპს ცალსახად ეწინააღმდეგება.

28. გაეცანით Open-closed principle-ს მოძებნეთ მაგალითები ჩვენი კოდიდან სადაც ის დაცულია. მოიყვანეთ აგრეთვე (შესაძლო) ნეგატიური მაგალითი ჩვენი კოდიდან. ანუ აღწერეთ ისეთი შესაძლო დარღვევა, რომელიც პრობლემებს გამოიწვევდა.

Clone this wiki locally