Абстрактни класове
Проблем
Нула квадратни метра
Пишеш програма за плочки на тераса. Терасата е от три части: правоъгълник 4 × 3 м, кръгла част с радиус 1 м и триъгълник с основа 2 м и височина 3 м. Има родител Shape с Area(), който връща 0, а всяка фигура го презаписва.
Колегата, който е писал Triangle, е забравил:
class Triangle : Shape{ public double Base { get; private set; } public double Height { get; private set; }
public Triangle(double baseLength, double height) { Base = baseLength; Height = height; }}Shape[] terrace = { new Rectangle(4, 3), new Circle(1), new Triangle(2, 3) };double total = 0;foreach (Shape s in terrace){ total += s.Area();}Console.WriteLine($"Плочки: {total:F2} м²");Плочки: 15.14 м²Трябва 18,14 м². Програмата не дава грешка, а поръчката е с 3 м² по-малко. Нулата на родителя е скрила липсващия метод.
А какво е new Shape()? Фигура без форма. Няма смисъл да съществува такъв обект.
Идеята
Абстрактен клас: обещание, което наследниците изпълняват
Shape не е истинска фигура. Той казва само „всяка фигура има лице“. Затова го направи абстрактен:
abstract class Shape{ public abstract double Area();}- Абстрактният метод няма тяло, само заглавие и
;. Не можеш да дадеш разумно лице на „фигура изобщо“, затова не даваш никакво. - Абстрактният клас не може да има обекти:
new Shape()не се компилира. - Всеки наследник трябва да напише метода. Иначе програмата не се компилира.
Сега забравеният Triangle дава грешка още при компилирането:
„'Triangle' does not implement inherited abstract member 'Shape.Area()'“. Наследникът пише метода с override, както при virtual.
По-точно, за любопитните
- Наследник, който не пише абстрактния метод, може да е абстрактен и той. Тогава обещанието минава към неговите наследници.
- Клас може да е абстрактен и без нито един абстрактен метод. Така само забраняваш обектите от него.
Виж го
Триъгълникът изпълнява обещанието
Добави метода в Triangle:
class Triangle : Shape{ // ... Base, Height и конструкторът
public override double Area() { return Base * Height / 2; }}Shape[] terrace = { new Rectangle(4, 3), new Circle(1), new Triangle(2, 3) };double total = 0;foreach (Shape s in terrace){ total += s.Area();}Console.WriteLine($"Плочки: {total:F2} м²");Плочки: 18.14 м²Цикълът е същият. Масивът Shape[] пази фигури от три класа, а s.Area() вика метода на всяка. Полиморфизмът от миналия урок работи и с абстрактен родител.
Пробвай
Намери грешката
Избери реда с грешката.
new Shape() е опит да се създаде обект от абстрактен клас. Първият ред е наред: new Shape[3] създава масив от три празни места за фигури, а не фигури. В тях можеш да сложиш правоъгълник, кръг или триъгълник.
Идеята
Абстрактен, но не празен
Абстрактният клас може да има всичко, което има обикновеният: полета, конструктор, обикновени методи. Нека всяка фигура има име и метод, който я описва:
abstract class Shape{ public string Name { get; private set; }
public Shape(string name) { Name = name; }
public abstract double Area();
public string Describe() { return $"{Name}: {Area():F2} м²"; }}Describe() е написан веднъж, в Shape. Той вика Area(), който в Shape още го няма. Не е проблем: Describe() винаги се вика върху истинска фигура, а тя има лице.
От Shape не се правят обекти, но конструкторът му е полезен: наследниците го викат.
class Circle : Shape{ public double Radius { get; private set; }
public Circle(double radius) : base("кръг") { Radius = radius; } // ... Area() както преди}Shape[] terrace = { new Rectangle(4, 3), new Circle(1), new Triangle(2, 3) };foreach (Shape s in terrace){ Console.WriteLine(s.Describe());}правоъгълник: 12.00 м²кръг: 3.14 м²триъгълник: 3.00 м²Родителят пише общото (името, описанието), а наследниците пишат различното (лицето).
public abstract double Area();
public string Describe(){ return $"{Name}: {Area():F2} м²";}
Shape s = new Circle(1);Console.WriteLine(s.Describe());Стъпка по стъпка, с думи
- Shape обещава Area(), но няма тяло. Обект само от Shape не може да има.
- Обектът е кръг: частта Circle има свой Area(). Тя изпълнява обещанието.
- s.Describe() е написан в Shape и важи за всяка фигура.
- Той вика Area(). Обектът е кръг, затова се изпълнява методът на Circle: π · 1 · 1.
- Describe() сглобява текста. Общото е в родителя, различното в наследника.
- Изходът е лицето на кръга, с два знака след запетаята.
Пробвай
Чий метод?
Какво ще отпечата програмата?
// Shape и Circle са от горната карта
Shape s = new Circle(2);Console.WriteLine(s.Describe());Променливата е от абстрактния тип Shape, а това е позволено. Обектът е кръг с радиус 2. Describe() е на Shape, но Area() в него е на кръга: π · 2 · 2 ≈ 12,57.
Пробвай
Кое липсва?
Какво трябва да пише на празното място, за да не може да се създаде обект от Shape?
class Shape
{
public abstract double Area();
}abstract. Щом класът има абстрактен метод, и самият клас трябва да е абстрактен. Иначе програмата не се компилира.Упражнение
Фигури и животни
Реши задачите в редактора си.
1. Лице и обиколка. Абстрактен клас Shape с име и два абстрактни метода: Area() и Perimeter(). Наследници Circle (радиус) и Rectangle (ширина и височина). Сложи кръг с радиус 5 и правоъгълник 4 × 2,5 в масив и отпечатай двете стойности с два знака след точката.
Кръг: лице 78.54, обиколка 31.42Правоъгълник: лице 10.00, обиколка 13.002. Животните. Абстрактен клас Animal с абстрактен метод Sound() и обикновен метод Breathe(), който печата „Животното диша.“ Наследници Cat и Dog. Извикай звука на двете в цикъл, после Breathe() на първото.
Мяу, мяу!Бау, бау!Животното диша.Упражнение
Превозни средства и изпълнения
3. Превозни средства. Абстрактен клас Vehicle с абстрактен метод Move(). SimpleCar и Bicycle го наследяват. Bicycle има и свой метод Pedal().
Колата потегляВелосипедът потегляВъртя педалитеgarage[1].Pedal(). Защо не се компилира, въпреки че там има велосипед?4. Изпълнения. Абстрактен клас Performance със заглавие и абстрактен метод Start(). DanceMove и Song го наследяват. Song има и метод Sing().
Започва танцът moonwalkЗапочва песента 'Bohemian Rhapsody'Пея 'Bohemian Rhapsody'Още задачи
- Триъгълник. Добави
Triangleкъм задача 1 с трите страни. Обиколката е сумата им, а лицето се смята по формулата на Херон: s = обиколка / 2, лице = √(s(s − a)(s − b)(s − c)). За страни 3, 4 и 5 лицето е 6,00, а обиколката 12,00. - Абстрактен служител. Направи
Employeeот миналия урок абстрактен, с абстрактенMonthlyPay(). ДобавиFullTime(заплатата е цялата) и оставиManagerиIntern. Какво печели програмата? Какво губи?
Тест