Loading...
ttitto avatar ttitto 1153 Точки

[Homework] OOP - Defining classes - Септември 2014

Понеже в този курс решенията на задачите търпят на широко обсъждане, което едва ли ще стане при проверката на домашни, мисля да споделям тук решенията си: Ето първото:

Persons

LaptopShop (ново условие)

PCCatalogue

SULS

Тагове:
22
C# OOP Basics 16/09/2014 21:33:12
g.stoyanov avatar g.stoyanov 776 Точки

Добре си направил домашното... имам само една забележка ;)

Целта на курса е да ни научи да пишем максимално опростен, преизползваем, ефективен, структуриран правилно и разбираем за всички код. Повечето домашни в този курс си мисля че ще са от един тип: имаме тухла с дадени размери, след това стена с даден брой тухли или размери, след това етаж, после брой етажи, вид покрив и накрая къща. Мисля си че тези задачи трябва да се имплементират като едно цяло - както е в реалният живот.

Давам пример с твоето домашно:

Първо правиш лаптоп, и то много добре, взел си в предвид всичко което си се сетил. След това правиш персонален компютър, отново отлично... Тук идва момента когато един код трябва да се оптимизира, както е в реалният живот, а именно - преизползването на код, опростяване на кода, лесна модификация (има си друго име но ще го учим по-късно - coupling) и опростен поглед върху цялата "картинка"! След като разгледаме домашното - всичките му задачи, най-добре е да анзлизираме проблемите и да намерим общи точки, след което да съставим необходимото решение. Според мен тук трябва да се подходи по този начин (представям го в най-опростения вариант):

                                          Компютърна част

                                        (елементи на класа)

                                        /            |             \

                                  Цена        Име      Производител

                                            (Наследници)

                /            /              /                      \                   \                       \

     Процесор  Памет  Видео карта         Хард диск   Захранване    Батерия

 

 

 

                                            Компютър

                                                   |

                   Има  Списък/Колекция от компютърни части

                                                   |

                                         /                   \

           има батеия и захранване      само захранване

                                    |                                |

                                         (Наследници)

                               Лаптоп                         PC

 

 

                                             Магазин

                                                   |

                                (Има списък със компютри)

 

Според мен 1 и 2 задача от домашното би трябвало да се приемат като една.

8
ttitto avatar ttitto 1153 Точки

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

1
oconne avatar oconne 113 Точки

 Тито правилно е следвал условието на задача-1 (Laptop Shop), изрично е посочено да бъдат създадени само два класа. Аз съм направил задачата по същият начин, но мисля че Тито е пропуснал следното: Класът Battery трябва да се наследи чрез композиране, т.е създаване на една инстанция от него, както той си го е направил, но тази инстанция трябва да съдържа полетата характерни за Battery: модел, живот на батерията . Класът Battery трябва да отговаря за валидирането на тези полета(ne Laptop), а след това да се създадът само свойства в Лаптоп които се пускат без валидиране. Така мисля че е поставен проблема в задача-1. Ето тук само схематично съм направил това :   TУК            . Мога ли да помоля Стоянов, да направи един солюшън на схемата която е постнал за Laptop Shop, ако има време, просто за по добра демоснтрация (или което избере за по подходящо). 

4
ttitto avatar ttitto 1153 Точки

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

Не съм виждал досега подчертавките в полетата да са най-отзад. Виждал съм да са в началото на името на полето. Но не е добра практика да има nonalphanumeric символи в имената. По-добре свиквай без тях!

И още една забележка. Имената на полетата в CSharp са camelCase, а не се съединяват с долни тирета.

0
16/09/2014 10:00:40
RoYaL avatar RoYaL Trainer 6849 Точки

Това с подчертавките е deprecated code style и общо взето заместител на this спрямо четимост.

1
oconne avatar oconne 113 Точки

Абсолютно точно за този стил на писане направихте забележка, не се използва вече и слаша се слага отпред и така натам, аз обаче страшно се обърквам по стандартният начин :), тази писаница дето съм я направил наистина да не се гледа като стил, само да се проследи идеята. Обещавам че ще го роменя..в скоро време !

0
nakov avatar nakov SoftUni Team Trainer 5295 Точки

Леко бутнах условията на доманите:

  • Добавих първа задача - по-малка и по-лесна.
  • Добавих в задачата за лаптопи повече по-лета, но по-малко задължителни.
  • Добавих примери в задачата за лаптопите.

Надявам се да не съм внесъл confusion.

Наков

6
ttitto avatar ttitto 1153 Точки

Еми ще пишем пак, каква ни е работата!

2
bsdemon avatar bsdemon 348 Точки

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

6
ttitto avatar ttitto 1153 Точки

 bsdemon: Колега, това е симулация на реална ситуация, в която клиентът не знае какво иска и си променя заданието малко преди крайния срок. Случва се много често в IT индустрията. В реални условия това е повод да се предоговори цената на проекта, но тук просто остава да си пренапишем задачите и да се радваме, че ни подготвят за всичко, което може да ни сполети.

6
16/09/2014 18:25:21
ttitto avatar ttitto 1153 Точки

Колеги в новата първа задача не ви ли се струва че има малка грешка. По условие конструкторът с по-малко параметри трябва да извика този с повече параметри. Не че е невъзможно, но е нелогично!

0
DimitarYotov avatar DimitarYotov 75 Точки

И аз мн му се чудих ... ама сигурно има логика (все някаква си ... :) )

Примерно като на HTML - правете header с таблици .... ама да знаете че не се прави така

Сигурно е повече за практика да не се чудим кое? как? защо? ако го срещнем някъде.

Аз лично ще карам по условие ...

Обаче при мен изниква др въпрос. 

Първо почнах да пера всичко е един фаил class Person, Main() и др. методи ги забих в един Program.cs.

После ми стана мн грозно така Person го направих в отделен файл. Сега се чудя методите за валидация на името и майла къде им е мястото .... при обекта, при Main() или в отделен file/class Methods ...  това нещо ми обягва

0
ttitto avatar ttitto 1153 Точки

Ако валидацията ти е много голяма е добре да я изведеш в private метод, в класа където е пропъртито, към което принадлежи валидацията. Или ако валидираш email, то изнасяш private метод в класа Person. Този метод викаш в сетера на пропъртито Email.

За нашите задачи обаче валидациите са по две-три сравнения и се правят само в пропъртитата.

0
DimitarYotov avatar DimitarYotov 75 Точки

Да, тези които са точно за определено нещо ясно .... там при обекта

Но да кажем при мен сега в случея за името ...
Не мисля да използвам някакви регулярни изрази и т.н. , предпочитам да си ги правя с методи. Да кажем Името искам да е по дълго от два символа и да съдържа само символи от 'А' - 'Z' или 'а' - 'z' или 'а' - 'з' или 'А' - 'З' ... т.н. и за всяко едно може да е разбито на по отделно .... 

Мисълта ми е че когато методите за валидация можеш да ги ползваш за повече от един клас .... например ако имаш Човек, Куче, Място за разходка .... и трите ще си имат някакви имена напр.(Димитър, Жу, Борово око) .... за валидация искам да използвам един метод ... и на трите класа ли трябва да го пиша или да си направя dll за такива случай

0
Valleri avatar Valleri 304 Точки

Не мисля, че има грешка и че е нелогично, просто алтернатива на constructor with default parameters - Chaining 

Имам малко въпроси и аз. Какво се има впредив под optional в условието?
Аз си представям така нещата, имаме да кажем инпут от user-a и го приемаме в конструктор, полетата, които са optional, ще влязат като празен стринг и не трябва да има проверка за тях при settera...няма нужда да гърми, те са си optional. Това е първия сценарии.

Втория вариант е да имаме отново конструктор с толкова параметри колкото е полето за попълване от клиента и да сложим default parameters, които няма да са празен стринг а нещо от рода на "N/A". Но дали ще е празен или "N/A" няма голяма разлика. Проверката нужна ли е?

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

По-конкретно въпроса ми е как се прави в реалния живот и също така за целите на домашното дали да създадем просто 3-4 примерни конструктура или има някакъв начин ефективно да работим с optional стойностите?

0
ttitto avatar ttitto 1153 Точки

Под optional означава, че или ги няма, или ги има. В C# една референтна променлива да я няма, означава да е null, а не да е празен стринг. Т.е. ако един стринг е празен, то той съществува, но няма стойност. Понеже числовите типове не могат да са Null, те се инициализират с 0;

За реализацията на optional reference параметри има два начина.

Първият: в конструктора им подаваш стойност по подразбиране null, напр. public Laptop(..., string ram=null,...) и (ако са повече от един) при извикването на конструктора ги подаваш(ако ти трябват) с наименовани променливи new Laptop(...,ram: someValue,...)

Другият начин е да направиш един конструктор само със задължителните стойности и в него да извикаш пълния конструктор със стойност null за незадължителните: напр пълен конструктор public Laptop(string model decimal price, string hdd, string ram,...){...}, непълен конструктор: public Laptop(string model, decimal price):this(model, price, null, null,...) {...}

При валидацията за незадължителните полета просто не правиш проверка дали са null, защото не е проблем да са null. Може обаче да проверяваш по други критерии. както в нашето условие е казано: параметърът е optional, но не може да е празен стринг означава може да е null, но ако не е null трябва да не е празен.

3
vladislav_hadzhiyski avatar vladislav_hadzhiyski 66 Точки

Числовите типове могат да са nullable 

Пример: int? num = null; 

2
StanDimitroff avatar StanDimitroff 90 Точки

Най-лесно и разбираемо мисля, че става с един конструктор(пълен), на който на незадължителните полета се задават стойности null, обаче целта на задачаите явно е да се упражни верижния конструктор, който вече е остарял според лекцията на Николай Костов от миналата година в курса по ОПП на Академията. Според него е добра практика да се използва наскоро въведения в .NET по-долу начин:

LINK

2
17/09/2014 14:04:47
bsdemon avatar bsdemon 348 Точки

Колеги измъчих се малко ама ето първата задача.
Моля ви дайте коментари ако правя нещо неправилно. Според мен работи по условие.


Persons

Laptop-Shop

PC Catalog

SULS -- ToDo

 

3
20/09/2014 23:31:34
Tr00peR avatar Tr00peR 566 Точки

Здравей, идеална е задачата, единствено забелязах, че не ползваш правилно ексепшъните.

При ArgumentNullException и ArgumentOutOfRangeException ако ще подаваш само един параметър, той трябва да самото име на параметъра, който прави проблема, а не съобщението за грешка, както се прави примерно в ArgumentException.

Тук може да видиш конструкторите.

1
bsdemon avatar bsdemon 348 Точки

Благодаря за корекциите, радвам се като малко дете на коледа, защото вече ми се изясняват нещата.

0
borislavml avatar borislavml 368 Точки

Колеги и аз ръчкам по новата първа задча, където наистина има доста неясности. Имам проблем с валидацията. Направих я в property-setter-a  и си работи перфектно със всичките му там ексепшъни. Но накрая като тръгнах да си правя тестове в main метода се сетих, че като подам параметрите на новия обект през конструктора [ Person somePerson = new Person("Pesho",23, "pesho@epich.bg" ] реално не ми се прави валидация и всякакъв инпут си минава. Ексепшъните гърмят само, когато подам сотйност през пропъртито [ somePerson.age = 101  си гърми "Out of range"]  . И веднага стигам до въпроса как се измъкваме в такива напълно релни ситуации? Винаги мога да нашия проверка и в конструктора, но това ми се струва леко малоумно да правя едно и също нещо два пъти.При по големи проекти това ше е цифра код копи-пейст. Има ли начин да направим валидацията в конструктора промерно и да "подадем" от него по някакъв начин вече валидираните параметри на пропърти сетъра. И дори да има такъв начин какво правим ако си направим нова инстанция на класа през parameterless конструктор [ Person somePerson = new Person( )] , без да подаваме параметри и след това през сетера ги нашием [ somePerson.name = "Pesho"]. Тогава и да сме си спестили двойната проверка отива на вятъра, защото подадения параметър не е минал валидаиция през конструктора! ..или поне аз така си мисля. Някой по на вътре с нещата да ме светне! smile 

0
16/09/2014 23:28:19
bsdemon avatar bsdemon 348 Точки

Колега аз направих всичко по лекцията, много са ми мътни още нещата. Проверката ми е в property-то и си работи. Според мен нещо си объркал, но не мога да кажа със сигурност. Дай да погледнем кода или погледни моя. Все ще го измислим :)

0
Tr00peR avatar Tr00peR 566 Точки

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

Първо по това, което си написал разбирам, че подаваш стойността на полета, а не на пропъртита, или пък не именуваш правилно. Прието е полетата да са малка буква, а пропъртитата с голяма - например ако somePerson.age е поле -  somePerson.Аge ще е пропъртитито.

Полетата в общия случай са private и somePerson.age = 23 би трябвало да е невалидно извън класа (трябва да се използва somePerson.Аge).

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

3
borislavml avatar borislavml 368 Точки

Ами не виждам какво мога да съм объркал. Проверка в пропъртито, но през конструктора не ми я хваща smile

Ето го кода.  Най-отдолу са ми проверките(коентирал съм ги) Свиркайте ако видите нещо. А твоя ше го линкнеш ли, че нещо не го виждам из постовете smile

0
Velichkov avatar Velichkov 87 Точки

Ето и моите решения. Приемам всякаква критика :)

Persons

LaptopShop

PCCatalog

SoftUni LS: TODO

1
17/09/2014 16:59:55
Karlie avatar Karlie 438 Точки

Имам питане по Laptop Shop - защо в клас Battery имаш едно публично поле GeneralInfo и едно private? Не може ли да се мине само с едно публично?

0
18/09/2014 00:25:38
Velichkov avatar Velichkov 87 Точки

Имам private поле и публично пропърти, което е сложено за валидация на подаваните данни :) Не виждам как ще стане само с пропъртито

0
18/09/2014 08:30:57
Karlie avatar Karlie 438 Точки

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

0
vvulevv avatar vvulevv 51 Точки

И аз имам 1 въпрос по първата задача. Там, където се прави проверка за валиден мейл - > if(null!=value && (value.Length==0 || !value.Contains("@"))) . Не мога да разбера логиката на самата проверка. if null != value не значи ли, че хвърляш exception в случай, че стойността, записана в email, е всичко друго освен null ? Може ли малко разяснение в тази логика?

0
ttitto avatar ttitto 1153 Точки

Условието горе означава: ако value не е null , то хвърли грешка, ако е празен стринг или ако не съдържа @


Един вид, ако е различно от null и е празен ->хвърляй,
ако е различно от null и не съдържа @ -> пак хвърляй

По условие разрешените стойности са null или непразен стринг съдържащ @

Ако не сме дали стойност на email (email=null) не е необходимо да хвърляме грешка, защото email е optional

Ако имейл съществува (email!=null), то трябва да го проверим дали е празен и дали в него има @.

И това обяснение ме подсеща, че всъщност проверката за празен стринг може да отпадне, защото ако съдържа @, то значи не е празен :)

4
17/09/2014 19:54:55
vvulevv avatar vvulevv 51 Точки

Благодаря за обяснението, сега схванах идеята! :)

1
DJZoning avatar DJZoning 85 Точки

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

2
dsmiteva avatar dsmiteva 13 Точки

Здравейте,

 

Аз имам един въпрос по задачата LaptopShop. В класа Laptop полето ни за батерия ще е instance на класа Battery. Следователно какъв е правилния начин да се зададе това поле и да му се напише пропъртито и по - точно сетъра.

 

Благодаря за отделеното време.

Диляна

0
Karlie avatar Karlie 438 Точки

Диляна, не претендирам, че това е правилният начин (т.к. сега ги уча тези неща и ще се радвам на коментар към подхода ми), но така ми се струва най-логично - в основния клас на програмата (Main) създаваш какъвто вид батерия ти трябва, а после генерираш всеки лаптоп с батерия по избор. Kак правя пропъртито - виж Laptop.cs:

LaptopShop

2
18/09/2014 12:38:32
RoYaL avatar RoYaL Trainer 6849 Точки

Това е по-правилният начин, от този който ни се показа на лекциите. Инициализация в конструктора по принцип е лош вариант. Въпреки, че точно в конкретния случай няма почти никаво значение дали е по начинът на Karlie или в конструктора :)

Но в друг случай би било от голямо значение:

 

Първо е важно, ентититата, които значат нещо, да бъдат реално такива - т.е. реални класове. Ако за всеки лаптоп правя new Processor("Intel"); То значи имам огромна нужда от клас, който се казва IntelProcessor.

Представете си абстрактен клас Processor. И два класа, които го наследяват - IntelProcessor и AmdProcessor.

Сега, ясно е - всеки лаптоп има нужда от процесор.

В конструктора на Laptop искаш да инициализираш нов процесор, но не си наясно той Интелски ли ще е или AMD. В такъв случай ще трябва да направиш някакво извращение, за да инстанцираш всички възможности и после да манипулираш точно тази, която ти трябва (абстрактен клас не може да се инициализира). Което е МНОГО ЛОШО решение.

Според LSP обект от тип IntelProcessor е също така и обект от тип Processor. Това означава, че ако някой метод очаква параметър от тип Processor и ти му подадеш негов подтип (IntelProcessor) всичко ще върви на шест точки.

В такъв случай, ето го и конструкторът на нашия Лаптоп клас:

public Laptop(Processor processor)

{

    this.Processor = processor;

}

 

Сега в main-a на LaptopShop класа правим следното:

 

Processor myLaptopProcessor = new AmdProcessor("4 GHz");

Laptop myLaptop = new Laptop(myLaptopProcessor);

 

Така имаме абсолютната гъвкавост да подадем какъвто подтип пожелаем, обектите да са независими един от друг, и в случай че искаме да направим някакви тестове, лесно можем да си направим mock object-и на съответните типове, в противен случай класът Лаптоп сам ще ги е инициализирал и не можем да ги заместим.

Pattern-ът се казва Dependency Injection и в обществото се води предпочитаният такъв.

8
18/09/2014 10:45:14
g.stoyanov avatar g.stoyanov 776 Точки

Наистина е така... но си мисля че е абсурдно да сложим всички възможни компоненти на един лаптоп в отделни полета. Един лист от Компоненти или някакъв интерфейс който би ни свършил работа е по-удачния вариант. Много по лесно после боравим с данните. Иначе - this.processor + this.ram+this.....+this.card reader+this.wireless кофти става. 

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