Сайт о телевидении

Сайт о телевидении

» » Концепции и принципы ооп. Принцип инкапсуляции

Концепции и принципы ооп. Принцип инкапсуляции

Столпы объектно-ориентированного программирования

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

· инкапсуляция: как объекты прячут свое внутреннее устройство;

· наследование: как в этом языке поддерживается повторное использование кода;

· полиморфизм: как в этом языке реализована поддержка выполнения нужного действия в зависимости от типа передаваемого объекта?

Конечно же, все три этих принципа реализованы и в С#. Однако перед тем как начать рассматривать синтаксические особенности реализации этих принципов, мы постараемся убедиться, что в них не осталось абсолютно никаких неясностей.

Первый «столп» объектно-ориентированного программирования - это инкапсуляция. Так называется способность прятать детали реализации объектов от пользователей этих объектов. Например, предположим, что вы создали класс с именем DBReader (для работы с базой данных), в котором определено два главных метода: Open() и Close().

// Класс DBReader скрывает за счет инкапсуляции подробности открытия

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

DBReader f = new DBReader();

f.Open(@"C:\foo.mdf");

// Выполняем с базой данных нужные нам действия

Наш вымышленный класс 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();

miniNovel.numberOfPages = 30000000;

Тип данных i nt вполне позволяет указать для книги небольших размеров количество страниц, равное 30 000 000. Однако понятно, что книг такой величины не бывает, и во избежание дальнейших проблем желательно использовать какой-нибудь механизм проверки, который отсеивал бы явно нереальные значения (например, он пропускал бы только значения между 1 и 2000). Применение поля - открытой переменной не дает нам возможности простым способом реализовать подобный механизм. Поэтому поля в реальных рабочих приложениях используются нечасто.

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

· создать традиционную пару методов - один для получения информации (accessor), второй - для внесения изменений (mutator);

· определить именованное свойство.

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

Инкапсуляция - это одна из трех основных особенностей Две другие - полиморфизм и наследование. Вместе они составляют базу ООП, определяющую целый спектр возможностей написания программ на разных языках, используя эти три принципа. Объектно-ориентированные языки, в свою очередь, обязаны четко им следовать.

Основы ООП

Объектно-ориентированное программирование стоит на трех китах своего мироздания:

  • Полиморфизме, отвечающем на вопрос, как определенный язык программирования трактует объекты, имеющие между собой связь, сходным образом.
  • Наследовании, дающем ответ, каким образом происходит стимуляция использования кода многократно.
  • Инкапсуляции, являющейся ответом на вопрос, как происходит сокрытие реализации, а значит и сохранения целостности данных.

Терминология

Инкапсуляция (программирование) - это использование модификаторов доступа с целью сокрытия частей программного кода от конечного пользователя. Под ним, в свою очередь, подразумевается разработчик или наследующий объект.

Суть понятия "инкапсуляция"

Дефиниция определяет, что под инкапсуляцией подразумевается сокрытие всего или части программного кода. Суть же понятия "инкапсуляция" заключается в манипулировании модификаторами доступа. Это означает, что разработчик сам решает, какие свойства, методы и классы будут открыты классу-клиенту, а какие - скрыты.

Модификаторы доступа

Существуют такие модификаторы доступа, которыми, в числе прочих, способна манипуляривать инкапсуляция (программирование Java):

  • public ("паблик" - публичный, открытый, доступ) - общий доступ как для текущих объектов и классов, так и для внешнего мира;
  • private ("прайват" - приватный, частный, скрытый доступ) - закрытый доступ, суть которого полностью противоположна предыдущему. Обеспечивает доступ только из текущего класса;
  • protected ("протектед" - защищенный, полускрытый, доступ) - доступ для текущего класса и производных от него;
  • по умолчанию - неуказанный модификатор доступа подразумевает, что поле/метод видно для всего текущего пакета классов.

В языке C# ("Си Шарп"), помимо указанных (исключая последний), есть еще такие модификаторы:

  • internal ("интернал" - внутренний доступ) - общедоступность в текущем сборе, закрытый доступ по всем остальным случаям;
  • internal protected ("интернал протектед" - внутренний защищенный доступ) - объединение двух модификаторов в один, в котором проявляются свойства обоих из них.

Роль инкапсуляции

Механизм инкапсуляции позволяет исключить внешнее влияние на программный код и неверное использование заложенных в нем данных. Это происходит за счет объединения кода и данных в одно целое.

Объект и инкапсуляция

Объединение реализации программного модуля и данных, заложенных в коде, в программировании называется объектом. Суть связи его с инкапсуляцией заключается в том, что именно такая методика позволяет поддерживать и обеспечивать целостное функционирование рассматриваемого механизма.

Преимущество инкапсуляции

Инкапсуляция - это способ упростить процесс кодирования. Многочисленные строки кода остаются "за кулисами", а в основном классе работа идет с экземплярами объектов.

Идея защиты данных

Инкапсуляция - это также механизм, который реализует идею защиты данных. Программная логика объектно-ориентированного программирования построена на базе того, что большая часть данных будет скрыта модификатором доступа private (приватный, частный) или protected (защищенный). Внешний мир, клиент случайно или преднамеренно не смогут повредить реализации программного модуля. Поскольку на самом деле сделать это очень легко даже не нарочно, инкапсуляция - это очень хороший принцип.

Единицы инкапсуляции

Класс, как основная единица инкапсуляции, описывает данные и содержит код, который этими данными способен оперировать. Также он является базой для построения объекта. Последний, в свою очередь, представлен как экземпляр класса.

Используется также следующая терминология:

  • члены - это код и данные, входящие в класс;
  • поля, или переменные экземпляра - так называются данные, которые определяет класс;
  • функции-члены - в них содержится сам код. Функции-члены - общее название. Частный случай - методы.

Инкапсуляция на конкретном примере

Инкапсуляция (программирование) пример:

* Примечание:

description - это описание метода/свойства/переменной, то есть комментирование того, что, собственно, происходит в программе. Продемонстрировано при помощи открывающихся/закрывающихся тегов

using System;

namespace OOPLibrary.Auto

/// Данный класс предназначен для того, чтобы описывать свойства и действия автомобиля

public class Auto

/// Переменная, созданная для того, чтобы в нее записывать, сколько автомобилю лет, так как внешнее вмешательство в это свойство разработчик считает лишним

/// оно помечается модификатором private, то есть закрытый, частный доступ (см. описание выше).

private int _age;

/// Булевская переменная (только два возможных значения - либо да, либо нет), которая описывает, движется ли автомобиль на данный момент

/// Она также не должна быть открыта для конечного пользователя, кем бы он ни был. Потому и этой переменной присваивается закрытый модификатор доступа "прайват"

private bool _isMoving;

/// В данной строковой переменной должна содержаться информация о цвете автомобиля. Он может подвергаться изменениям со стороны внешнего воздействия

/// потому для Цвета выбран модификатор открытого доступа "паблик".

public string Color;

/// В данном конкретном случае допускаем, что имя автомобиля тоже можно менять

/// присваивается модификатор паблик (открытый доступ для всех, вне зависимости от класса или сборки).

public string Name;

/// Открывается конструктор класса и все свойства, выраженные переменными и заданные чуть ранее, получают свои значения

public Auto()

_age = 5;

_isMoving = false;

Color = "Фиолетовый";

Name = "Шкода Октавиа";

/// Метод реализует возврат значения возраста авто. Зачем это необходимо?

/// закрытый модификатор доступа не делает его возможным для изменения клиентом.

/// Возвращает возраст автомобиля.

public string GetAge()

return "На данный момент выбранной машине" + _age + " лет.";

/// Если автомобиль не движется, этот метод реализует старт движения. Выполняется проверка переменной, обознающей, в каком состоянии находится автомобиль (едет он или нет), и, в зависимости от результатов, выполняется соответствующее действие/выводится соответствующее сообщение.

public void Start()

if (_isMoving)

Console.WriteLine("Движение уже было начато");

_isMoving = true;

Console.WriteLine("На старт, внимание.. Вперед! Поехали!");

/// Если движение было начато, то этот метод его останавливает. Та же программная логика, что и в предыдущем рассмотренном случае.

public void Stop()

if (_isMoving)

_isMoving = false;

Console.WriteLine("Стоп, машина");

Console.WriteLine("Ошибка. Автомобиль и так стоит на месте, не движется");

/// Осуществляется поворот налево, если имеет место движения автомобиля

public void MoveLeft()

if (_isMoving)

Console.WriteLine("Осуществлен поворот налево");

Console.WriteLine("Ошибка. Автомобиль стоит на месте. Функция поворота на данный момент недоступна");

/// Аналогичный метод с поворотом направо

public void MoveRight()

if (_isMoving)

Console.WriteLine("Поворот направо был осуществлен успешно");

Console.WriteLine("Ошибка. Автомобиль еще не двигался с места. Поворот направо является на данный момент действием, невозможным для выполнения.");

Но в действительности обширно встречается и в других (см. подтипизация на записях и полиморфизм записей и вариантов). В ООП инкапсуляция тесно связана с принципом абстракции данных (не путать с абстрактными типами данных , реализации которых предоставляют возможность инкапсуляции, но имеют иную природу). Это, в частности, приводит к другому распространённому заблуждению - рассмотрению инкапсуляции неотрывно от сокрытия . В частности, в сообществе С++ или Java принято рассматривать инкапсуляцию без сокрытия как неполноценную. Однако, некоторые языки (например, Smalltalk , Python) реализуют инкапсуляцию в полной мере, но не предусматривают возможности сокрытия в принципе. Другие (Standard ML , OCaml) жёстко разделяют эти понятия как ортогональные и предоставляют их в семантически различном виде (см. сокрытие в языке модулей ML).

Энциклопедичный YouTube

    1 / 3

    ✪ Что такое геттеры и сеттеры для класса. Методы get и set. Инкапсуляция это. Пример. C++ Урок #76

    ✪ Python Tutorial - 39 - Classes Encapsulation

    ✪ OCJA (1Z0 - 808) || Object Oriented Programming: Encapsulation

    Субтитры

Подробности

PHP5

class A { private $a ; // скрытое свойство private $b ; // скрытое свойство private function DoSomething () //скрытый метод { //actions } public function ReturnSomething () //открытый интерфейс { //actions } };

В этом примере закрыты свойства $a и $b для класса A с целью предотвращения повреждения этих свойств другим кодом, которому необходимо предоставить только права на чтение.

Минимальность и инкапсуляция

В "Эффективном использовании C++" (Effective C++), я приводил доводы в пользу интерфейсов класса, которые являются полными и минимальный . Такие интерфейсы позволяют клиентам класса делать что-либо, что они могли бы предположительно хотеть делать, но классы содержат методов не больше, чем абсолютно необходимо. Добавление функций вне минимума, необходимого для того, чтобы клиент мог сделать его работу, как я писал, уменьшает возможности повторного использования класса. Кроме того, увеличивается время трансляции для программы клиента. Джек Ривес (Jack Reeves) написал, что добавление методов сверх этих требований, нарушает принцип открытости-закрытости, производит жирные интерфейсы класса, и в конечном счете ведет к загниванию программного обеспечения . Возможно увеличение числа параметров, чтобы уменьшить число методов в классе, но теперь мы имеем дополнительную причину, чтобы отказаться от этого: уменьшается инкапсуляция класса.

Конечно, минимальный интерфейс класса – не обязательно самый лучший интерфейс. Я отметил в "Эффективном использовании C++", что добавление функций сверх необходимости может быть оправданным, если это значительно увеличивает эффективность класса, делает класс, более легким в использовании или предотвращает вероятные клиентские ошибки . Основываясь на его работе с различными строковыми классами, Джек Ривес отмечает, что для некоторых функций трудно ощутить, когда их делать не членами, даже если они могли быть не друзьями и не методами . "Наилучший" интерфейс для класса может быть найден только после балансировки между многими конкурирующими параметрами, среди которых степень инкапсуляции является лишь одним.

Однако урок этой статьи должен быть ясен. Стандартная мудрость, несмотря на использование "недругов" и не методов улучшает инкапсуляцию класса, и предпочтительнее для таких функций над методами, потому что делает решение проще, когда надо проектировать и разрабатывать классы с интерфейсами, которые являются полными и минимальными (или близкими к минимальному). Возражения, связанные с неестественностью, возникающей в результате изменения синтаксиса вызова, являются совершенно необоснованными, а склонность к "недругам" и не методам ведет к пакующим стратегиям для организации интерфейсов класса, которые минимизируют клиентские зависимости при трансляции, сохраняя доступ к максимальному числу дополнительных функций.

Пришло время, чтобы отказаться от традиционной, но неточной идеи, связанной с тем, что означает "быть объектно-ориентированным". Действительно ли Вы – истинный сторонник инкапсуляции? Если так, я знаю, что вы ухватитесь за "недружественные" внешние функции с пылом, которого они заслуживают.

Из книги Сущность технологии СОМ. Библиотека программиста автора Бокс Дональд

Инкапсуляция и С++ Предположим, что вам удалось преодолеть проблемы с транслятором и компоновщиком, описанные в предыдущем разделе. Очередное препятствие при построении двоичных компонентов на C++ появится, когда вы будете проводить инкапсуляцию (encapsulation), то есть

Из книги Как функции, не являющиеся методами, улучшают инкапсуляцию автора Мейерс Скотт

Инкапсуляция Инкапсуляция не определяет вершину мира. Нет ничего такого, что могло бы возвысить инкапсуляцию. Она полезна только потому, что влияет на другие аспекты нашей программы, о которых мы заботимся. В частности, она обеспечивает гибкость программы и ее

Из книги Информатика и информационные технологии: конспект лекций автора Цветкова А В

Инкапсуляция и функции – не члены Мы теперь видим, что приемлемый способом оценки инкапсуляции является количество функций, которые могли бы быть разрушены, если изменяется реализация класса. В этом случае становится ясно, что класс с n методами более инкапсулирован, чем

Из книги Информатика и информационные технологии автора Цветкова А В

Из книги Язык программирования С# 2005 и платформа.NET 2.0. автора Троелсен Эндрю

автора Реймонд Эрик Стивен

Инкапсуляция Первым принципом ООП является инкапсуляция. По сути, она означает возможность скрыть средствами языка несущественные детали реализации от пользователя объекта. Предположим, например, что мы используем класс DatabaseReader, который имеет два метода Open() и Close().//

Из книги Искусство программирования для Unix автора Реймонд Эрик Стивен

Инкапсуляция на основе методов чтения и модификации Давайте снова вернемся к рассмотрению нашего класса Employee. Чтобы "внешний мир" мог взаимодействовать с частным полем данных fullName, традиции велят определить средства чтения (метод get) и модификации (метод set). Например://

Из книги Основы объектно-ориентированного программирования автора Мейер Бертран

Инкапсуляция на основе свойств класса В отличие от традиционных методов чтения и модификации, языки.NET тяготеют к реализации принципа инкапсуляции на основе использования свойств, которые представляют доступные для внешнего пользователя элементы данных. Вместо того,

Из книги TCP/IP Архитектура, протоколы, реализация (включая IP версии 6 и IP Security) автора Фейт Сидни М

Из книги Сетевые средства Linux автора Смит Родерик В.

4.1. Инкапсуляция и оптимальный размер модуля Первым и наиболее важным качеством модульного кода является инкапсуляция. Правильно инкапсулированные модули не открывают свое внутренне устройство друг другу. Они не обращаются к центральной части реализации друг друга,

Из книги Операционная система UNIX автора Робачевский Андрей М.

Инкапсуляция действий со ссылками Теперь накоплено достаточно подтверждений того, что любая система моделирования и разработки ПО должна поддерживать понятие ссылки, а, следовательно, и динамические псевдонимы. Как теперь справиться с неприятными последствиями?

Из книги автора

24.5.4 Инкапсуляция защищенной полезной нагрузки Заголовок инкапсуляции защищенной полезной нагрузки протокола IP (IP Encapsulating Security Payload) применяется как для режима транспорта, так и для режима туннеля.Формат этого заголовка показан на рис. 24.8. Получатель использует индекс SPI

Из книги автора

Инкапсуляция и извлечение данных Стек протоколов хорошо иллюстрирует перемещение данных между программными компонентами, поддерживающими сетевое взаимодействие, однако он не дает ответа на вопрос, какие же изменения претерпевает информация на этом пути. На различных

Из книги автора

Инкапсуляция IP При работе в локальной сети на базе технологии CSMA/CD возможны два варианта инкапсуляции датаграмм IP в кадры уровней LLC и MAC.Первый заключается в использовании кадров Ethernet 2.0. В этом случае поле данных (1500 октетов) полностью принадлежит IP-датаграмме, a SAP

11 марта 2010 в 22:41

ООП с примерами (часть 2)

  • Учебный процесс в IT

Волею судьбы мне приходится читать спецкурс по паттернам проектирования в вузе. Спецкурс обязательный, поэтому, студенты попадают ко мне самые разные. Конечно, есть среди них и практикующие программисты. Но, к сожалению, большинство испытывают затруднения даже с пониманием основных терминов ООП.

Для этого я постарался на более-менее живых примерах объяснить базовые понятия ООП (класс, объект, интерфейс, абстракция, инкапсуляция, наследование и полиморфизм).

Посвящена классам, объектам и интерфейсам.
Вторая часть, представленная ниже, иллюстрирует инкапсуляцию, полиморфизм и наследование

Инкапсуляция

Представим на минутку, что мы оказались в конце позапрошлого века, когда Генри Форд ещё не придумал конвейер, а первые попытки создать автомобиль сталкивались с критикой властей по поводу того, что эти коптящие монстры загрязняют воздух и пугают лошадей. Представим, что для управления первым паровым автомобилем необходимо было знать, как устроен паровой котёл, постоянно подбрасывать уголь, следить за температурой, уровнем воды. При этом для поворота колёс использовать два рычага, каждый из которых поворачивает одно колесо в отдельности. Думаю, можно согласиться с тем, что вождение автомобиля того времени было весьма неудобным и трудным занятием.

Теперь вернёмся в сегодняшний день к современным чудесам автопрома с коробкой-автоматом. На самом деле, по сути, ничего не изменилось. Бензонасос всё так же поставляет бензин в двигатель, дифференциалы обеспечивают поворот колёс на различающиеся углы, коленвал превращает поступательное движение поршня во вращательное движение колёс. Прогресс в другом. Сейчас все эти действия скрыты от пользователя и позволяют ему крутить руль и нажимать на педаль газа, не задумываясь, что в это время происходит с инжектором, дроссельной заслонкой и распредвалом. Именно сокрытие внутренних процессов, происходящих в автомобиле, позволяет эффективно его использовать даже тем, кто не является профессионалом-автомехаником с двадцатилетним стажем. Это сокрытие в ООП носит название инкапсуляции.

Инкапсуляция – это свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе и скрыть детали
реализации от пользователя.

Инкапсуляция неразрывно связана с понятием интерфейса класса. По сути, всё то, что не входит в интерфейс, инкапсулируется в классе.

Абстракция

Представьте, что водитель едет в автомобиле по оживлённому участку движения. Понятно, что в этот момент он не будет задумываться о химическом составе краски автомобиля, особенностях взаимодействия шестерён в коробке передач или влияния формы кузова на скорость (разве что, автомобиль стоит в глухой пробке и водителю абсолютно нечем заняться). Однако, руль, педали, указатель поворота (ну и, возможно, пепельницу) он будет использовать регулярно.

Абстрагирование – это способ выделить набор значимых характеристик объекта, исключая из рассмотрения незначимые. Соответственно, абстракция – это набор всех таких характеристик.

Если бы для моделирования поведения автомобиля приходилось учитывать химический состав краски кузова и удельную теплоёмкость лампочки подсветки номеров, мы никогда бы не узнали, что такое NFS.

Полиморфизм

Любое обучение вождению не имело бы смысла, если бы человек, научившийся водить, скажем, ВАЗ 2106 не мог потом водить ВАЗ 2110 или BMW X3. С другой стороны, трудно представить человека, который смог бы нормально управлять автомобилем, в котором педаль газа находится левее педали тормоза, а вместо руля – джойстик.

Всё дело в том, что основные элементы управления автомобиля имеют одну и ту же конструкцию и принцип действия. Водитель точно знает, что для того, чтобы повернуть налево, он должен повернуть руль, независимо от того, есть там гидроусилитель или нет.
Если человеку надо доехать с работы до дома, то он сядет за руль автомобиля и будет выполнять одни и те же действия, независимо от того, какой именно тип автомобиля он использует. По сути, можно сказать, что все автомобили имеют один и тот же интерфейс, а водитель, абстрагируясь от сущности автомобиля, работает именно с этим интерфейсом. Если водителю предстоит ехать по немецкому автобану, он, вероятно выберет быстрый автомобиль с низкой посадкой, а если предстоит возвращаться из отдалённого маральника в Горном Алтае после дождя, скорее всего, будет выбран УАЗ с армейскими мостами. Но, независимо от того, каким образом будет реализовываться движение и внутреннее функционирование машины, интерфейс останется прежним.

Полиморфизм – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.

Например, если вы читаете данные из файла, то, очевидно, в классе, реализующем файловый поток, будет присутствовать метод похожий на следующий: byte readBytes(int n);
Предположим теперь, что вам необходимо считывать те же данные из сокета. В классе, реализующем сокет, также будет присутствовать метод readBytes . Достаточно заменить в вашей системе объект одного класса на объект другого класса, и результат будет достигнут.

При этом логика системы может быть реализована независимо от того, будут ли данные прочитаны из файла или получены по сети. Таким образом, мы абстрагируемся от конкретной специализации получения данных и работаем на уровне интерфейса. Единственное требование при этом – чтобы каждый используемый объект имел метод readBytes .

Наследование

Представим себя, на минуту, инженерами автомобильного завода. Нашей задачей является разработка современного автомобиля. У нас уже есть предыдущая модель, которая отлично зарекомендовала себя в течение многолетнего использования. Всё бы хорошо, но времена и технологии меняются, а наш современный завод должен стремиться повышать удобство и комфорт выпускаемой продукции и соответствовать современным стандартам.

Нам необходимо выпустить целый модельный ряд автомобилей: седан, универсал и малолитражный хэтч-бэк. Очевидно, что мы не собираемся проектировать новый автомобиль с нуля, а, взяв за основу предыдущее поколение, внесём ряд конструктивных изменений. Например, добавим гидроусилитель руля и уменьшим зазоры между крыльями и крышкой капота, поставим противотуманные фонари. Кроме того, в каждой модели будет изменена форма кузова.

Очевидно, что все три модификации будут иметь большинство свойств прежней модели (старый добрый двигатель 1970 года, непробиваемая ходовая часть, зарекомендовавшая себя отличным образом на отечественных дорогах, коробку передач и т.д.). При этом каждая из моделей будет реализовать некоторую новую функциональность или конструктивную особенность. В данном случае, мы имеем дело с наследованием.
Наследование – это свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым или родительским. Новый класс – потомком, наследником или производным классом.

Необходимо отметить, что производный класс полностью удовлетворяет спецификации родительского, однако может иметь дополнительную функциональность. С точки зрения интерфейсов, каждый производный класс полностью реализует интерфейс родительского класса. Обратное не верно.

Действительно, в нашем примере мы могли бы произвести с новыми автомобилями все те же действия, что и со старым: увеличить или уменьшить скорость, повернуть, включить сигнал поворота. Однако, дополнительно у нас бы появилась возможность, например, включить противотуманные фонари.

Отсутствие обратной совместимости означает, что мы не должны ожидать от старой модели корректной реакции на такие действия, как включения противотуманок (которых просто нет в данной модели).