Чтобы сэкономить место, можно освободиться от этой глупой концепции — "микрон] новая печь". Для приготовления закуски хватит и внутренностей печи. Тогда в инструкции достаточно написать примерно следующее: "Поместите полуфабрикат в ящик. Соедини красный и черный провод. Установите на трубе излучателя напряжение в 3000 вольт. Дол появиться негромкий гул. Постарайтесь не стоять близко к установке, если хотите иметь ц тей". Простая и понятная инструкция!
Но такой функциональный подход создает некоторые проблемы.
Слишком сложно. Нежелательно, чтобы фрагменты микроволновой печи па мешивались с фрагментами закуски при разработке программы. Но поскольку ц данном подходе нельзя создавать объекты и упрощать написание, работая с и цым из них в отдельности, приходится держать в голове все нюансы каждого of екта одновременно.
Не гибко. Когда-нибудь потребуется замена имеющейся микроволновой печщ печь другого типа. Это делается без проблем, если интерфейс печи можно буд оставить старым. Без четко очерченных областей действия, а также без разделен интерфейса и внутреннего содержимого становится крайне трудно убрать стар объект и поставить на его место новый.
Невозможно использовать повторно. Печи предназначены для приготовлен! разных блюд. Вряд ли кому-то захочется создавать новую печь всякий раз при а обходимости приготовить новое блюдо. Если задача уже решена, неплохо исши зовать ее решение и в других программах.
Объект должен быть способен спроектировать внешний интерфейс максимально прс стым при полной достаточности для корректного функционирования. Если интерфа" устройства будет недостаточен, все кончится битьем кулаком или чем-то более тяжела по верхней панели такого устройства или просто разборкой для того, чтобы добраться» его внутренностей (что наверняка окажется нарушением законодательства об интелла туальной собственности). С другой стороны, если интерфейс слишком сложен, весы сомнительно, что кто-то купит такое устройство (как минимум, вряд ли кто-то будет HI пользовать все предоставляемые интерфейсом возможности).
Люди постоянно жалуются на сложность видеомагнитофонов (впрочем, с переходо на управление с помощью экрана количество жалоб несколько уменьшилось). В эк устройствах слишком много кнопок с различными функциями. Зачастую одна и та i кнопка выполняет разные функции — в зависимости от того, в каком именно состояш находится в этот момент видеомагнитофон. Кроме того, похоже, невозможно найти я видеомагнитофона различных марок с одинаковым интерфейсом.
Теперь рассмотрим ситуацию с автомобилями. Вряд ли можно сказать (и доказать что автомобиль проще видеомагнитофона. Однако, похоже, люди не испытывают тан трудностей с его вождением, как с управлением видеомагнитофоном.
В каждом автомобиле в наличии примерно одни и те же элементы управления и гаи ти на одних и тех же местах. Если же управление отличается... Ну вот вам реальная ж тория из моей жизни — у моей сестры был французский автомобиль, в котором упращ
228 |
Часть |
IV. |
Объектно-ориентированное |
программирован |
ние фарами оказалось там, где у всех "нормальных" автомобилей находится управление сигналами поворота. Отличие вроде бы небольшое, но я так и не научился поворачивать на этом автомобиле влево, не выключив при этом фары...
Кроме того, при хорошо продуманном дизайне автомобиля один и тот же элемент управления никогда не будет использоваться для выполнения более одной операции в за висимости от состояния автомобиля.
Микроволновая печь должна быть сконструирована таким образом, чтобы никакая комбинация кнопок или рукояток не могла ее сломать или навредить вам. Конечно, оп ределенные комбинации не будут выполнять никаких функций, но главное, чтобы ни од на из них не привела к следующему.
Поломке устройства. Какие бы рукоятки ни крутил ваш ребенок и какие бы кнопки не нажимал — микроволновая печь не должна от этого сломаться. После того как вы вернете все элементы управления в корректное состояние, она должна нормально работать.
К пожару или прочей порче имущества или нанесению вреда здоровью по требителя. Мы живем в сутяжном мире, и если бы что-то похожее могло про изойти — компании пришлось бы продать все вплоть до автомобиля ее президен та, чтобы рассчитаться с подающими на нее в суд и адвокатами.
Однако чтобы эти два правила выполнялись, вы должны принять на себя определен ную ответственность. Вы ни в коем случае не должны вносить изменения в устройство, в частности, отключать блокировки.
Почти все кухонное оборудование любой степени сложности, включая микроволно вые печи, имеет пломбы, препятствующие проникновению пользователя внутрь. Если такая пломба повреждена, это указывает, что крышка устройства была снята, и вся от ветственность с производителя тем самым снимается. Если вы каким-либо образом из менили внутреннее устройство печи, вы сами несете ответственность за все последую щие неприятности, которые могут произойти.
Аналогично, класс должен иметь возможность контролировать доступ к своим членамданным. Никакая последовательность вызовов членов класса не должна приводить программу к аварийному завершению, однако класс не в состоянии гарантировать это, если внешние объекты имеют доступ к внутреннему состоянию класса. Класс должен иметь возможность прятать критические члены-данные и делать их недоступными для внешнего мира.
Итак, как же С# реализует объектно-ориентированное программирование? Впрочем, это не совсем корректный вопрос. С# является объектно-ориентированным языком про-
Глава 10. Что такое объектно-ориентированное программирование |
229 |
граммирования, но не реализует его — это делает программист. Как и на любом другом гом языке, вы можете написать на С# программу, не являющуюся объект! ориентированной (например, вставив весь код Word в функцию M a i n O ) . Иногда нужно писать и такие программы, но все же главное предназначение С# — создает объектно-ориентированных программ.
С# предоставляет программисту следующие необходимые для написания объект! ориентированных программ возможности.
Управляемый доступ. С# управляет обращением к членам класса. Ключе» слова С# позволяют объявить некоторые члены открытыми для всех, а другие защищенными или закрытыми. Подробнее эти вопросы рассматриваются в гла ве 11, "Классы".
Специализация. С# поддерживает специализацию посредством механизма, i вестного как наследование классов. Один класс при этом наследует члены друга класса. Например, вы можете создать класс Саг, как частный случай класса Vi h i c l e . Подробнее эти вопросы рассматриваются в главе 12, "Наследование".
Полиморфизм. Эта возможность позволяет объекту выполнить операцию так, как это требуется для его корректного функционирования. Например, класс Rocket унаследованный от V e h i c l e , может реализовать операцию S t a r t совершенно иначе, чем Саг, унаследованный от того же V e h i c l e . По крайней мере, будем надеяться, что это справедливо хотя бы по отношению к вашему автомобилю хотя с некоторыми автомобилями никогда ни в чем нельзя быть уверенным...| просьг полиморфизма рассматриваются в главах 13, "Полиморфизм", 1 "Интерфейсы и структуры".
230 |
Часть IV. |
Объектно-ориентированное программировал |
Глава 11
Классы
У Защита класса посредством управления доступом
>Инициализация объекта с помощью конструктора
>Определение нескольких конструкторов в одном классе
>Конструирование статических членов и членов класса
ласе должен сам отвечать за свои действия. Так же как микроволновая печь не должна вспыхнуть, объятая пламенем, из-за неверного нажатия кнопки, так и класс не должен скончаться (или прикончить программу) при предоставлении не
корректных данных.
Чтобы нести ответственность за свои действия, класс должен убедиться в корректно сти своего начального состояния и в дальнейшем управлять им так, чтобы оно всегда ос тавалось корректным. С# предоставляет для этого все необходимое.
Простые классы определяют все свои члены как p u b l i c . Рассмотрим программу BankAccount, которая поддерживает член-данные b a l a n c e для хранения информа ции о балансе каждого счета. Сделав этот член p u b l i c , вы допускаете любого в святая святых банка, позволяя каждому самому указывать сумму на счету.
Неизвестно, в каком банке храните свои сбережения вы, но мой банк и близко не на столько открыт и всегда строго следит за моим счетом, самостоятельно регистрируя ка ждое снятие денег со счета и вклад на счет. В конце концов, это позволяет уберечься от всяких недоразумений, если вас вдруг подведет память.
Управление доступом дает возможность избежать больших и малых ошибок в работе банка. Обычно программисты, привыкшие к функциональному про граммированию, говорят, что достаточно лишь определить правило, согласно которому никакие другие классы не должны обращаться к члену b a l a n c e не посредственно. Увы, теоретически это, может быть, и так, но на практике такой подход никогда не работает. Да, программисты начинают работу, будучи пере полненными благими намерениями, которые вскоре непонятно куда исчезают под давлением сроков сдачи проекта...
Пример программы с использованием открытых членов
В приведенной демонстрационной программе класс BankAccount объяв ляет все методы как public, в то же время члены-данные nAccountNumber и dBalance сделаны private. Эта демонстрационная программам
корректна и не будет компилироваться, так как создана исключительно в дидактических целях.
// |
BankAccount - создание банковского счета с использованием |
// |
переменной типа double для хранения баланса |
счета (она |
// |
объявлена как |
p r i v a t e , чтобы скрыть баланс |
от |
внешнего |
// |
мира) |
|
|
|
|
// |
П р и м е ч а н и е : пока в программу не будут внесены |
|
// |
исправления, |
она не будет |
компилироваться, |
так |
как |
// |
функция Main() обращается |
к private - член у класса |
// B a n k A c c o u n t . usin g System;
namespace BankAccount
{
public class Program
{
public static v o i d Main(string[] args)
{
Console . WriteLine("В текущем состоянии эта " + "программа не к о м п и л и р у е т с я . " ) ;
//Открытие банковского счета
Console . WriteLine("Создание объекта " +
|
"банковского |
с ч е т а " ) ; |
BankAccount ba = n e w B a n k A c c o u n t ( ) ; |
b a . I n i t B a n k A c c o u n t ( ) ; |
|
// |
Обращение к балансу при помощи метода Deposit() |
// |
вполне к о р р е к т н о ; Deposit() |
имеет право доступа ко |
//всем членам - данным b a . D e p o s i t ( 1 0 ) ;
//Непосредственное обращение к члену - данны м вызывает
//ошибку компиляции
Console . WriteLine("Здесь вы получите " + "ошибку к о м п и л я ц и и " ) ;
ba . dBalance += 1 0 ;
// Ожидаем подтверждения пользователя Console . WriteLine("Нажмите <Enter> для " +
"завершения программы .. . ") ;
C o n s o l e . R e a d ( ) ;
} }
//BankAccount - определение класса, представляющего
//простейший банковский счет
public class BankAccount
232 |
Часть |
IV. |
Объектно-ориентированное |
программирование |