Принципи роботи спадкування і компонентів Visual C # (Sharp)

Спадкування полягає у визначенні базових класів, що володіють функціональністю, яку можна чи не можна підмінити або перевантажити, як було розглянуто в попередньому розділі Компоненти визначають підсистеми, які складаються в одне ціле подібно частинам картинки-пазла Ідеєю компонентів є надати можливість асоціювати два примірники інтерфейсу і змусити їх працювати спільно, при цьому кожен з них не знає, що робить інший

Щоб відчути різницю між спадкуванням з використанням класів і компонентами з використанням інтерфейсів і класів, ми розглянемо классічкій приклад наслідування і перетворення цього прикладу в компоненти

Ілюстрація спадкування за допомогою фігури, прямокутника і квадрата

Одним з найбільш популярних прикладів використання спадкування є обчислення площі фігур Початковою точкою даного спадкування є абстрактний клас, що має одну властивість і один метод, щоб вказувати один розмір і відповідну площу Наприклад, відповідне визначення абстрактного базового класу може виглядати таким чином:

abstract class Shape {

public abstract double CalculateArea()

}

Метод caicuiateArea () використовується для обчислення площі фігури Він осягнути абстрактним і повинен бути реалізований в похідному класі

Для початку, визначимо клас Square, який являє квадратну фігуру:

class Square : Shape { double _width

public double Width {

get {

return _width

}

set {

_width = value

}

}

public override double CaicuiateArea() { return Width * Width

}

&gt&nbsp

Квадрат має тільки один розмір, width, який представляє розмір сторони квадрата У визначенні класу був визначений метод CaicuiateAreaо, який обчислює площа квадрата, множачи властивість width на саме себе

Так як прямокутник є різновидом квадрата, то клас Rectangle нледует від класу square:

class Rectangle :  Square { double _length

public double Length { get {

return _length

}

set {

_length = value

}

}

public new double CaicuiateArea() { return Width * _length

}

}

Так як для опису прямокутника розміру тільки однієї сторони недостатоо, то ми додаємо властивість Length У реалізації методу для обчислення пладі Rectangle CaicuiateArea () довжина множиться на ширину

Подивіться уважно, як оголошений метод caicuiateArea () У випадку з метом RectanglecaiculateAreaо було використано ключове СЛОВО new, а не override Це було зроблено з метою забезпечення однорідності обчислень Під однорідністю обчислень мається на увазі, що при виконанні певних обчислень з типом ми отримуємо відповідь, очікуваний від даного типу, а не від како-небудь іншого типу

Припустимо, що ви створили екземпляр типу Rectagle, з яким згодом виконали понижувальний приведення до типу square При виклику методу CaiculateArea () ви хочете, щоб він виконував обчислення, як для квадрата, а не як для прямокутника Таким чином, використання ключового слова new в моді Rectangle CaiculateArea () забезпечує, що площа квадрата обчислюється способом для квадрата, а прямокутника – Способом для прямокутника

Але не все так просто, як здається Припустимо, що Rectangle наведено з поніженм до shape Так як при виклику методу CaiculateArea () оголошується спадкування, то площа обчислюється методом для квадрата, що є неправильним Птоми здається, що замість ключового слова new потрібно використовувати ключове сло override Але якщо ЦЕ вирішує проблему С методом ShapeCaiculateArea о, ТО при перетворенні прямокутника в квадрат площа являє прямоуголік, а не квадрат Таким чином, ми маємо ситуацію, для якої немає рішення

Для ілюстрації цих відмінностей розглянемо наступний вихідний код для вичіенія площі фігури Rectangle:

Rectangle els = new Rectangle() elsWidth =20

elsLength = 30

double area = elsCaiculateArea()

У даному прикладі створюється екземпляр класу Rectangle і властивостями width і Length присвоюються значення 20 і 30 відповідно При виклику методу CaiculateArea () розрахований значення площі зберігається у змінній area

Даний вихідний код працює так, як від нього очікується Він створює прямгольнік, присвоює розміри його сторонам і обчислює його площу Але тип Rectangle може також бути типом square Для цього первісний код потрібно модифікувати, як виділено жирним шрифтом в наступному коді:

Rectangle rectangle = new Rectangle() rectangleWidth = 20

rectangleLength = 30

Square square = rectangle

double area = squareCaiculateArea() ConsoleWriteLine(&quotSquare Area is &quot + squareCaiculateArea() +

&quot Rectangle Area is &quot + rectangleCaiculateArea())

Тут змінна rectangle має тип Rectangle Сторонам прямокутника прваіваются розміри, після чого прямокутник перетвориться в квадрат і при-

привласнюється змінної square Коли використовується ключове слово new, то пладь буде 400, що вірно, т к квадрат має розмір тільки для однієї сторони, що дорівнює 20

Тепер припустимо, що ми використовували ключове слово override і привели тип до Square Значення width так і залишиться 20, але площа вже буде 600 І якщо ми протестуємо обчислення площі квадрата, то результати цього тестування будуть негативними

Приклад з класом shape демонструє, що навіть якщо ви думаєте, що квадрат побен прямокутнику, один з цих класів не може успадковувати від іншого, не викликаючи-якого роду проблем Тому shape повинен бути реалізований як інтерфейс, а не як базовий клас А клас Square є базовим класом класу Rectangle, але кожен з цих класів реалізує інтерфейс shape Тоді у нас буде однакове поведінку Рішення виглядає таким чином:

interface IShape {  double CalculateArea()

}

class Square : IShape { double _width

public double Width { set {

width = value

} get {

return _width

}

}

public double CalculateArea() { return _width * _width

}

}

class Rectangle : Square, IShape { double Height

public double Height { set {

height = value

}

get {

return Height

}

} public new double CalculateArea() {

return Width * Height

}

}

У результаті даної модифікації успадкування з використанням як інтерфеов, так і класів поведінка методу calculateArea () буде однаковим, незісімо від того, до якого типу може призвести примірник Rectangle Веріфацію на відсутність проблем з однаковим поведінкою можна виконати за допомогою наступного коду:

Rectangle rectangle = new Rectangle() rectangleHeight = 30

rectangleWidth = 20

Square square = rectangle

IShape shapeCastFromRectangle = rectangle IShape shapeCastFromSquare = square

ConsoleWriteLine(&quotArea Rectangle (&quot + rectangleCalculateArea() + &quot) Area Square (&quot + squareCalculateArea()+

&quot) Area Cast From Rectangle (&quot + shapeCastFromRectangleCalculateArea() + &quot) Area Cast From Square (&quot +

shapeCastFromSquare CalculateArea() + &quot)&quot)

Різні прийоми, використані в цьому фрагменті коду, пояснені в підемо матеріалі даної глави

ПРИМІТКА

Даний приклад ілюструє, що, застосовуючи спадкування, можна виконати Пона приведення типу і отримати необхідну поведінку Але це можливо, тільки якщо ієрархія спадкоємства спроектована належним чином Необхідно Пона, що поведінка залежить від типу, отриманого з дерева успадкування Якщо не дотримуватися належну обережність, то можна отримати дуже дивні побічні еекти Мова С # дозволять явно визначити операції, виконуються кожним методом, і слід дуже уважно обміркувати, що повинен робити кожен метод

Ілюстрація використання компонентів за допомогою фігури, прямокутника і квадрата

Фігуру також можна реалізувати за допомогою компонентів Використання компентов означає визначення ідеї, яке слід визначенням реалізації цієї ідеї Розробка і реалізація компонентів відрізняються від розробки і реалізує ієрархій успадкування При спадкуванні необхідно приймати до уваги приведення типів, функціональність базових класів, а також підміну і пегрузку методів і властивостей {Приведенням типу називається перетворення одного типу в інший із застосуванням або без застосування явного оператора приведення типу) При роботі з компонентами необхідно мислити концепціями та їх реалаціей у вигляді інтерфейсів

Розглянувши реалізації shape, Rectangle і square, ми можемо визначити інтеейс ishape наступним чином:

interface IShape {  double CaicuiateArea()

double Width { get set }

}

До цього оголошенню можна навіть додати властивість Length, але, тим не менш, оая ідея інтерфейсу ishape невірна Чи думаємо ми про фігуру в термінах її довжини і ширини Мабуть, немає Швидше, ми уявляємо собі фігуру як комбінацію площі, периметра та інших властивостей, загальних для всіх фігур А довжина і ширина не є визначальними для всіх фігур Наприклад, коло визначається радіусом або діаметром, а трикутник- розміром підстави, заввишки і зміщенням веіни трикутника Суть полягає в тому, що концепція фігури не є коепціей прямокутника або квадрата

Тому правильне визначення концепцій у вигляді інтерфейсів буде виглеть так:

interface IShape {  double CaicuiateArea()

}

interface ISquare : IShape { double Width { get set }

}

interface IRectangle : IShape { double Width { get set } double Length { get set }

}

Дане визначення містить три інтерфейсу: ishape, що визначає фігуру IRectangle, що визначає прямокутник isquare, що визначає квадрат Іерфейс и IRectangle І ISquare є похідними Інтерфейс а IShape Ет про означає, що вони також ishape Інтерфейси isquare і IRectangle незалежні один від одного, тому що вони є різними фігурами, хоча на вигляд і схожі (взагалі квадрат – це різновид прямокутника, але прямокутник не обовязковий квадрат)

Ця розєднаність інтерфейсів квадрата і прямокутника демонструє, що при розробці інтерфейсів необхідно фокусуватися на конкретному поведемо інтерфейсу, а не на загальному поведінці Загальним поведінкою ми займаємося при розробці класів Моделювання реальних подій визначається в реалізацх, як показано в наступному прикладі:

class Squarelmpl : ISquare, IRectangle { } class Rectanglelmpl – IRectangle { &gt

Клас Squarelmpl реалізує поведінку інтерфейсів ISquare І IRectangle, а також моделює реальність, де квадрат також є і прямокутником А клас Rec tangle imp 1 реалізує тільки поводження IRectangle, демонструючи, що прямгольнік може бути тільки прямокутником, але не квадратом

Тепер написання коду, в якому реалізація видає неоднорідні результати, є неможливим Наприклад, наступний код є неможливим:

IRectangle rectangle = new Rectanglelmpl() ISquare square = (ISquare)rectangle

Але цей код також дозволяється:

IRectangle rectangle = new Squarelmpl() ISquare square = (ISquare)rectangle

ПРИМІТКА

При визначенні ідей, отримані інтерфейси можна розглядати як хараерістікі поведінки, якими може володіти реалізація Цілком припустимо, що реалізація може мати кілька характеристик поведінки Це означає, що, нрімер, реалізація може бути одночасно як квадратом, так і прямокутному У випадку з рестораном, офіціанти – це люди, які мають почуття, желія, переваги та антипатії, але ми не знаємо і не хочемо знати про ці аспекти Все, що нас цікавить в даних людях – це їх функціональність як офіціанта

В якості оптимізації було б можливим наступне спадкування інтерфейсу:

interface ISquare : IShape { double Width { get set }

}

interface IRectangle : ISquare { double Length { get set }

}

або:

interface ISquare : IRectangle {

}

interface IRectangle : IShape { double Width { get set } double Length { get set }

}

Але це не було б дуже гарною ідеєю, т к на рівні інтерфейсів ми подразеваем, що квадрат і прямокутник взаємоповязані Хоча вони можуть і не бути повязані на рівні реалізації Наприклад, припустимо, що ми створюємо суперфігуру з характеристиками прямокутника і трикутника При створенні взаимоотнош між інтерфейсами ми маємо на увазі, що, залежно від застосовуваного успадкування інтерфейсу, суперфігуру повинна мати характеристики квадрата,

хоча вона може і не мати їх Таким чином, при використанні успадкування з інтерфейсами ishape в якості базового інтерфейсу iRectangie є дустімим, але взаємозвязок між iRectangie і isquare – ні Майте на увазі, що дане взаємовідношення може виникнути при реалізації і може бути извле за допомогою приведення типів

Тепер, коли ви у вас є уявлення про відмінності між застосуваннями успадкування та компонентів, ми можемо приступити до створення нашого докладемо для обчислення податків У процесі роботи над створенням цього додатка також надаватимуться подробиці про реалізацію інтерфейсів

Джерело: Гросс К С # 2008: Пер з англ – СПб: БХВ-Петербург, 2009 – 576 е: ил – (Самовчитель)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*