320 likes | 546 Views
Наследование. Объектная модель. Инкапсуляция. Наследование. Полиморфизм. Инкапсуляция – механизм объединения данных и методов в один класс, с возможностью определить для всех элементов уровень доступа. Наследование – создание класса на основе существующего, с заимствованием
E N D
Наследование Объектная модель Инкапсуляция Наследование Полиморфизм Инкапсуляция – механизм объединения данных и методов в один класс, с возможностью определить для всех элементов уровень доступа. Наследование – создание класса на основе существующего, с заимствованием всех его элементов. Полиморфизм – вызов различных версий функции через единый интерфейс. Два способа повторного использования кода: вложение и наследование.
Вложение классов - отношение «класс A содержиткласс Б» (HAS-A) classMan { publicstring Name { get; set; } //... } classCar { privateMan _driver; public Car() { _driver = newMan(); } }
Различия композиции и агрегации Композиция - один класс является элементом другого, время жизни вложенного класса определяется временем жизни класса-контейнера. Агрегация - слабый вариант композиции, если контейнер будет уничтожен, то его содержимое — нет. Book Reader Page
Наследование (inheritance) -отношение «класс А является классом Б» (IS-A) Вопрос 1: велосипед является (is a) транспортным средством? Вопрос 2: транспортное средство является велосипедом?
Наследование – создание нового класса на основе существующего. • Все элементы базового класса заимствуются производным классом. • Наследование позволяет использовать уже написанный код базового класса, дополняя его новым кодом производного класса. • Достоинства наследования: • Исключение повторяющихся фрагментов кода. • Упрощается модификация программы. • Позволяет изменять поведение классов с закрытым кодом. • Структура программы становится более лаконичной. • Недостатки наследования: • Сильная связность (проблема «хрупких базовых классов»).
Композиция vs. Наследование Transport Avia Avia Pilot Pilot Man File MP3File Date Meeting
Пример наследования classMan { publicstring Name { get; set; } } classStudent : Man { publicstring Group { get; set; } } classTeacher : Man { publicstring Department { get; set; } } var man = newStudent { Name = "Иванов И.И.", Group = "A-123" };
Конструкторы и наследование Первым всегда вызывается конструктор базового класса! Варианты вызова: 1. Неявный (автоматический) вызов – вызывается конструктор по умолчанию базового класса. 2. Явный вызов – в конструкторе производного класса через ключевое слово base (ему можно передать параметры).
classMan { publicstring Name { get; privateset; } public Man(string name) { Name = name; } } classStudent : Man { publicstring Group { get; set; } public Student(string name) : base(name) { } } var man = newStudent("Иванов И.И.") { Group = "A-123" };
Пример наследования с расширениемданных class Label { public string Text { get; set; } } class ColoredLabel : Label { public Color Foreground { get; set; } } Спорное решение… Оправданное решение class Control { public Rectangle Rect { get; set; } } class Text : Control { public string Text { get; set; } }
Запрет наследования («финальные классы») sealedclass Point { public double x { get; set; } public double y { get; set; } } class Circle : Point { }
Правила наследования • Класс можно наследовать только от одного класс-предка и от любого количества • интерфейсов. • 2. При наследовании потомок получает все элементы предка (данные, методы, • свойства, события, индексаторы). • 3. Элементы private напрямую не доступны потомку. • 4. Элементы protected доступны всем потомкам, но недоступны извне (как private). • 5. Структуры не поддерживают наследование (кроме реализации интерфейсов). То, что работает в пределах иерархии одинаково, предпочтительно полностью определить в базовом классе.
Простые паттерны наследования #1 Вынос общего функционала в базовый класс class Window { protected void Resize() { } } class WindowUsers : Window { public void Show() { } } class WindowArticles : Window { public void Show() { } }
Window Window Button Button Объекту базового класса можно присвоить объект производного класса. Это делается для единообразной работы со всей иерархией. сlass Window { ..... }; class Button : Window { ..... }; Window w = new Window(); Button b = new Button(); Window w = new Button(); Button b = new Window(); //ОШИБКА
Base * pArray Base Base Child const int SZ = 10; Base [] pArray = new Base[SZ]; … pArray[0] = new Base(); pArray[1] = new Child(); … Base Child
class Window { public void Draw() { } } class Button : Window { new public void Draw() { } } class List : Window { new public void Draw() { } } Window form1 = new Window(); Button button1 = new Button(); List list1 = new List(); Window [] elements = {form1, button1, list1}; foreach (Window wnd in elements) { wnd.Draw(); }
Ранее связывание – ссылка разрешается на этапе компиляции, поэтому для выбора варианта замещающего метода компилятор может руководствоваться только типом, для которого вызывается метод. Позднее связывание – учитывает, что во время выполнения программы под одним типом может скрываться ссылка на объект другого, производного типа.
Виртуальные методы class Window { public virtual void Draw() { } } class Button : Window { public override void Draw() { } } class List : Window { public override void Draw() { } }
-Сигнатуры иерархии виртуальных методов должны быть идентичны. -При использовании виртуальных методов из всех одноименных методов иерархии всегда выбирается тот, который соответствует фактическому типу вызвавшего его объекта. -Виртуальными обычно оформляются те методы, которые на разных уровнях иерархиинаследования должны работать по-разному.
Абстрактные методы и абстрактные классы abstract class Shape { public abstract void Draw(); } class Line : Shape { public override void Draw() { } } class Circle : Shape { public override void Draw() { } } Shape[] shapes = { new Line(), new Circle() }; foreach (Shape shape in shapes) { shape.Draw(); }
- Абстрактный класс содержит абстрактные (пустые) методы и предназначен только для наследования - Абстрактный класс может также содержать обычные методы. - Если потомок абстрактного класса не переопределил все его абстрактные методы, он также является абстрактным.
classDisk { publicenumDiskType { CD, DVD } publicDiskType Type { get; set; } publicdoubleCalcRental(intdaysCount) { switch (Type) { caseDiskType.CD: returndaysCount * 10; caseDiskType.DVD: return 50 + daysCount * 20; } thrownewInvalidDataException("Неизвестный тип диска"); } }
classProgram { staticvoid Main() { var disk = newDisk { Type = Disk.DiskType.DVD }; var rental = disk.CalcRental(2); } }
Начинаем рефакторинг classDisk { publicenumDiskType { CD, DVD } publicDiskType Type { get; set; } publicvirtualdoubleCalcRental(intdaysCount) { switch (Type) { caseDiskType.CD: returndaysCount * 10; caseDiskType.DVD: return 50 + daysCount * 20; } thrownewInvalidDataException("Неизвестный тип диска"); } }
classDvdDisk : Disk { publicoverridedoubleCalcRental(intdaysCount) { return 50 + daysCount * 20; } } classProgram { staticvoid Main() { var disk = newDvdDisk { Type = Disk.DiskType.DVD }; var rental = disk.CalcRental(2); } }
classCdDisk : Disk { publicoverridedoubleCalcRental(intdaysCount) { returndaysCount * 10; } } abstractclassDisk { publicenumDiskType { CD, DVD } publicDiskType Type { get; set; } publicabstractdoubleCalcRental(intdaysCount); } Что здесь можно зарефакторить?
abstractclassDisk { publicabstractdoubleCalcRental(intdaysCount); } var disks = newDisk[] { newDvdDisk(), newDvdDisk(), newCdDisk() }; vartotalRental = 0.0; foreach (var disk in disks) { totalRental += disk.CalcRental(7); }
Оператор is Shape shape = new Line(); if (shape is Circle) { Console.WriteLine("Это окружность"); } Он определяет, совместим ли текущий тип объекта, находящегося слева от ключевого слова is, с типом, заданным справа. Результат операции равен true, если объект можно преобразовать к заданному типу, и falseв противном случае.
Оператор as Shape shape = new Line(); Line line = shape as Line; if (line != null) { Console.WriteLine("Это линия"); } Операцияasвыполняет преобразование к заданному типу, а если это невозможно, формирует результат null.