Інкапсуляція - студопедія

Стовпи об'єктно-орієнтованого програмування

С # можна вважати новим членом спільноти об'єктно-орієнтованих мов програмування, до найпоширеніших з яких відносяться Java, C ++, Object Pascal і (з деякими припущеннями) Visual Basic 6.0. У будь-якому об'єктно-орієнтованої мови програмування обов'язково реалізовані три найважливіших принципу - «стовпи» об'єктно-орієнтованого програмування:







· Інкапсуляція: як об'єкти ховають своє внутрішнє пристрій;

· Успадкування: як в цій мові підтримується повторне використання коду;

· Поліморфізм: як в цій мові реалізована підтримка виконання потрібної дії в залежності від типу переданого об'єкта?

Звичайно ж, всі три ці принципи реалізовані і в С #. Однак перед тим як почати розглядати синтаксичні особливості реалізації цих принципів, ми постараємося переконатися, що в них не залишилося абсолютно ніяких неясностей.

Перший «стовп» об'єктно-орієнтованого програмування - це інкапсуляція. Так називається здатність ховати деталі реалізації об'єктів від користувачів цих об'єктів. Наприклад, припустимо, що ви створили клас з ім'ям DBReader (для роботи з базою даних), в якому визначено два основних способи: Open () і Close ().

// Клас DBReader приховує за рахунок інкапсуляції подробиці відкриття

// і закриття баз даних

DBReader f = new DBReader ();

// Виконуємо з базою даних потрібні нам дії

Наш вигаданий клас DBReader инкапсулирует внутрішні подробиці того, як саме він виявляє, завантажує, виконує операції і в кінці кінців закриває файл даних. За рахунок інкапсуляції програмування стає простіше: вам немає необхідності турбуватися про величезну кількість рядків коду, які виконують своє завдання приховано від вас. Все, що від вас вимагається - створити екземпляр потрібного класу і передати йому необхідні повідомлення (типу «відкрити файл з ім'ям foo.mdf»).

З філософією інкапсуляції тісно пов'язаний ще один принцип - приховування всіх внутрішніх даних (тобто змінних-членів) класу. Як ми вже говорили, в ідеалі всі внутрішні змінні члени класу повинні бути визначені як private. В результаті звернення до них з зовнішнього світу буде можливо тільки за допомогою відкритих функцій - членів. Таке рішення, крім усього іншого, захищає вас від можливих помилок, оскільки відкриті дані дуже просто пошкодити.

Спадкування: відносини «бути» і «мати»

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

Мал. 3.3. Ставлення «бути»

Як ми пам'ятаємо, на вершині будь-якої ієрархії в .NET завжди знаходиться базовий клас Object. У нашій ситуації можливості цього класу спочатку доповнюються можливостями, привнесеними класом Shape. Йдеться про властивості, полях, методах і події, які є загальними для всіх геометричних фігур (shapes). Клас Hexagon (шестикутник), похідний від Shape, доповнює можливості попередніх двох базових класів своїми власними унікальними властивостями.

Діаграму, представлену на рис. 3.3, можна прочитати таким чином-«Шестиугольник є геометрична фігура, яка є об'єкт». Коли ваші • ласо Зв'язуються один з одним відносинами спадкування, це означає, що зи встановлюєте між ними відносини типу «бути» (is-a). Такий тип відносин називається також класичним спадкуванням.

У світі об'єктно-орієнтованого програмування використовується ще одна еорма повторного використання коду. Ця форма називається включенням-деле- "ювання (або відношенням« мати »- has-a). При її використанні один клас включає до свого складу іншого і відкриває зовнішнього світу частина можливостей цього внутрішнього класу.

Наприклад, якщо ви створюєте програмну модель автомобіля, у вас може з'явитися ідея включити всередину об'єкта «автомобіль» об'єкт «радіо» за допомогою відносини «мати». Це цілком розумний підхід, оскільки навряд чи можливо зробити як радіо від автомобіля, так і автомобіль від радіо, використовуючи відносини спадкування. Замість успадкування ви створюєте два незалежних класу, що працюють спільно, де зовнішній (контейнерний) клас створює внутрішній клас і відкриває зовнішнього світу його можливості (рис. 3.4).

Інкапсуляція - студопедія

Мал. 3.4. Відносини між середовищем виконання і бібліотекою базових класів .NET

Зовнішній об'єкт саг (автомобіль) відповідальний за створення внутрішнього об'єкта Radio (радіо). Якщо ви хочете, щоб через об'єкт саг можна було б звертатися до внутрішнього об'єкту Radio, в класі саг необхідно передбачити спеціальний відкритий інтерфейс для цієї мети. Зверніть увагу, що користувач об'єкта саг і не дізнається, що той передає звернення якомусь внутрішньому об'єкту:







// Внутрішній клас Radio инкапсулирован зовнішнім класом саг

Саг viper = new Car ();

Viper.TurnOnRadio (false); // Виклик буде переданий внутрішньому об'єкту Radio

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

Останній, третій стовп об'єктно-орієнтованого програмування - це поліморфізм. Можна сказати, що цей термін визначає можливості, закладені в мові, по інтерпретації пов'язаних об'єктів однаковим чином. Існує дві основні різновиди поліморфізму: класичний поліморфізм і поліморфізм «для конкретного випадку» (ad hoc). Класичний поліморфізм зустрічається тільки в тих мовах, які підтримують класичне спадкування (в тому числі, звичайно, і в С #). При класичному поліморфізм ви можете визначити в базовому класі набір членів, які можуть бути заміщені в похідному класі. При заміщенні в похідних класах членів базового класу ці похідні класи будуть по-різному реагувати на одні і ті ж звернення.

Для прикладу ми знову звернемося до нашої ієрархії геометричних фігур. Припустимо, що в класі Shape (геометрична фігура) визначена функція Draw () - малювання, яка не приймає параметрів і нічого не повертає Оскільки геометричні фігури бувають різними і кожен тип фігури потрібно зображати своїм власним унікальним способом, швидше за все, нам потрібно в похідних класах (таких як Hexagon - шестикутник і Circle - окружність) створити свій власний метод Draw (), замінивши їм метод Draw () базового класу (рис. 3.5).

Інкапсуляція - студопедія

Мал. 3.5. класичний поліморфізм

Класичний поліморфізм дозволяє визначати можливості всіх похідних класів при створенні базового класу. Наприклад, в нашому прикладі ви можете бути впевнені, що метод Draw () в тому чи іншому варіанті присутній в будь-якому класі, похідному від Shape. До переваг класичного поліморфізму можна віднести також і те, що в багатьох ситуаціях ви зможете уникнути створення повторюваних методів для виконання подібних операцій (типу DrawCircle (), DrawRectangleO, DrawHexagon () і т. Д.).

Другий різновид поліморфізму - поліморфізм для конкретного випадку (ad hoc polymorphism). Цей тип поліморфізму дозволяє звертатися схожим чином до об'єктів, не пов'язаних класичним спадкуванням. Досягається це дуже просто: в кожному з таких об'єктів повинен бути метод з однаковою сигнатурою (тобто однаковим ім'ям методу, прийнятими параметрами і типом значення, що повертається. У мовах, що підтримують поліморфізм цього типу, застосовується технологія «пізнього зв'язування» (late binding), коли тип об'єкту, до якого відбувається звернення, стає очевидним тільки в процесі виконання програми. в залежності від того, до якого типу ми звертаємося, викликається потрібний метод. як приклад розглянемо схему на рис. 3.6.

Зверніть увагу, що спільного предка - базового класу для ССircle, СНехаgon і CRectangle не існує. Однак в кожному класі передбачено метод Draw () з однаковою сигнатурою. Для того щоб продемонструвати застосування поліморфізму цього типу в реальному коді, ми скористаємося прикладом на Visual Basic 6.0. До винаходу VB.NET Visual Basic не підтримував класичний поліморфізм (так само, як і класичне спадкування), змушуючи розробників зосереджувати свої зусилля на поліморфізм ad hoc.

Інкапсуляція - студопедія

Мал. 3.6. Поліморфізм для конкретного випадку

'Це - код на Visual Basic 6.0!

'Спочатку створимо масив елементів типу Object і встановимо для кожного елемента посилання на об'єкт

Dim objArr (3) as Object

Set objArr (0) = New Ccircle

Set objArr (1) = New Chexagon

Set objArr (2) = New Ccircle

Set objArr (3) = New Crectangle

'Тепер за допомогою циклу змусимо кожен елемент намалювати самого себе

Dim i as Integer

objArr (i). Draw () 'Пізніше зв'язування

У цьому коді ми спочатку створили масив елементів типу Object (це вбудований тип даних Visual Basic 6.0 для зберігання посилань на будь-які об'єкти, що не має нічого спільного з класом System.Object в .NET). Потім ми зв'язали кожен елемент масиву з об'єктом відповідного типу, а потім за допомогою циклу скористалися методом Draw () для кожного з цих об'єктів. Зверніть увагу, що у геометричних фігур - елементів масиву - немає загального базового класу з реалізацією методу Draw () за замовчуванням.

Теоретичний огляд головних принципів поліморфізму - інкапсуляції, успадкування та поліморфізму на цьому закінчено. Звичайно ж, в С # реалізовані всі ці принципи, при цьому С # підтримує і відносини «бути» і відносини «мати» для повторного використання коду, і обидва різновиди поліморфізму. Тепер наше завдання - дізнатися, як реалізується кожен з цих принципів засобами синтаксису С #.

Засоби інкапсуляції в С #

Принцип інкапсуляції передбачає, що до внутрішніх даними об'єкта (змінним-членам) не можна звернутися безпосередньо через екземпляр цього об'єкта. Замість цього для отримання інформації про внутрішній стан об'єкта та внесення змін необхідно використовувати спеціальні методи. У С # інкапсуляція реалізується на рівні синтаксису за допомогою ключових слів public, private і protected. Як приклад ми розглянемо таке визначення класу:

// Клас з єдиним полем

public class Book

public int numberOfPages;

Термін «поле» (field) використовується для відкритих даних класу - змінних, оголошених з ключовим словом public. При використанні полів в додатку виникає проблема: полю можна привласнити будь-яке значення, а організувати перевірку цього значення бізнес-логікою вашого застосування досить складно. Наприклад, для нашої відкритої змінної numberOfPages використовується тип даних int. Максимальне значення для цього типу даних - це досить велике число (2 147 483 647). Якщо в програмі буде існувати такий код, проблем з боку компілятора не виникне:

public static void Main ()

Book miniNovel = new Book ();

Дотримання принципу інкапсуляції дозволяє захистити внутрішні дані класу від ненавмисного пошкодження. Для цього достатньо всі внутрішні дані зробити закритими (оголосивши внутрішні змінні з використанням ключових слів private або protected). Для звернення до внутрішніх даних можна використовувати один з двох способів:

· Створити традиційну пару методів - один для отримання інформації (accessor), другий - для внесення змін (mutator);

· Визначити іменоване властивість.

Ще один метод захисту даних, пропонований С #, - використовувати ключове слово readonly. Однак який би спосіб ви не вибрали, загальний принцип залишається тим же самим - інкапсульований клас повинен ховати деталі своєї реалізації від зовнішнього світу. Такий підхід часто називається «програмуванням за методом чорного ящика». Ще одна перевага такого підходу полягає в тому, що ви можете як завгодно вдосконалювати внутрішню реалізацію свого класу, повністю змінюючи його вміст. Єдине, про що вам доведеться подбати, - щоб у новій реалізації залишилися методи з тієї ж сигнатурою і функціональністю, що і в попередніх версіях. В цьому випадку вам не доведеться міняти жодного рядка існуючого коду за межами даного класу.







Схожі статті