|
|
|
|
|
|
|
ООП С Sharp - Замечание для программистов на Visual Basic 6
Если вы — опытный разработчик на Visual Basic 6, но не имели дело с C++ или Java, многие концепции этой главы могут показаться вам “чуждыми”. Visual Basic позволяет кодировать нечто, часто называемое объектом — модуль класса Visual Basic. Некоторые источники даже относят это к ООП, хотя такие объекты лишь очень приблизительно можно отнести к оригинальным концепциям ООП. Точнее будет сказать, что Visual Basic реализует несколько наиболее основных средств ООП. По сути дела, модуль класса VB — это COM-компонент, но помещенный в оболочку, скрывающую большую часть того, что делает COM-компонент. В частности, он не поддерживает наследование методов так, как это делается в C# и в ООП вообще.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Что такое объект?
В повседневной жизни объектом называют нечто, идентифицирующее отдельный материальный элемент. Объектом может быть автомобиль, дом, книга, документ или счет к оплате. Теперь вам придется несколько расширить эту концепцию и думать об объекте, как о некой отдельной сущности, которую вы хотите представить в программе. Поэтому наша дискуссия будет включать живые “объекты”, такие как человек, сотрудник или заказчик, наряду с более абстрактными “объектами”, например, компанией, базой данных или страной.
Такое представление объектов не только позволит писать код, моделирующий реальный мир, оно также даст возможность разбивать крупные программы на небольшие, более управляемые узлы. В действительность идея происходит от концепции черного ящика, с которой вы, возможно, уже сталкивались ранее.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С# Классы
Члены класса
До сих пор вы видели, что существуют две стороны объекта: что он делает — это то, что общеизвестно, и как он работает — что обычно скрыто. В программировании “что он делает” обычно представлено в первую очередь методами, являющимися функциональными блоками, которые можно использовать. Метод — это просто термин C#, обозначающий функцию. “Как он работает” — представлено как методами, так и данными (переменными), которые хранит в себе объект. В Java или C++ эти данные называют переменными-членами, в то время как в Visual Basic эти данные должны быть представлены любыми переменными уровня модуля в конкретном модуле класса. В C# принят термин поля. В общем случае класс определяется своими полями и методами. Термин член (member) используется для обозначения всего, что является частью класса — будь то поле, метод или любой другой элемент, упомянутый выше, который может быть определен внутри класса.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Модификаторы доступа
Единственное поле класса Authenticator, password, хранит текущий пароль (изначально, при создании объекта Authenticator — пустую строку) и помечено ключевым словом private. Это значит, что оно не видимо вне класса, а видимо только коду, являющемуся частью Authenticator. Пометка поля или метода как private гарантирует, что это поле или метод будут частью внутреннего устройства класса, в противоположность внешнему интерфейсу. Преимущество этого подхода состоит в том, что если вы решите изменить внутреннее устройство (например, хранить password не в виде строки, а воспользоваться каким-то другим типом данных), то вы просто внесете необходимые изменения, не разрушая код, находящийся вне определения класса Authenticator, — ничто, находящееся вне класса, не может получить доступ к этому полю.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Экземпляры объектов
Проще всего понять, как использовать класс в коде, можно, представив его как новый тип переменных. Вы используете предопределенные типы переменных — такие как int, float, double и прочие. Объявляя класс Authenticator, вы тем самым сообщаете компилятору о существовании нового типа переменной, называемого Authenticator. Определение класса содержит все, что компилятору нужно знать, чтобы быть готовым к работе с переменными этого типа.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Создание статических полей
Важно понимать, что по умолчанию каждый экземпляр класса (каждый объект) имеет свой собственный набор полей, определенных в классе. Например, в следующем фрагменте каждый из экземпляров karli и julian содержит свою собственную строку с именем password:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Создание статических методов
Как объяснялось в примере Authenticator, по умолчанию метод, подобный ChangePassword(), вызывается с каким-то определенным экземпляром, указанным именем переменной перед операцией точки (.). Этот метод затем неявно имеет доступ ко всем членам (полям, методам и тому подобному) этого конкретного экземпляра.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Как методы экземпляра и статические методы реализованы в памяти
Как уже упоминалось, каждый объект имеет собственную копию полей экземпляра класса. Однако это не касается методов. Если бы каждый объект имел собственную копию кода метода, это потребовало бы значительного расхода памяти, потому что код методов остается одним и тем же для всех экземпляров объектов. Таким образом, методы экземпляра, как и статические методы, существуют лишь в одном экземпляре и ассоциируются с классом в целом. Позже вы ознакомитесь и с другими типами членов класса (конструкторами, свойствами и так далее), которые содержат код вместо данных и следующих той же логике.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Замечание по поводу ссылочных типов
Прежде чем завершить разговор о классах, нужно предупредить вас об одной потенциальной ловушке, которая может возникнуть в C#, поскольку C# рассматривает все классы как ссылочные типы. Это может иметь некоторые неожиданные эффекты, когда придется сравнивать экземпляры класса на предмет эквивалентности и устанавливать экземпляры классов эквивалентными друг другу.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Перегрузка методов
Перегрузка метода означает создание нескольких методов — каждый имеет то же самое имя, но отличается сигнатурой. Причину, по которой вам могут понадобиться перегруженные методы, лучше пояснить на примере. Рассмотрим, как с помощью C# выводятся данные на консоль с помощью метода Console.WriteLine().
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Когда использовать перегрузку
Обычно перегрузку метода стоит рассматривать, когда нужны несколько методов, принимающих различные параметры, но концептуально выполняющих одно и то же действие, как это делает Console.WriteLine() в предыдущем разделе. Ситуации, в которых обычно используется перегрузка, описаны в следующих подразделах.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С# Свойства
Как уже упоминалось, класс определяется его полями и методами. Однако классы также могут содержать и другие типы членов, включая конструкторы, индексаторы, свойства, делегаты и события. По большей части все эти прочие элементы используются только в наиболее сложных ситуациях и не являются существенными для понимания принципов объектно-ориентированного проектирования. По этой причине в данном приложении обсуждаются только свойства C#, которые используются чрезвычайно часто и могут существенно упростить внешний пользовательский интерфейс, представленный классом. Другие члены класса рассматриваются в первой части книги. Свойства используются очень часто и могут упростить внешний интерфейс класса, поэтому мы и обсудим их здесь.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Инкапсуляция данных
Вас может удивить, зачем нужен весь этот предыдущий код? Не проще ли объявить поле minPasswordLength как public, чтобы обращаться к нему безо всяких свойств? Ответ заключается в том, что поля представляют внутренние данные объекта, поэтому они — неотъемлемая часть его функциональности. Теперь, в ОПП, ваша цель — сделать так, чтобы пользователи объекта нуждались лишь в знании того, что делает объект, а не того, как он это делает. Поэтому открытие прямого доступа к полям нарушает идеологию ООП.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Введение в интерфейсы
Одна из характерных черт объектов в повседневной жизни связана с тем, что они образуют семейства взаимосвязанных сущностей, разделяющих общие аспекты ди-зайна. Диван подобен креслу с тем отличием, что на нем может сидеть более одного человека. Компакт-диск обладает примерно теми же свойствами, что и магнитная лента, но с дополнительной возможностью прямого доступа. Многие автомобили отличаются стилем и размером кузова, но внутри их двигатели и другие компоненты построены во многом аналогично и зачастую из одних и тех же компонентов.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Использование наследования в C#
Пример, которым мы воспользуемся для демонстрации наследования, описывает некую воображаемую компанию сотовой связи под названием Mortimer Phones. Мы разработаем класс, представляющий счет абонента и отвечающий за калькуляцию его телефонных счетов. Это будет более длинный и сложный пример, чем класс Authenticator, и в процессе его разработки вы вскоре обнаружите, что одного простого класса окажется недостаточно; вместо этого понадобится множество взаимосвязанных классов. Наследование поможет справиться с задачей.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Добавление наследования
Пока наш пример с Mortimer Phones предельно упрощен. В частности, он предусматривает только один тарифный план для всех заказчиков, что крайне далеко от реальности. Многие люди регистрируются под тарифным планом, согласно которому они платят фиксированную сумму каждый месяц, однако существует и много других планов.
На данный момент, чтобы попытаться принять во внимание все разнообразные тарифные планы, в наш метод RecordCall() придется включить несколько вложенных операторов switch, что будет выглядеть примерно так (предполагая, что поле CallPlan является перечислением).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Иерархии и дизайн классов
В процедурных языках и даже в некоторых более совершенных языках, таких как Visual Basic, значительный акцент делается на разбиении программы на функции. Объектная ориентация смещает этот акцент дизайна программы в сторону от функциональности программы к объектам, из которых она состоит.
Наследование является чрезвычайно важным средством объектно-ориентированного программирования, и ключевая стадия дизайна программ заключается в проектировании иерархий классов — то есть отношений между классами. Вообще, как и в случае с примером Mortimer Phones, вы обнаружите, что ваша программа в конечном итоге будет состоять из множества специализированных объектов, которые будут представлять собой конкретные типы более обобщенных объектов.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Полиморфизм и виртуальные члены
Вернемся к примеру с Mortimer Phones. Ранее мы уже видели такую строку кода:
Nevermore60Customer arabel = new Nevermore60Customer();
Фактически, можно создать экземпляр объекта Nevermore60Customer и так:
Customer arabel = new Nevermore60Customer();
Поскольку Nevermore60Customer унаследован от Customer, совершенно законно, если ссылка типа Customer будет установлена либо на Nevermore60Customer, либо на Customer, либо на экземпляр любого другого класса, непосредственно унаследованного от Customer. Обратите внимание, что все, что здесь меняется — это объявление ссылочной переменной.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Сокрытие методов
Даже если метод не объявлен как virtual в базовом классе, все равно можно представить другой метод с той же сигнатурой в классе-наследнике. Сигнатура метода — это набор всей информации, необходимой для описания его вызова: имя, количество параметров и их типы. Однако при этом новый метод не переопределяет одноименного метода базового класса. Вместо этого говорят, что он скрывает метод базового класса. Как упоминалось ранее, это означает, что компилятор всегда проверяет тип данных переменной, используемой для ссылки на экземпляр, когда принимает решение о том, какой именно метод должен быть вызван.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Абстрактные функции и классы
До сих пор всякий раз, когда мы определяли класс, то тут же создавали и экземпляры этого класса, но так бывает не всегда. Во многих ситуациях вам придется создавать очень обобщенные классы, от которых вы будете наследовать другие, более специализированные классы, никогда не создавая их экземпляров. В C# для этой цели предназначено ключевое слово abstract. Если класс объявлен как abstract, то создать его экземпляр невозможно.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Определение абстрактного класса
Мы займемся изменением дизайна Mortimer Phones не просто ради удовольствия. Дело в том, что у нашей существующей иерархии классов имеется недостаток. Класс Customer представляет абонентов, вносящих платежи по факту (pass-as-you-go) в качестве базового класса для всех прочих типов абонентов. То есть, мы трактуем этот тарифный план в качестве специального, от которого наследуются все прочие.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Запечатанные классы и методы
Во многих отношениях можно думать о классах и методах sealed (запечатанный), как о противоположности абстрактным классам и методам. В то время как объявление чего-либо абстрактным означает, что это что-то должно быть переопределено или унаследовано, sealed означает как раз наоборот — что наследование и переопределение запрещено. Не все объектно-ориентированные языки поддерживают эту концепцию, но иногда это может оказаться удобным. В синтаксисе C# это выглядит следующим образом:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С# Интерфейсы
Ранее в настоящем приложении вы узнали о существовании двух типов наследования — наследование реализации и наследование интерфейса. До сих пор мы рассматривали наследование реализации. данном разделе мы рассмотрим более внимательно наследование интерфейса.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С# Конструкторы / Деструкторы
В последнем разделе настоящего приложения мы отставим в сторону наследование и рассмотрим еще одну тему, которая важна для ООП: создание и освобождение объектов — или, используя обычную терминологию, конструирование (construction) и уничтожение (destruction) объектов. Предположим, имеется следующий код:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С Sharp - Написание конструкторов
Когда вы видите определение конструктора на C#, то оно может показаться очень похожим на определение методов, с тем отличием, что конструкторы обычно не вызываются явно. Они подобны методам, которые вызываются от вашего имени при каждом создании экземпляра класса. В дополнение, поскольку вы никогда не вызываете такой метод явно, нет способа получить доступ к возвращаемому значению, а потому конструкторы никогда ничего не возвращают.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ООП С# - Передача параметров конструкторам
Вернемся к классу Authenticator. Представим, что мы хотим модифицировать этот класс так, чтобы можно было указывать начальный пароль при первоначальном создании экземпляра класса. Это можно сделать с помощью конструктора, принимающего параметры. В этом конструктор ведет себя, как метод — в том смысле, что вы можете определить для него любые параметры, и это дает ему значительные преимущества по сравнению с функциями Visual Basic Initialize или Form_Load.
|
|
|
|
|
|
|
|
|