Loading...
Filkolev avatar Filkolev 4482 Точки

[OOP] Exam Preparation - December 14 2015 - Empires

Здравейте колеги,

В Judge е създадено ново състезание по ООП с примерна изпитна задача в новия формат на изпита (без скелет). Състезанието може да достъпите ТУК.

Като ресурси са качени условието и решение на Java. Състезанието е отворено за C# и Java архиви, но към момента има технически проблем със събмитването на Java zip, който би трябвало да бъде разрешен близките дни. Със C# проекти не би трябвало да срещнете затруднения.

Подготовката в понеденик ще бъде на C#. Съществени разлики в дизайна на решението на C# и на Java няма, но ако някой има желание да решава изпита другата седмица на Java може да погледне решението, което е качено.

Ако имате неясноти или въпроси по задачата, питайте тук.

16
C# OOP Basics 12/12/2015 21:16:45
supersane avatar supersane 234 Точки

Аз понеже ще започвам фундаменталс март месец, започнах да си гледам от сега видеата от курсовете. Разгледах това изпитно задание и имам един въпрос, който може би е уточнен, но аз не знам. Понеже пише input & output independency, а тестовете в джъдж, ще се подават през конзолата, това значи ли, че трябва да се направи нещо като меню, в което да се избира какъв да е входа и изхода, но същевременно да хардкоуднато за през конзола, заради judge тестовете?

0
Filkolev avatar Filkolev 4482 Точки

Няма как да се направи меню. Тук е нужна абстракция и енджина да не знае откъде идва входа и накъде отива изхода. За да стане това енджина трябва да работи с други класове, чиято конкретна имплементация него не го интересува. Във всеки един момент имплементацията може да се смени и изходът например да отива към файл вместо към конзолата.

Ако все още не ти е ясно какво имам предвид, виж пак лекцията, където се говори за loose coupling, или още по-добре - изгледай лекцията за SOLID принципите от КПК курса.

0
supersane avatar supersane 234 Точки

Явно още не съм покрил тази част от курса, благодаря за отговора :)

0
badbutcher avatar badbutcher 144 Точки

А мога ли да попитам кога ще качите видеото от 11-петък когато RoYaL решаваше Capitalism?

5
RFilipov avatar RFilipov 136 Точки

Във Building трябва да имаш булев метод, който дава информация дали една сграда може да произведе нещо в момента на извикване на метода.

В BuildingFactory няма ли да е по-добре 

var buildingType = Assembly
                .GetExecutingAssembly()
                .GetTypes();

да се изнесе в статична променлива, за да не го смяташ всеки път, а само веднъж когато стартира програмата.

Може да приложиш reflection и при командите вместо switch ако ти се занимава.

0
13/12/2015 20:47:02
Filkolev avatar Filkolev 4482 Точки

Като цяло ми се струва ОК. Забелязах в класа Building, че връщаш null ако не е дошло време за произвеждане на единица/ресурс, което е грешно по условие - трябва да се хвърли грешка.

В същия клас имаш и логика по създаване на юнит, което нарушава single responsibility принципа - някаква си сграда бърка по асемблито с рефлекшън, за да инстанциира някакъв клас. По-добре това да се изнесе в специализиран клас, фактори. Това ще ти позволи и да развържеш сградата от конкретен юнит, понеже може да пазиш някакво пропърти като стринг (името на типа юнит), а при създаване на юнит да викаш факторито като му подадеш стринга. Друг вариант е всяка сграда да пази типа юнит, който произвежда, като поле (т.е. логиката по вадене на типа да някъде отвън и юнита да се вкарва през конструктора като dependency); при викане на метода Produce да се инстанциира нов обект от въпросния клас. Това обаче е обвързване, защото класа Archery например трябва да знае за съществуването на Archer, което ако може да се избегне е една идея по-добре.

ToString() може да го извадиш в базовия клас, няма причина да е в конкретните класове, повтаря се една и съща логика.

В юнита малко куца сетъра за здравето. Ако приемем, че някога в бъдеще ще има битки, то логично е здравето да намалява и в даден момент да може да стане 0. Не е много приятно всеки път като умре юнит някъде да се мята ексепшън. Примерно юнит има 20 кръв и някой му набива 35 демидж, логичната развръзка е: юнита умира и остава на 0 кръв (понеже не може да е на минус).

Може да помислиш и за разширяемост малко. Ако аз реша да разширя приложението, мога ли да го направя? За някои неща ползваш рефлекшън и е ясно, че добавяне на клас е достатъчно, но има други места, където не мога да пипам. Какво би трябвало да направиш (имайки текущия код), за да добавиш нова команда? Тук може да се направят нещата малко по-добре.

0
RFilipov avatar RFilipov 136 Точки

Понеже в другата тема ме пита дали бих добавил нещо по условието сега се сещам за Health валидацията. Нали всеки път когато правим инстанция на даден Unit подаваме базови константи за Health. Тук в пропъртито ще сложим валидация, обаче после като започне мелето всеки път ще трябва да смятаме дали няма Health да падне под 0 и да даде грешка (и да го приравняваме на 0 всеки път). Имаме доста проверки и сметки занапред срещу една валидация при създаване на обекта. Тая валидация конкретно за Health не е ли малко излишна в случая. Вместо валидация може да се направи това:

            set
            {
                this.health = value;

                if (this.health < 0)
                {
                    this.health = 0;
                }
            }

 

0
13/12/2015 21:30:34
SvilenYanovski avatar SvilenYanovski 80 Точки

Обработвате ли някъде ексепшъните, които хвърляте за валидация на параметрите, че нещо не се ориентирам в кода? (java)

0
Filkolev avatar Filkolev 4482 Точки

Не, никъде не ги обработвам. В случая, когато не вадим аутпут от всяка команда, не е необходимо да се прави. Случва се нещо непредвидено - спираме приложението. Според мен нормален сценарии.

1
Filkolev avatar Filkolev 4482 Точки

Здравейте,

Качих ресурсите на страницата на курса, както и в Judge. Видеото е пуснато да се обработва, утре би трябвало да е достъпно.

Направих някои козметични промени по кода, тъй като не остана време някои неща да се довършат. Основно добавих нужните валидации, което е работа за 15 минути. На повечето места, където съм променял нещо, съм сложил кратки коментари. Понеже едното фактори направих с рефлекшън към края, направих и другото по същия начин.

По-сериозни промени по структурата са преместването на event handler-ите в отделен файл (преди бяха в класа Building), както и промяната на интерфейсите, свързани със сградите. При наличието на event-и няма нужда да имаме публични методи за произвеждане на единици или ресурси, достатъчно е да дефинираме event-ите и който се интересува от тях ще получи каквото му трябва през event аргументите.

Както обсъждахме по време на подготовката, някои методи направих virtual с цел да позволим разширяване на функционалността в бъдеще.

Тъй като накрая вече не бях особено фокусиран, струва ми се, че не обясних достатъчно качествено event-ите, затова сложих по някой друг коментар на местата, където се случва нещо интересно, свързано с тях.

Ако нещо е останало неясно - питайте. Ако имате предложение какво може да се промени с цел допълнително подобряване качеството на кода, споделете. При наличието на такава свобода на действие има доста неща, които може да приложим и които да доведат до някакви ползи.

6
valiobar avatar valiobar 29 Точки

Здравейте това е моето решение :https://github.com/valiobar/empire-exam/tree/master/Empire

Точките в Judge ги докарах,но малко на магия ще се радвам на коментар.

 

 

 

0
RoYaL avatar RoYaL Trainer 6849 Точки

Не трябва да имаш такива директни инициализации, които не позволяват подменяне:

        private Database data=new Database();
        private Gold gold =new Gold();
        private Steel steel=new Steel();
        private UserInterface userIntarface=new UserInterface();

Това горе долу е същото като просто да ползваш Console.WriteLine(); тъй като никой отвън не може да подмени например, UserInterface-а.

И въпреки UserInterface-а, в командите пак ползваш Console.WriteLine(). Това е доста подвеждащо за програмиста, защото той би си помислил, че системката ще работи със зададения от нея UserInterface, а де факто какъвто и интерфейс да сложиш, накрая командите принтират на конзолата.

Проверката дали нещо е от някакъв тип и какво да принтира противоречи на полиморфичния принцип. Арчърът да принтира каквото трябва, Swordsman-а и той. Не да ги кейз-ваш кое какво е

Дефолтните параметри е хубаво да минават през пропъртита. Т.е. някакъв константи със стойности, които после например се подават на базовия конструктор и той подава тези стойности на пропърти сетъра.

Като цяло избягвай да ползваш типове на променливи от конкретен клас и методи, които приемат точно конкретни класове. Използвай интерфейси за тази цел.

2
nasun4o avatar nasun4o 15 Точки

Здравей може ли да качиш и решението което беше без Евенти ? :)

1
Filkolev avatar Filkolev 4482 Точки

Може да погледнеш решението на Java. Просто в интерфейса има по два метода - Produce и пропърти CanProduce. При ъпдейтването на сградите енджина всеки път проверява за всяка сграда дали може да произведе единица или ресурс и ако може - вика Produce метода и вкарва произведеното в базата.

0
Filkolev avatar Filkolev 4482 Точки

Направиха ми две неща впечатление на пръв поглед. Съвсем бегло подгледнах нещата.

Имаш неймспейс Libraries и в него някакъв принтер и три ексепшъна? Нещо не ми се връзва логиката. Ексепшъните по-добре ги сложи в собствен неймспейс за ексепшъни, пък принтера може да е в нещо като Utils например.

Ползването на делегати за IO ми хареса, но само като за спорта. Не мисля обаче, че е практично, това по-скоро трябва да са отделни класове. В момента може да подадеш само един метод, например Console.ReadLine. Но нямаш възможност да добавиш повече логика, а в повечето случаи вероятно ще се наложи. За да прочетеш ред от файл един метод не ти е достатъчен, трябва да отвориш файла преди това, а след като приключиш с него - да го затвориш. В повечето случаи ще трябва да отвориш някакъв стрийм, за да започнеш да четеш и нямаш готов един метод, който да подадеш на конструктора и всичко да тръгне от веднъж. Иначе идеята е супер, делегатите явно си ги разбрал добре, за да ги ползваш по този начин.

0
Raskolnikov avatar Raskolnikov 102 Точки

Здравейте :)

Ето и моя вариант на задачата. Дава ми 100 точки, но не знам дали е добре написан кодът. Ще съм благодарен на градивна критика.

https://github.com/Razkolnikow/Empires

Мерси.

0
Filkolev avatar Filkolev 4482 Точки

Структурата в GameObjects неймспейса не е добра, вътре има 10+ класа с различни задачи. Раздели ги в още неймспейси.

В енджина има доста проблеми. На първо място, обвързан е с много конкретни класове. За да се избегне това, най-добре подай всичко отвън - през конструктора. В момента уж работиш с интерфейси, но това е само фиктивно, понеже изрично казваш с кои класове работиш. Изобщо думата new щом я има, значи правиш coupling, който трябва да помислиш как да избегнеш.

Всеки път инициализираш Output при изпълнение на команда. Освен, че важат горните забележки, това е излишно, не ти трябва нов обект всеки път като искаш да принтираш нещо. 

В момента енджина освен че борави с конкретни сгради, няма и начин да бъде разширен. Какво трябва да направя, за да добавя нова команда, ако ми е забранено да пипам по енджина в сегашния му вид?

Разгледай как се ползват речниците, доста ще ти помогнат да избегнеш нелицепиятните проверки за типа на юнитите и ръчното броене.

Принтирането на сградите трябва да е вътрешна логика - сградата казва какво трябва да се принтира (още от първите домашни се упражнява това), енджина просто го принтира. В момента сградата показва куп информация на външния свят, само и само енджина да може да я достъпи и изпечата.

Смятането на ресурсите го правиш с много чуплив подход. Един обект ресурс ти считаш, че винаги е с фиксирано количество, но това може да не е така. Може да има сграда, която произвежда 100 злато, какво правим тогава? По условие ресурсите трябва да имат и количество.

Проверката най-долу коя сграда какво произвежда не трябва да е в енджина. Всяка сграда трябва да си знае какъв тип войник създава и имайки тази информация, може просто да я покажеш през пропърти. Ако в един момент Archery-то започне да произвежда конници, си отваряш работа по промяна на целия енджин.

2
Raskolnikov avatar Raskolnikov 102 Точки

Бърз и полезен отговор.

Мерси!

0
justBeOk avatar justBeOk 194 Точки

Имам един въпрос. Трябва ли на изпита да правим всичко на 100% както е казано в условието? Питам защото в условието на Empires пише: "Building - can produce a unit and a resource", а по начина по който ти си го направил, самият клас Building не produce-ва unit-и и resource-и, а има отделени класове (имплементиращи IResourceFactory и IUnitFactory), които произвеждат обекти.

0
Filkolev avatar Filkolev 4482 Точки

Да, но в сградите има методи, които викат факторитата и връщат каквото трябва. Каква е логиката по инициализране на обекти вътре не е толкова важна, за потребителя де факто сградата създава юнити и ресурси, а как точно го прави под капака не е съществено. Не е написано в камък, че трябва една сграда собственоръчно да създава тези обекти, а че има някакъв механизъм да го прави. И в случая прецених, че е по-добре тази логика да е в други класове, които сградата ползва вътрешно. Аналогично енджинът може да принтира съобщения на изхода, но го прави с помощта на друг клас, не ходи сам да пише по конзолата.

Не е нужно условието да се приема чак толкова буквално. Същественото е една сграда да има поведението, което се изисква, а как ще имплементираш върешната логика е твое решение.

1
justBeOk avatar justBeOk 194 Точки

Рабирам. Благодаря за изчерпателния отговор.

0
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.