Loading...

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

sportsman avatar sportsman 137 Точки

Въпроси по ООП и КПК

Имам 2 въпроса:

Първият въпрос: кое е правилно - авоматичните property-та да са преди или след конструкторите ?

Вторият въпрос ми е следния: в една от лекциите на Телерик един от лекторите казва, че е анти-качествен код да инициализираме стойностите на полетата в класа заедно с тяхната декларация, например:

private string dogName = "Sharo";

private static int numberOfLegs = 4;

От друга страна в книгата "Въведение в програмирането с C#" се казва, че е това е добра практика тъй като прави кода по-лесно четим и по-разбираем. Та въпросът ми е коя от двете практики трябва да използвам на изпита и като цяло? Благодаря.

3
C# OOP Advanced 21/10/2014 06:43:36
Samuil.Petrow avatar Samuil.Petrow 1550 Точки

Според мен не е КПК да се инициализират променливи по този начин. 

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

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

3
11/10/2014 09:23:34
sportsman avatar sportsman 137 Точки

Благодаря :)

2
Samuil.Petrow avatar Samuil.Petrow 1550 Точки

Нека и някой друг да даде все пак мнение, може да има по-оптимален вариант :)

1
cherokee avatar cherokee 146 Точки

Тъй като говорим за полета в клас, знаем, че clr-a ги инициализира при инстанцииране то им с дефолтните стойности за дедените типове - т.е. Веднъж имаш задаване на стойности, ако не си го направил изрично. От друга страна е прието, че инициализацията на състоянието (state) на обекта се прави при инстанцииранено му или с други думи през конструктора на обекта и това ще ти е второто присвояване на стойност за твоите полета (ако не си им присвоил вече такава при декларирането им). Малко започна да става дълго, затова с две думи. Ако съответното поле няма да си променя стойността го инициализираш при декларацията му и направо го правиш константа или само за четене (както колегата по горе каза). Ако полето ще си сменя стойността през жизненият цикъл на класа, го инициализираш в конструктора с началната му такава ( било то чрез  параметър на конструктора или с фиксирана начална стойност) - това е добре да се прави дори за полета, които не са част от публичният интерфейс на класа (представени чрез пропъртита). 

2
sportsman avatar sportsman 137 Точки

Знам колега и благодаря за изчерпателния отговор. Но пак казвам, че така е написано в книгата и за това реших да попитам за всеки случай. Благодаря отново.

2
XmUrF avatar XmUrF 363 Точки

Аз пък защо си мисля, че инициализацията на филдовете при създаване на обект взима default стойности само когато няма други зададени? Тоест според мен инициализация при декларацията си е ОК от тази гледна точка.

Даже що се отнася за статик класове бях чел, че по-добре така, отколкото през конструктор, тъй като е по-бързо. От друга страна обаче пък в MSDN-a прочетох, че е грешка при статични методи да се инициализират филдовете при декларация. Пък те се предполага да ги разбират тия неща :). Самият факт че го споменават обаче, означава, че при нормален клас не се счита за грешка. Но и аз не мога да се сетя за ситуация, при която това да е необходимо, освен може би за бързина?

0
RoYaL avatar RoYaL Trainer 6849 Точки

private static int numberOfLegs = 4;

 

Спокойно може да стане константа. Освен ако нямаш някаква велика логика за рязане на крака или нещо друго садистко :) След като стане константа е оправдано да има стойност, най-малкото че това е идеята на константите :)

 

Инициализирането на статични стрингове в конструктор, също ми се струва безсмислено и овъркил и също така те кара да пишеш конструктор на събкласовете, за езиците където това не е нужно (в C# и Java е задължително на дериватите да пишеш конструктор, дори и той да не прави никаква логика, просто за да посочиш че изпълнява super/base конструктора, но в други езици като РНР например, има имплицит call на parent конструктора, ако не напишеш конструктор в дериватите) е излишен хем код, хем четене на логика.

Присвоването при инстанцирането в конструктор трябва да е обвързано с някаква логика - дали името идва отвън или например е конкатенирано с годините му. Ако годините идват отвън, например, "Sharo4" няма как да го напишеш директно в полето. Нито пък е редно да напишеш в полето "Sharo" а в конструктора да апенднеш "4".

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

Би било тъпо Шаро, който е на 5 години да можеш да му сетваш друго име, защото той вече отговаря на това и много трудно ще го научиш на друг бихейвиър.

И тогава ако искаш да експоузнеш "dogName" за пред публиката, най-лошото нещо което можеш да направиш е да направиш { get; set; }

Дефолтен гетър е ок, но директен сетър не. Всъщност, дори и логика в сетъра не е добра идея. Защото нека речем, че имаш система за продаване на кучета. 

Някой, когато иска да си купи кучето Шаро ще извади неговата инстанция от някъде (нека опростим до myDog = new Sharo()) и след това например, ще иска да му смени името.

За клиента не е важно дали ще пипа полето "dogName". За него е важно, кучето му да се казва, например, Боби.

Тогава най-логичното нещо е той да поиска да го преименува. И там се намесва метода Rename(), който прави това интърнали.

В такъв случай, код опростен до:

 

private string dogName = "Sharo";

public void Rename(String newName)

{

    if (this.age > 0.5) { // 6 months

        throw new Exception("....");

    }

    this.dogName = newName;

}

 

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

 

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

 

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

 

Т.е.

 

abstract class Dog

{

    protected string name;

    

    public Dog(string param1, string param2) { ... }

 

    public void MyMethodOne() { ... }

    public void MyMethodTwo() { ... }

    // a lot of other methods

}

 

class Sharo : Dog

{

    protected string name = "Sharo";

 

    public Sharo(int param1, param2) : base(param1, param2);

}

    

class Bobi : Dog

{

    protected string name = "Bobi";

 

    public Bobi(int param1, param2) : base(param1, param2);

}

 

Тук ще се появи проблем обаче, ако param1 и param2 асайнват нещо към полета на Шаро и Боби. Не толкова, че може да се объркат стойности, въпреки че е възможно, а до това че ще имаш една част от полетата инициализирани по декларация, а друга - в конструктора. 

 

P.S.: Просто бъди констистентен - хванеш ли един начин за правене - ползвай него в целия апликейшън. В случая ако Шаро си го направил да е по декларация, Боби не го прави да е в конструктора.

 

 

3
13/10/2014 10:09:58
Karlie avatar Karlie 438 Точки

Използвам темата, за да попитам и аз нещо по въпроса "какво е качествен клас". Кое е по-правилно: когато имам клас с няколко параметъра, да направя празен конструктор, конструктор с един параметър, с два и т.н., или да използвам колкото се може по-малко конструктора. Втори въпрос: ако трябва да имам повечко комбинации за конструкторите, кое е по-правилно - да ги изреждам един след друг, или да направя един конструктор с default-ни стойности. И трето - винаги ли е задължително да има празен конструктор? Ето пример, с който ще с е опитам да си изясня въпросите. Ако имам клас Person, включващ name, age, details, какви конструктори е по-правилно да има:

Person(){}

Person(string name){}

Person(string name, int age){}

Person(string name, int age, string details){}

или

Person(string name, int age, string details)

или

Person(string name, int age = default(int), string details = default(string)) (Това според мен е най-доброто, но виждам, че други хора ползват вариант 1.)

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

0
16/10/2014 21:45:52
cherokee avatar cherokee 146 Точки

Винаги мисли в какво състояние ще създадеш/оставиш даденият обект, така, че да е работещ и без да хвърля неочаквани ексепшъни, т.е. дали след като го създадеш и направиш обръщане към някое пропърти няма да гръмне - защото не си го инициализирал правилно. Ако нямаш нужда от конструктор с параметри, не е нужно да правиш празен такъв - компилатора ще ти направи такъв по подразбиране. Аз лично предпочитам най-последният вариант от примерите ти, защото първо покрива останалите варианти, а и всички полета се инициализират с "безопасни" стойности - е бих направил само details да е по подразбиране.

1
17/10/2014 06:57:25
RoYaL avatar RoYaL Trainer 6849 Точки

В общия случай е най-добре в такива моменти да ползваш setter-ите на пропъртитата и съответно да изградиш клас, който реализира Builder Pattern-а, за да си създадеш Person обекта.

Линкнал съм нарочно информация за pattern-а от StackOverflow, защото в заформилата се дискусия има примери и като твоя :)

1
17/10/2014 09:44:17
sportsman avatar sportsman 137 Точки

Имам още едно въпрсоче, което смятам да го задам пак в тази тема. Кое е правилно - авоматичните property-та да са преди или след конструкторите?

3
HPetrov avatar HPetrov 822 Точки

Структурата трябва да ти е следната:

Константи, полета (публични после private), конструкори, пропъртита и накрая методите.

2
sportsman avatar sportsman 137 Точки

Благодаря, трябваше да питам да съм спокоен :). Сигурен съм, че знаеш, но все пак да допълня: най-най-отпред са статичните мембъри и след това всичко, което ти изброи.

Поздрави.

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