430 likes | 629 Views
Курс по програмиране на C#. Занятие № 9 Наследяване. Видимост и капсулиране. Съдържание. Наследяване Наследяване в C# Превръщане на типове нагоре и надолу по йерархията Видимост Капсулиране. Наследяване. Какво е „наследяване“? Основен принцип в ООП
E N D
Курс по програмиране на C# Занятие №9Наследяване. Видимост и капсулиране
Съдържание • Наследяване • Наследяване в C# • Превръщане на типове нагоре и надолу по йерархията • Видимост • Капсулиране
Наследяване • Какво е „наследяване“? • Основен принцип в ООП • Един клас може да наследи характеристиките и поведението на друг • Родителски клас – по-общ; обхваща по-голямо множество обекти • Наследен клас – по-специализиран; обхваща по-тясно множество обекти, описвайки ги по-подробно • Един родителски клас може да има много наследници • Йерархии от класове – един клас може едновременно да бъде наследен задруг и да бъде родителски за няколко други класа • Някои езици позволяват множествено наследяване (няколко независими родителски класа); C# не е от тях
Наследяване • Примери за наследяване • Жив организъм <- Бактерия, Растение, Животно, Гъба • Животно <- Риба, Земноводно, Влечуго, Птица, Бозайник • Бозайник <- Лъв, Котка, Куче, Примат, Човек • Сграда <- Жилищна сграда, Обществена сграда • Обществена сграда <- Библиотека, Читалище, Училище • Художествено произведение <- Картина, Скулптура, Роман, Мюзикъл • Файл <- Текстов файл, Двоичен файл • Текстов файл <-TXT файл, HTML файл, XML файл • Графичен елемент <- Прозорец, Бутон, Текстово поле, Календар • Бутон <- Обикновен бутон, Радио бутон, Бутон в лента с инструменти
Наследяване • Защо е полезно наследяването? • Категоризирането на обекти в практиката става на различни нива • Служи за по-добро смислово структуриране на кода на програмата • Позволява манипулациите с нехомогенни набори от данни • Прави възможно приложението на алгоритми върху категория от обекти, независимо от пълният им набор от характеристики и поведение
Наследяване • Принципи на действие на наследяването • Родителският клас описва набор от характеристики и поведение, присъщи на всички обекти от него • Наследеният клас автоматично получава всички описани от родителския клас характеристики и поведение, като в допълнение към тях описва други, специфични само за неговите обекти • Всеки екземпляр на наследения клас може автоматично да се разглежда и като екземпляр на родителския клас • Същите принципи важат и в по-сложни йерархии на повече от едно ниво
Наследяване в C# • Деклариране на наследени класове • В декларацията на клас може да се укаже родителски клас, който той наследява • Не може да бъде указан повече от един родителски клас • Родителският клас може от своя страна да наследява трети клас и т.н. • Не може клас да наследява себе си или някой от своите наследници • Ако не се укаже родителски клас, декларираният клас по подразбиране наследява object • Членовете на родителския клас не се декларират повторно в наследения клас; те могат да се използват наготово
Наследяване в C# // Родителски клас - произволно животноclassAnimal{publicstringSpecies;publicintWeight;publicint Age;publicvoid Vocalize() { }} // Наследен клас – кучеclassDog : Animal{publicstringBreed;publicstring Name;publicvoid Vocalize() {Console.WriteLine("Woof!"); }} Деклариране на наследен клас Ключова дума class Наименование на класа Двоеточие Наименование на родителския клас (частично или пълно) Блок с декларации на членове При неуказване на родителски клас, класът по подразбиране наследява object Разрешено е член на наследения клас да има същото наименование като член на родителския клас (но в общия случай не е желателно)
Деклариране на наследен клас - демо // Демонстрация
Наследяване в C# • Употреба на членовете на родителски клас • В декларацията на наследен клас могат да бъдат достъпвани членовете на родителския клас, все едно са членове на същия клас • Ако наследения клас декларира член със същото име като член на родителския клас, членът на родителския клас може да бъде достъпен чрез представката “base.” • В останалата част от програмата, оперирането с всички членове на обект се извършва по еднотипен начин, независимо в кой клас в йерархията от класове на обекта е деклариран съответният член
Наследяване в C# classAnimal{//...publicstringGetDescription() {returnstring.Format("Species: {0}; Age:{1}",Species,Age); }} classDog : Animal{//...publicstringGetDescription(){returnstring.Format("{0}; Breed: {1}; Name: {2}",base.GetDescription(),Breed,Name); }} Употреба на членовете на родителски класв декларацията на наследения клас Достъпване на членове с припокриващи се имена в декларацията на наследения клас Ключова дума base Оператор за достъпване на член . Наименование на члена Употреба на членовете на родителски клас в останалата част от програмата
Употреба на членовете на родителски клас - демо // Демонстрация
Наследяване в C# • Конструктори на наследени класове • При деклариране на конструктор в наследен клас може да се укаже кой конструктор на родителския клас да бъде изпълнен, както и аргументите, които да му бъдат подадени • Тези аргументи може да са както константи и литерали, така и параметри на конструктора в наследения клас или по-сложни изрази • Конструкторът на базовия клас се изпълнява преди конструктора на наследения клас • Ако не е указано кой конструктор на родителския клас да бъде изпълнен, по подразбиране се изпълнява конструкторът без параметри
Наследяване в C# classAnimal{//...publicAnimal(){}publicAnimal(string species) {Species = species; }} classDog : Animal{//...publicDog(): base("Canis lupus familiaris"){ }publicDog(string breed) {Species = "Canis lupus familiaris";Breed = breed; }} Конструктори на наследени класове - изпълнение на конструктор на родителския клас Модификатор за видимост Наименование Списък с параметри Двоеточие Ключова дума base Списък с аргументи на родителския конструктор Тяло на конструктора Изпълнение на конструктор по подразбиране
Конструктори на наследени класове - демо // Демонстрация
Превръщане на типове нагоре и надолу по йерархията • Променливи и екземпляри на класове в паметта • Не е задължително типът на променлива и типът на обекта в паметта, който тази променлива реферира, да съвпадат • Разрешено е променлива от типAда реферира обект от тип B, ако типът А е част от йерархията от родителски типове на типа B (казваме, че тип B е съвместим с тип A) • В противен случай се предизвиква грешка (при компилация или по време на изпълнение)
Превръщане на типове нагоре и надолу по йерархията • Превръщане на типове нагоре по йерархията (upcasting) • Нека тип Aе част от йерархията от родителски типове на тип B • Присвоява се на променлива от тип A израз от тип B • Друг вариант: израз от тип B участва в по-сложен израз в ролята на израз от тип A • Не е необходим специален синтаксис (неявно превръщане на типове) • Винаги е разрешено и не може да предизвика грешка • Типът на обекта в паметта не се променя • Следствие: на променлива от тип object може да бъде присвоен произволен израз
Превръщане на типове нагоре и надолу по йерархията // Създаване на екземпляр на класа DogDogdog = newDog("German shepherd"); // Превръщане на типове нагоре по// йерархиятакъм родителския клас AnimalAnimalanimal = dog; // Превръщане на типове нагоре по// йерархиятав сложен израз((Animal)dog).Vocalize(); Присвояване на израз от наследен тип на променлива от родителски тип Сложни изрази с превръщане на типове нагоре по йерархията
Превръщане на типове нагоре по йерархията - демо // Демонстрация
Превръщане на типове нагоре и надолу по йерархията • Превръщане на типове надолу по йерархията (downcasting) • Нека тип Aе част от йерархията от родителски типове на тип B • Присвоява се на променлива от тип B израз от тип A • Друг вариант: израз от тип A участва в по-сложен израз в ролята на израз от тип B • Необходимо е явно превръщане на типове • В случай че типът на обекта в паметта не е съвместим с типа B, се предизвиква грешка • Компилаторът не разрешава преобразуване между типове, които се намират в различни клонове от йерархията (тъй като това води до сигурна грешка при изпълнение)
Превръщане на типове нагоре и надолу по йерархията // Присвояване на екземпляр na класа Dog// на променлива от тип AnimalAnimalanimal = newDog("Poodle"); // Превръщане на типове надолу по// йерархията към наследения клас DogDogdog = (Dog)animal; // Превръщане на типове надолу по// йерархията в сложен изразConsole.WriteLine(((Dog)animal).Breed); Присвояване на израз от родителски тип на променлива от наследен тип Тип, към който ще бъде извършено превръщането, заграден в кръгли скоби Израз, който трябва бъде превърнат в указания тип Сложни изрази с превръщане на типове надолу по йерархията
Превръщане на типове надолу по йерархията - демо // Демонстрация
Превръщане на типове нагоре и надолу по йерархията • Проверка за съвместимост на типове • Нека тип Aе част от йерархията от родителски типове на тип B • С оператора is може да се провери дали обектът в паметта, рефериран от израз от тип A, е съвместим с типа B • Ако операторът is върне стойност истина, превръщането на типове надолу по йерархията е безопасно • Операторът asможе да се използва за безопасно превръщане надолу по йерархията; в случай че типовете са несъвместими, резултатът от превръщането е null • Операторът as може да се използва единствено за превръщане надолу по йерархията към референтен тип
Превръщане на типове нагоре и надолу по йерархията // Създаване на екземпляр на класа AnimalAnimalanimal = newAnimal("Felissilvestriscatus"); // Проверка за съвместимост на типове за// безопасно превръщане на типове надолу// по йерархията с isif(animal isDog){Console.WriteLine( ((Dog)animal).Breed);} // Безопасно превръщане на типове надолу// по йерархията с asDogdog = animal asDog;if(dog != null)Console.WriteLine(dog.Breed); Проверка за съместимост на типове с is Израз, чиято стойност трябва да бъде проверена за съвместимост Оператор is Тип, съвместимостта с който трябва да бъде проверена Безопасно превръщане на типове надолу по йерархията с as Израз, чиято стойност трябва да бъде превърната към друг тип Оператор as Референтен тип, към който трябва да бъде извършено безопасното превръщане
Проверка за съместимост на типове и безопасно превръщане на типове надолу по йерархията - демо // Демонстрация
Видимост • Какво е „видимост“? • Понятие в програмирането, служещо за ограничаване на достъпа до фрагменти от кода • В ООП – видимост на членове и видимост на типове • Видимостта на член ограничава достъпа до съответния член от различни участъци от програмата (същия клас, наследени класове, други класове в същия модул, други модули) • Видимостта на тип ограничава достъпа до съответния тип от различни участъци от програмата (същия модул, друг модул)
Видимост • Видимост в C# • Модификатори за достъп • Поставят се в декларациите на типове и членове • Важат единствено за съответния тип/член • Ако модификатор за достъп не е указан, по подразбиране видимостта е минималната възможна
Видимост • Модификатори за достъп за членове в C# • Модификаторът за достъп се поставя в началото на декларацията на съответния член • private – членът е достъпен единствено в рамките на декларация на същия клас (това е видимостта по подразбиране) • protected– членът е достъпен в рамките на декларацията на същия клас и всички негови наследени класове • public – членът е достъпен навсякъде в програмата • internal – членът е достъпен навсякъде в рамките на същото асембли • internal protected– членът е достъпен навсякъде в рамките на същото асембли, както и в декларациите на всички наследени класове, независимо в кое асембли са декларирани
Видимост classMailMessage{privatestring _subject; protectedMailMessage(string subject){ _subject = subject;} publicstringGetSubject(){return_subject;} internalvoid Send(){// ...}} Видимост на членове Членове, достъпни единствено за същия клас Членове, достъпни за същия клас и наследените от него класове Членове, достъпни за цялата програма Членове, достъпни в същото асембли
Видимост на членове - демо // Демонстрация
Видимост • Модификатори за достъп за типове в C# • Модификаторът за достъп се поставя в началото на декларацията на съответния тип (клас, структура, изброен тип и др.) • public – типът е достъпен навсякъде в програмата • internal – типът е достъпен в рамките на същото асембли (това е видимостта по подразбиране)
Видимост publicclassMusicAlbum{// ...} publicenumMusicGenre{// ...} internalclassAlbumCollection{// ...} internalstructUserInfo{// ...} Видимост на типове Типове, достъпни за цялата програма Типове, достъпни в същото асембли
Видимост на типове - демо // Демонстрация
Капсулиране • Какво е „капсулиране“? • Основен принцип в ООП • Всеки обект трябва да скрива от външния свят вътрешната си реализация • Видими за останалата част от програмата са само важните за нея характеристики и поведение на обектите
Капсулиране • Защо е полезно капсулирането? • Опростява „външния вид“ на обекта • Позволява промяна на вътрешната реализация на обекта, без да се налага промяна в останалата част от програмата • Осигурява интегритет на вътрешните характеристики на обекта
Капсулиране • Принципи на капсулирането • Всеки член или тип данни трябва да има възможно най-ограничената видимост, позволяваща смисленото реализиране на програмата • Характеристиките на обекта са достъпни само за самия клас • Методите, реализиращи вътрешно поведение, също са достъпни само за самия клас • Външен достъп до характеристиките на обекта може да се осъществи само чрез други членове на класа (методи – аксесори и мутатори; конструктори; свойства; индексатори) • Възможно е наследените класове да имат привилегирован достъп до някои членове на родителския клас, но само за тези, които са им нужни
Капсулиране - демо // Демонстрация
Задачи за упражнение • Преработете програмата с геометричните фигури и тела от упражненията към предишната лекция, така че да се възползвате от принципите за наследяване и капсулиране: • Капсулирайте данните на всички обекти: всички полета да бъдат частни и промяната на стойностите им да се извършва през конструктори и методи (публични или защитени) • Създайте базови класове Object2D (характеристики: периметър и лице)и Object3D (характеристики: обем и пълна повърхнина) и реализирайте изчисляването им на базата на специфичните характеристики наследени класове • Създайте колекции от базовите класове и реализирайте логика за въвеждане/извеждане на характеристиките им (използвайки upcastingи downcasting)
Задачи за упражнение • Реализирайте приложение за регистриране и разглеждане на списък с произведения в библиотека със следните класове: • Печатно произведение (заглавие; език; издателство) • Периодично печатно произведение (година; брой) • Вестник (вид: ежедневник, седмичник, двуседмичник; гл. редактор; водеща новина за броя) • Списание (тема; авторски колектив; описание на корицата на броя) • Самостоятелно печатно произведение (дата на издаване; автор/автори; брой страници) • Научна статия (научна област; препоръчана литература) • Книга (номер на изданието; твърди/меки корици; наличие на илюстрации) • Художествена литаратура (жанр; целева аудитория) • Техническа литература (научна област; ниво на аудиторията: начинаещи, напреднали, експерти)
Задачи за упражнение • Реализирайте походова конзолна ролева игра: • Играта се развива в правоъгълна мрежа от символи, подобно на Златотърсачи • Създайте базов клас за единица (играч или чудовище), който съдържа информация за координатите на единицата, точките живот и нанасяните от единицата щети • Създайте наследени класове за играч и различни видове чудовища • В началото на играта, поставете играча и известен брой чудовища в игралното поле • Реализирайте безкраен цикъл, в който всяка единица получава ход, в който може да се придвижва или атакува противник, отнемайки му точки живот; чудовищата управлявайте програмно • Бонус: добавете случаен елемент в играта (например случайно количество щети при нападение, случайно генериран терен на игралното поле, и/или различни характеристики за чудовища от един и същи вид) • Бонус: добавете екипировка за играча
Благодаря! • Александър Далемски • sasho@david.bg • Skype: musasho • https://facebook.com/adalemski • ДАВИД академия • acad@david.bg • http://acad.david.bg/ • @david_academy • https://facebook.com/DavidAcademy