Инкапсуляция определение. ООП. Инкапсуляция и сокрытие данных. Как эффективно скрывать реализацию и создавать слабосвязанные объекты

Последнее обновление: 29.07.2018

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

Стандартное описание свойства имеет следующий синтаксис:

[модификатор_доступа] возвращаемый_тип произвольное_название { // код свойства }

Например:

Class Person { private string name; public string Name { get { return name; } set { name = value; } } }

Здесь у нас есть закрытое поле name и есть общедоступное свойство Name . Хотя они имеют практически одинаковое название за исключением регистра, но это не более чем стиль, названия у них могут быть произвольные и не обязательно должны совпадать.

Через это свойство мы можем управлять доступом к переменной name . Стандартное определение свойства содержит блоки get и set . В блоке get мы возвращаем значение поля, а в блоке set устанавливаем. Параметр value представляет передаваемое значение.

Мы можем использовать данное свойство следующим образом:

Person p = new Person(); // Устанавливаем свойство - срабатывает блок Set // значение "Tom" и есть передаваемое в свойство value p.Name = "Tom"; // Получаем значение свойства и присваиваем его переменной - срабатывает блок Get string personName = p.Name;

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

Class Person { private int age; public int Age { set { if (value < 18) { Console.WriteLine("Возраст должен быть больше 17"); } else { age = value; } } get { return age; } } }

Блоки set и get не обязательно одновременно должны присутствовать в свойстве. Если свойство определяют только блок get, то такое свойство доступно только для чтеня - мы можем получить его значение, но не установить. И, наоборот, если свойство имеет только блок set, тогда это свойство доступно только для записи - можно только установить значение, но нельзя получить:

Class Person { private string name; // свойство только для чтения public string Name { get { return name; } } private int age; // свойство только для записи public int Age { set { age = value; } } }

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

Мы можем применять модификаторы доступа не только ко всему свойству, но и к отдельным блокам - либо get, либо set:

Class Person { private string name; public string Name { get { return name; } private set { name = value; } } public Person(string name, int age) { Name = name; Age = age; } }

Теперь закрытый блок set мы сможем использовать только в данном классе - в его методах, свойствах, конструкторе, но никак не в другом классе:

Person p = new Person("Tom", 24); // Ошибка - set объявлен с модификатором private //p.Name = "John"; Console.WriteLine(p.Name);

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

    Модификатор для блока set или get можно установить, если свойство имеет оба блока (и set, и get)

    Только один блок set или get может иметь модификатор доступа, но не оба сразу

    Модификатор доступа блока set или get должен быть более ограничивающим, чем модификатор доступа свойства. Например, если свойство имеет модификатор public, то блок set/get может иметь только модификаторы protected internal, internal, protected, private

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

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

Например, есть некоторый класс Account, в котором определено поле sum, представляющее сумму:

Class Account { public int sum; }

Поскольку переменная sum является публичной, то в любом месте программы мы можем получить к ней доступ и изменить ее, в том числе установить какое-либо недопустимое значение, например, отрицательное. Вряд ли подобное поведение является желательным. Поэтому применяется инкапсуляция для ограничения доступа к переменной sum и сокрытию ее внутри класса:

Class Account { private int sum; public int Sum { get {return sum;} set { if (value > 0) { sum=value; } } } }

Автоматические свойства

Свойства управляют доступом к полям класса. Однако что, если у нас с десяток и более полей, то определять каждое поле и писать для него однотипное свойство было бы утомительно. Поэтому в фреймворк.NET были добавлены автоматические свойства. Они имеют сокращенное объявление:

Class Person { public string Name { get; set; } public int Age { get; set; } public Person(string name, int age) { Name = name; Age = age; } }

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

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

Стоит учитывать, что нельзя создать автоматическое свойство только для записи, как в случае со стандартными свойствами.

Автосвойствам можно присвоить значения по умолчанию (инициализация автосвойств):

Class Person { public string Name { get; set; } = "Tom"; public int Age { get; set; } = 23; } class Program { static void Main(string args) { Person person = new Person(); Console.WriteLine(person.Name); // Tom Console.WriteLine(person.Age); // 23 Console.Read(); } }

И если мы не укажем для объекта Person значения свойств Name и Age, то будут действовать значения по умолчанию.

Автосвойства также могут иметь модификаторы доступа:

Class Person { public string Name { private set; get;} public Person(string n) { Name = n; } }

Мы можем убрать блок set и сделать автосвойство доступным только для чтения. В этом случае для хранения значения этого свойства для него неявно будет создаваться поле с модификатором readonly, поэтому следует учитывать, что подобные get-свойства можно установить либо из конструктора класса, как в примере выше, либо при инициализации свойства:

Class Person { public string Name { get;} = "Tom" }

Сокращенная запись свойств

Как и методы, мы можем сокращать свойства. Например:

Class Person { private string name; // эквивалентно public string Name { get { return name; } } public string Name => name; }

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

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

Хранение объектов в памяти. Доступ к свойствам из методов

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

Для устранения неоднозначности в этом вопросе было введено понятие "экземпляр объекта ". Каждая переменная объектного типа является экземпляром соответствующего типа. Для каждого экземпляра создается свой набор свойств, под которые выделяется память. Свойства одного экземпляра никак не связаны со свойствами другого экземпляра. Это и понятно: если в программе есть объект "зубчатое колесо", и мы должны моделировать зубчатую передачу, то свойства у ведущего и ведомого колес будут разными. А вот методы у всех экземпляров совершенно одинаковы. Их хранят в единственной копии для всех экземпляров объекта данного типа (Рис. 5.2).

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

Рис. 9.2. Хранение объектов в памяти.

Следующая трудность возникает при доступе к свойству объекта. Рассмотрим пример кода:

TYPE TA=CLASS
a:WORD;
PROCEDURE abc;

PROCEDURE TA.abc;
VAR a:BYTE;
BEGIN
a:=10
END;

Чему будет присваиваться значение 10 при выполнении метода abc – локальной переменной с именем а, описанной внутри этого метода, или же свойству объекта с тем же именем а? Если свойству объекта, то каким образом программа будет знать, о каком из экземпляров объекта идет речь? Метод abc – общий на все экземпляры, в каждом из экземпляров есть свойство а. Какое же из них менять?

Для устранения неопределенности используется ключевое слово SELF – указатель на текущий экземпляр объекта:

PROCEDURE TA.abc;
VAR a:BYTE;
BEGIN
Self.a:=10 { присваивание свойству }
a:=20 { присваивание локальной переменной }
END;

Self всегда знает, с каким экземпляром объекта идет работа. Откуда? Это очень просто – с каждым экземпляром объекта связано имя объектной переменной. Это же имя ставится перед вызываемым методом, например, x.abc. Имея такую информацию, компилятор будет знать, что внутри метода слово Self в данный момент заменяет собой имя переменной х.

Создаваемые программистом объекты могут включаться в библиотеки и использоваться не только им лично, но и многими другими разработчиками программ. Следовательно, нужно предусмотреть какую-то защиту от неверного использования свойств и методов. С другой стороны, как мы уже видели, прямое изменение значений свойств объекта – операция крайне нежелательная (изменили модуль зуба, а диаметр пересчитать забыли). Эти два соображения привели к появлению принципа инкапсуляции , гласящего: значения свойств не должны меняться напрямую, а только через вызовы соответствующих методов . Рассмотрим два примера программного кода:

В первом случае значение свойства а можно менять напрямую, просто написав с.а:=10. Это явное нарушение принципа инкапсуляции. Во втором случае для изменения значения свойства а введен специальный метод SetA. Внутри этого метода при необходимости будут пересчитываться значения других свойств, зависящих от значения свойства а. Кроме того, можно выполнять проверку корректности присеваемого свойству значения. Если речь идет о свойстве "число зубьев зубчатого колеса", то это число не может быть меньше шести (что, кстати, отражено в самом слове "шестеренка"), иначе не получится плавного зацепления.

Таким образом, инкапсуляция нужна по следующим соображениям:

1. Изменение значения одного из свойств часто должно приводить к изменению значения другого свойства.

2. Возможна проверка корректности присваиваемых свойству значений.

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

TYPE TA=CLASS
PRIVATE
a:REAL;
PUBLIC
b:REAL
END;

Перед списком скрытых свойств ставится слово PRIVATE, а перед списком открытых для изменения свойств – слово PUBLIC. В приведенном фрагменте свойство а является скрытым и попытка выполнить оператор вида с.а:=10 приведет к ошибке "Field identifier expected". Однако внутри методов все скрытые свойства видны по-прежнему. Рекомендуется делать скрытыми все свойства объекта

Но в действительности обширно встречается и в других (см. подтипизация на записях и полиморфизм записей и вариантов). В ООП инкапсуляция тесно связана с принципом абстракции данных (не путать с абстрактными типами данных , реализации которых предоставляют возможность инкапсуляции, но имеют иную природу). Это, в частности, приводит к другому распространённому заблуждению - рассмотрению инкапсуляции неотрывно от сокрытия . В частности, в сообществе С++ или 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#, Java, С++, Smalltalk, Visual Basic и т.п.) должны отвечать трем основным принципам объектно-ориентированного программирования (ООП), которые перечислены ниже:

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

Как данный язык скрывает детали внутренней реализации объектов и предохраняет целостность данных?

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

Как данный язык стимулирует многократное использование кода?

Полиморфизм

Как данный язык позволяет трактовать связанные объекты сходным образом?

Прежде чем погрузиться в синтаксические детали реализации каждого принципа, важно понять базовую роль каждого из них.

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

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

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

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

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

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

Код и данные, составляющие вместе класс, называют членами . Данные, определяемые классом, называют полями , или переменными экземпляра . А код, оперирующий данными, содержится в функциях-членах , самым типичным представителем которых является метод. В C# метод служит в качестве аналога подпрограммы. (К числу других функций-членов относятся свойства, события и конструкторы.) Таким образом, методы класса содержат код, воздействующий на поля, определяемые этим классом.

Роль наследования

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

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

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

Роль полиморфизма

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

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



В продолжение темы:
Windows

Часть вторая : "Важнейшие характеристики каждого семейства процессоров Intel Core i3/i5/i7. Какие из этих чипов представляют особый интерес" Введение Сначала мы приведём...

Новые статьи
/
Популярные