08. Military Elite from "Interfaces and Abstraction" Differences Explanation
Привет колеги!
Реших да се завърна на една задача, която преди докарах до 80/100 и зарязах. Предполагам,. всеки ердин от вас си я спомня, но ето линк за исловието:
https://softuni.bg/downloads/svn/csharp-oop-advanced/July-2017/01.%20CSharp-OOP-Advanced-Interfaces-And-Abstraction/01.%20CSharp-OOP-Advanced-Interfaces-And-Abstraction-Exercises.docx
Успях да я докарам дп 100/100, но имам големи разлики с авторското решение в github на курса. Исках да ви питам, каква е разликата и дали моето е добре направено? Дали на изпита и моето би минало?
Основните разликa и тя е в цялата структура е:
1.Аз съм правил класове само за изпозваните в самата програма типове войници. Пример: аз НЕ съм правил клас за Soldier (абстрактен е в авторкото решение) или SpecialisedSoldier, а направих само интерфейси ISoldier и ISpecialisedSoldier,
като ISpecialisedSoldier : ISoldier
и ICommando : ISpecialisedSoldier
и public class Commando : ICommando
в авторското решение:
Soldier : ISoldier
Private : Soldier, IPrivate
ISpecialisedSoldier : IPrivate
SpecialisedSoldier : Private, ISpecialisedSoldier
Commando : SpecialisedSoldier, ICommando
Защо ни е да правим абстрактен клас Soldier и абстрактен клас SpecializedSoldier, при условие,че може да наследим направо интерфейсите?
моят код: https://github.com/ivanduzunov/CSharpOOPAdvancedCourse/tree/master/Interfaces%20and%20Abstraction%20-%20Exercise/08.%20Military%20Elite
авторското решение: https://github.com/CSharpFundamentals/CSharp-OOP-Advanced_July2017/tree/master/MillitaryElite/MilitaryElite
Благодаря!
А добре, защо тогава е нужен абстрактен клас Soldier, при условие,че си имаме интерфейс ISoldier?
Защото този абстрактен клас е вече моделът за всички войници - има си конструктор, override ToString() и каквото друго е нужно.
Значи идеята е да имаме в класовете максимално малко неща, тоест структурата да ни е максимално абстрактна, нали така?
Искаше ми се и някой по-навътре в нещата да ти отговори, защото и аз тепърва се уча и не искам да те подведа, но ще ти кажа аз как ги виждам нещата. Ще използвам за пояснение моето решение от гитхъба ми.
ISoldier има първо, второ име и ID. Съответно Soldier абстрактния клас го имплементира него. Да кажем, че след време нашата програма трябва да може да работи и с информация за пътуване на войник - разходи на ден и дни в които ще пътува. Правим интергейс ITraveler и му слагаме съответни properties DollarsPerDay, DaysAbroad и метод CalcExpenses и ISoldiera може да имплементира него също(важно е да се каже, че един клас може да наследява само 1 друг клас в C#, но може да имплементира повече от 1 интерфейси). Soldier-a ще имплементира интерфейса като в метода за калкулиране на разходи ще сложим логика която дава на войника 10% отгоре на DollarsPerdDay за напредвидени разходи:
double CaclExpenses()
{
this.DollarsPerDay += this.DollarsPerDay * 0.10;
return this.DaysAbroad * this.DollarsPerDay;
}
След време се налага програмата вече да работи и с Marine тип който обаче също трябва да пътува. Правим абстрактен клас Marine който имплементира ITraveler и пак имаме нужните ни данни използвайки интерфейса. Така преизползваме интерфейса като можем да дадем коренно различна логика за смятането на разходи - например да са с 15% отгоре на ден за непредвидени такива, но и се вадят 5% от цялата сума за периода, защото отиват за данъци, а ние искаме да знаем чистите разходи.
double CaclExpenses()
{
this.DollarsPerDay += this.DollarsPerDay * 0.15;
return (this.DaysAbroad * this.DollarsPerDay) -= (this.DaysAbroad * this.DollarsPerDay) * 0.05;
}
По този начин преизползваме някакво общо абстрактно поведение на интерфейса, но може да имплементираме коренно различна логика за всеки клас който го наследява.
На много места където съм чел казват, че абстракцията отнема време да се осмисли за да се ползва на 100% и трябва опит в изграждане на проекти(дори малки като нашите) за да добие човек реален поглед над нещата така че според мен не се притеснявай ако имаш пропуски засега(всички вероятно сме така).