[Java - OOP Advanced - Exercises] Problem08 - MilitaryElite
Здравейте!
Преди малко, докато обмислях контструкцията която трябва да направя за тази задачка, доста се позачудих за класа Soldier и интерфейса който се изисква за него. Ясно ми е, че идеята е да се упражняваме в писането на интерфейси, но ще попитам за принцната ситуация.
Тъй като field-овете на Soldier, по условието на задачата, ще се достъпват само от наследниците му и то за toString() метода. Има ли смисъл да правим гетърите публични и съответно въобще да имаме интерфейс ISoldier?
Поздрави!
П.П. Всъщност ако ползваме гетърите само за toString(), реално ни стигат да са private за всички класове нали?
Поправете ме ако греша, но toString() методите не трябва да са private. Доколкото разбирам, @Override toString() се прави за да дефинира как се отпечатва даден обект. Примерно: System.out.print(soldier)
Също, ако не се лъжа, toString() няма нужда да се декларират в интерфейса, защото се наследяват от Object.
Поздрави,
Ивелин Тенев
Да прав си, така е - toString си е публичен метод на Object. Няма как да бъде private. Става дума за публичните гетъри от класовете на войниците. Понеже ги пишем към интерфейси, интерфейса ни задължава те да са публични, но иначе за целите на задачата можеше да си останат private защото единственото място където се ползват е в toString() метода на войника. В случая се упражняваме да пишем класовете към интерфейси.
Да, не бях разбрал напълно предишния ти пост. Снощи до 3 си играх с това и още не съм се преборил :)
Доколкото разбирам правилата са следните:
Към ОП: Ако не се лъжа, field-вете на Soldier не трябва да се достъпват от наследниците му, а да се преизползва super.toString() метода.
Поздрави,
Ивелин Тенев
private Properтита има смисъл и дори си е почти задължително за сетърите.
В случая в задачата понеже всички наследници на Soldier имат неговите атрибути
: Id , firstName , lastName
Няма нужда да имат публични гетъри тъй като единствената им употреба е в класа Soldier , но все пак ако се налага някъде нещо за някой метод е по-добре да го има като гетър и да се достъпва. А и целта е да се упражнят интерфейси в тая задача , затова не можем да избягаме от public
Привет,
Ето как го разбирам аз:
Когато правим задача с ООП не гледаме да нагодим архитектурата към условието на конкретната задача, защото много често в практиката се налага да се правят разширения. Мислим какви евентуално задачи могат да дойдат като разширения и как с минимални промени ще можем да ги решим. Тоест, винаги когато един метод е "безопастно" да бъде public го оставяме така. В повечето случаи няма нужда get методите да са скрити, защото няма да стане проблем ако те бъдат прочетени. Set методите, обаче много често могат да създадът проблеми: Например, ако правим клас Person с id, firstName и lastName не е проблем тези три полета да се четат, можем да пуснем и public setters за firstName и lastName, тъй като предполагаме, че е възможно човек да си смени първото и последното си име, но не слагаме public setter за id, защото ако то се смени има голяма вероятност да се счупят различни мапове или листове, които разчитат на това id. За protected полетата по същия начин, само когато могат да доведат до проблем, например: Имаме няколко класа, които имат метод execute и независимо какво прави този метод винаги има примерно try catch или някаква валидация в началото. Съответно искаме да изнесем валидацията и еxecute в абстрактен клас:
abstract class Test {
public abstract validate();
protected abstract doExecute();
public execute {validate(); doExecute();}
}
Тоест няма проблем клас от вън да валидира, ако обаче оставим doExecute() публичен има голяма вероятност от Exception, т.е. искаме винаги когато се вика doExecute, преди това да се направи валидация на данните.
Подобно е и с вдигането на нивото на абстракция. Задачата може да се реши и с по ниско ниво на абстракция, но мисленето е насочено отново към това какви промени могат да дойдат и как най - лесно да си преизползваме кода. И тук идва трудната част (поне според мен), до къде да се спре с тази абстракция, защото все пак е не възможно да се предвиди всичко, а и не е ОК да се изпишат тонове код за малка задача.
Хубаво е да имаш getters.
От това, което прочетох тези дни много програмисти поставят валидацията в getter-a.
Причината за това е, че перфектна програма няма, и така си спестяваш хвърлянето на exeptions, защото може да не се наложи да иползваш това поле.
Хм, защо валидация в getter? По - скоро в setter, защото реално там се задава стойността, getter-a освен да я върне друго не прави.
"защото може да не се наложи да иползваш това поле." - това не го разбирам, може ли да поясниш ?
Най-добрия вариант е в сетъра, но в сетъра гърми при подаване а в гетъра при пойскване.
Ако подадеш нещо грешно ще ти гръмне още на входа иначе ще ти гръмне когато го извикаш, ако го извикаш.
Във връзка с това, трети тест ми гърмеше с IllegalArgument, при създаване на Engineer с невалиден Corp. Пазя Corp в Enum и имам валидация във setter-а, който хвърля еxception, който обработвам при инстанциирането на обекта. Теста ми гърмеше обаче защото се опитвах да взема Corp.valueOf от невалидния вход, преди да го подам на конструктора.
Въпросът ми е, в такива случаи е по-добре да се подават аргументите като стринг, и да се evaluate-ват в setter-ите, или да се прави валидация на входа, освен в setter-а?
Поздрави,
Ивелин Тенев
Според мен не трябва да има валидация в getters, защото ако ще гърми там един вид вече си си счупил класа. Валидацията трябва да е в setter-a или конструктура.
Относто enum-a, не мисля, че класа трябва да прима string изобщо (нито в сетърите, нито в конструкторите), директно да си приема enum: void setStatus(Status status) и който ще ползва класа да се погрижи да си набави enum value-то. Същото е като при integer, когато един клас трябва да работи с integer поле, то няма set метод, който приема стринг и вътре прави Integer.valueOf(), който вика класа, той да се грижи да му подаде съответните типивe.
Ето ги мойте имплементации на класа и интерфейса с enum:
https://github.com/kosio197/InterfacesAndAbstraction/blob/master/src/bg/softuni/militaryelite/model/contracts/Mission.java
https://github.com/kosio197/InterfacesAndAbstraction/blob/master/src/bg/softuni/militaryelite/model/MissionImpl.java
щом 1 поле няма да ти потрябва изобщо защо мястото му е там ?
Няма да се наложи в така поставеното условие на задачата, но по - късно може да се наложи при разширение на задачата. А нали все пак трябва да гледаме кода да може да се презиползва, т.е. лесно да се разшири при нови изисквания, нали за това е и това високо ниво на абстракция, което се изисква в тази задача. То реално ако ще е само печата(да минат тестовете на задачата) и се предполага задачата да не се разширява повече, защо изобщо са тези класове и интерфейси? Getter-a на мисията може да потрябва за много неща в последствие, за различни статистики на пример. И там вече ако съм бутнал невалидни мисии какво да го правя като ми гръмне getter-a в последствие, като вече ми е счупена статистиката?
Аз поне така си ги обяснявам тези изсквания, не само в тази, а и в другите задачи по ООП, защото то реално повечето могат да се решат и с един доста прост клас. Може и аз да не съм разбрал правилно, но така ми се струва логично след всичкото писане и четене.
@AntonPortenov "От това, което прочетох тези дни много програмисти поставят валидацията в getter-a.
Причината за това е, че перфектна програма няма, и така си спестяваш хвърлянето на exeptions, защото може да не се наложи да иползваш това поле."
Все още го мисля това. Може ли да дадеш линк към това което си чел?