Объектно-ориентированный подход. Объектно_ориентированный_подход. Объектно ориентированный подход Мэтт Вайсфельд 5е международное издание ббк 32. 973. 2018
Скачать 5.43 Mb.
|
Использование множественных конструкторов Во многих случаях объект можно будет сконструировать несколькими метода- ми. Чтобы приспособиться к таким ситуациям, вам потребуется предусмотреть более одного конструктора. К примеру, взглянем на представленный здесь класс Count : public class Count { int count; public Count(){ count = 0; } } С одной стороны, мы хотим инициализировать атрибут count для отсчета до нуля — мы можем легко сделать это, использовав конструктор для инициали- зации count значением 0 , как показано далее: public Count(){ count = 0; } С другой стороны, нам может потребоваться передать параметр инициализации, который позволит задавать для count различные числовые значения: 75 Конструкторы. . public Count (int number){ count = number; } Это называется перегрузкой метода (перегрузка имеет отношение ко всем ме- тодам, а не только к конструкторам). В большинстве объектно-ориентированных языков предусматривается функциональность для перегрузки методов. Перегрузка методов Перегрузка позволяет программистам снова и снова использовать один и тот же метод, если его подпись каждый раз отличается. Подпись состоит из имени метода и списка параметров (рис. 3.1). Рис. 3.1. Компоненты.подписи Таким образом, все приведенные далее методы имеют разные подписи: public void getCab(); // другой список параметров public void getCab (String cabbieName); // другой список параметров public void getCab (int numberOfPassengers); ПОДПИСИ _____________________________________________________________________________ Подпись,.в.зависимости.от.языка.программирования,.может.включать.или.не.вклю- чать.возвращаемый.тип..В.Java.и.C#.возвращаемый.тип.не.является.частью.подпи- си..Например,.приведенные.далее.два.метода.будут.конфликтовать,.несмотря.на.то. что.возвращаемые.типы.различаются: public void getCab (String cabbieName); public int getCab (String cabbieName); Наилучший.способ.понять.подписи.заключается.в.том,.чтобы.написать.код.и.прогнать. его.через.компилятор. Глава.3..Прочие.объектно-ориентированные.концепции 76 Используя различающиеся подписи, вы можете по-разному конструировать объекты в зависимости от применяемого конструктора. Такая функциональность очень полезна в ситуациях, в которых вы не всегда заранее знаете, сколько дан- ных у вас будет в наличии. Например, при наполнении корзины в интернет- магазине может оказаться так, что клиенты уже вошли под своими учетными записями (и у вас будет вся их информация). С другой стороны, совершенно новый клиент может класть товары в корзину вообще без доступной информа- ции об учетной записи. В каждом из этих случаев конструктор будет по-разному осуществлять инициализацию. Использование UML для моделирования классов Вернемся к примеру с DataBaseReader , который мы использовали в главе 2. Представим, что у нас есть два способа сконструировать DataBaseReader Передать имя базы данных и позицию курсора в начале базы данных. Передать имя базы данных и позицию в базе данных, где, как мы хотим, должен установиться курсор. На рис. 3.2 приведена диаграмма класса DataBaseReader . Обратите внимание на то, что эта диаграмма включает только два конструктора для класса. Хотя на ней показаны два конструктора, без списка параметров нельзя понять, какой конструктор каким именно является. Чтобы провести различие между этими конструкторами, вы можете взглянуть на соответствующий код в классе DataBaseReader , приведенный далее. Рис. 3.2. Диаграмма.класса.DataBaseReader 77 Конструкторы. . ОТСУТСТВИЕ ВОЗВРАЩАЕМОГО ТИПА ________________________________________________ Обратите.внимание,.что.на.диаграмме.класса.на.рис..3.2.у.конструкторов.нет.воз- вращаемых.типов..У.всех.прочих.методов,.кроме.конструкторов,.должны.быть.воз- вращаемые.типы. Рис. 3.3. Создание.нового.объекта Вот фрагмент кода класса, который показывает его конструкторы, а также атри- буты, инициализируемые конструкторами (рис. 3.3). public class DataBaseReader { String dbName; int startPosition; // инициализировать только name public DataBaseReader (String name){ dbName = name; startPosition = 0; }; // инициализировать name и pos Глава.3..Прочие.объектно-ориентированные.концепции 78 public DataBaseReader (String name, int pos){ dbName = name; startPosition = pos; }; . . // остальная часть класса } Обратите внимание, что инициализация startPosition осуществляется в обоих случаях. Если не передать конструктору данные в виде списка параметров, то он будет инициализирован таким значением по умолчанию, как 0 Как сконструирован суперкласс? При использовании наследования вы должны знать, как сконструирован соот- ветствующий родительский класс. Помните, что когда оно задействуется, от родительского класса наследуется все. Таким образом, потребуется очень хоро- шо знать все данные и поведения родительского класса. Наследование атрибу- тов довольно очевидно. Однако то, как наследуются конструкторы, не так оче- видно. После обнаружения ключевого слова new и выделения памяти для объекта предпринимаются следующие шаги (рис. 3.4). Рис. 3.4. Конструирование.объекта 1. Внутри конструктора происходит вызов конструктора суперкласса соот- ветствующего класса. Если явного вызова конструктора суперкласса нет, то автоматически вызывается конструктор по умолчанию. При этом вы сможе- те увидеть соответствующий код, взглянув на байт-коды. 2. Инициализируется каждый атрибут класса объекта. Эти атрибуты являются частью определения класса (переменные экземпляра), а не атрибутами вну- 79 Обработка.ошибок. . три конструктора или любого другого метода (локальные переменные). В коде DataBaseReader , показанном ранее, целочисленная переменная startPosition является переменной экземпляра класса. 3. Выполняется остальная часть кода внутри конструктора. Проектирование конструкторов Как вы уже видели ранее, при проектировании класса желательна инициализа- ция всех атрибутов. В отдельных языках программирования компилятор обе- спечивает некоторую инициализацию. Но, как и всегда, не следует рассчитывать на компилятор в плане инициализации атрибутов! При использовании Java вы не сможете задействовать тот или иной атрибут до тех пор, пока он не будет инициализирован. Если атрибут впервые задается в коде, то позаботьтесь о том, чтобы инициализировать его с каким-нибудь допустимым условием — напри- мер, определить для целочисленной переменной значение 0 Конструкторы используются для обеспечения того, что приложения будут пре- бывать в стабильном состоянии (мне нравится называть его «надежным» со- стоянием). Например, инициализируя атрибут значением 0 , можно получить нестабильное приложение, если этот атрибут предназначается для использова- ния в качестве делителя в операции деления. Вы должны учитывать, что деление на ноль — недопустимая операция. Инициализация значением 0 не всегда ока- зывается наилучшим вариантом. При проектировании правильная методика заключается в том, чтобы определить стабильное состояние для всех атрибутов, а затем инициализировать их с этим стабильным состоянием в конструкторе. Обработка ошибок Крайне редко бывает так, что тот или иной класс оказывается идеально напи- санным с первого раза. В большинстве, если не во всех ситуациях будут ошиб- ки. Любой разработчик, не имеющий плана действий на случай возникновения проблем, рискует. Если ваш код способен выявлять и перехватывать ошибочные условия, то вы можете обрабатывать ошибки несколькими путями: в книге «Учебник по Java для начинающих» (Java Primer Plus) Пол Тима (Paul Tyma), Габриэл Торок (Gabriel Torok) и Трой Даунинг (Troy Downing) утверждают, что существует три основных подхода к проблемам, выявляемым в программах: устранить про- блемы, игнорировать проблемы, отбросив их, или выйти из среды выполнения неким корректным образом. В книге «Объектно-ориентированное проектиро- вание на Java» (Object-Oriented Design in Java) Гилберт и Маккарти более под- Глава.3..Прочие.объектно-ориентированные.концепции 80 робно останавливаются на этой теме, добавляя такой вариант, как возможность выбрасывать исключения. Игнорирование проблем (плохая идея!). Проверка на предмет проблем и прерывание выполнения программы при их обнаружении. Проверка на предмет потенциальных проблем, перехват ошибок и попытка решить обнаруженные проблемы. Выбрасывание исключений (оно зачастую оказывается предпочтительным способом урегулирования соответствующих ситуаций). Эти стратегии рассматриваются в приведенных далее разделах. Игнорирование проблем Если просто игнорировать потенциальные проблемы, то это будет залогом про- вала. Кроме того, если вы собираетесь игнорировать проблемы, то зачем вообще тратить силы на их выявление? Ясно, что вам не следует игнорировать любые из известных проблем. Основная задача для всех приложений заключается в том, что они никогда не должны завершаться аварийно. Если вы не станете обраба- тывать возникшие у вас ошибки, то работа приложения в конечном счете за- вершится некорректно либо продолжится в режиме, который можно будет считать нестабильным. Во втором случае вы, возможно, даже не будете знать, что получаете неверные результаты, а это может оказаться намного хуже ава- рийного завершения программы. Проверка на предмет проблем и прерывание выполнения приложения Если вы выберете проверку на предмет проблем и прерывание выполнения приложения при их выявлении, то приложение сможет вывести сообщение о наличии неполадок. При этом работа приложения завершится корректно, а пользователю останется смотреть в монитор компьютера, качать головой и за- даваться вопросом о том, что произошло. Хотя это намного лучший вариант, чем игнорирование проблем, он никоим образом не является оптимальным. Однако он позволяет системе навести порядок и привести себя в более стабиль- ное состояние, например закрыть файлы и форсировать перезагрузку системы. Проверка на предмет проблем и попытка устранить неполадки Проверка на предмет потенциальных проблем, перехват ошибок и попытка устранить неполадки гораздо лучше, чем просто проверка на предмет проблем 81 Обработка.ошибок. . и прерывание выполнения приложения в соответствующих ситуациях. В данном случае проблемы выявляются кодом, а приложение пытается «починить» себя. Это хорошо работает в определенных ситуациях. Взгляните, к примеру, на следующий код: if (a == 0) a=1; c = b/a; Ясно, что если не включить в код условный оператор, а нуль будет стоять после оператора деления, то вы получите системное исключение, поскольку делить на нуль нельзя. Если перехватить исключение и задать для переменной значе- ние 1 , то по крайней мере не произойдет фатального сбоя системы. Однако присвоение значения 1 необязательно поможет, поскольку результат может оказаться неверным. Оптимальное решение состоит в том, чтобы предложить пользователю заново ввести правильное входное значение. СМЕШЕНИЕ МЕТОДИК ОБРАБОТКИ ОШИБОК ________________________________________ Несмотря.на.тот.факт,.что.такая.обработка.ошибок.необязательно.будет.объектно- ориентированной.по.своей.природе,.я.считаю,.что.у.нее.есть.законное.место.в.объ- ектно-ориентированном.проектировании..Выбрасывание.исключений.(о.чем.пойдет. речь.в.следующем.разделе).может.оказаться.весьма.затратным.в.плане.«накладных. расходов»..Таким.образом,.даже.если.исключения.могут.быть.правильным.выбором. при.проектировании,.вам.все.равно.необходимо.принимать.во.внимание.другие. методики.обработки.ошибок.(хотя.бы.проверенные.структурные.методики).в.зави- симости.от.ваших.требований.в.области.проектирования.и.производительности. Хотя рассмотренные ранее методики выявления ошибок предпочтительнее бездействия, у них все же есть несколько недостатков. Не всегда легко опре- делить, где именно впервые возникла проблема. Кроме того, на выявление проблемы может потребоваться некоторое время. Подробное объяснение об- работки ошибок выходит за области интересов этой книги. Однако при про- ектировании важно предусматривать в классах обработку ошибок с самого начала, а операционная система зачастую сама может предупреждать вас о про- блемах, которые выявляет. Выбрасывание исключений В большинстве объектно-ориентированных языков программирования преду- сматривается такая функция, как исключения. В самом общем смысле под ис- ключениями понимаются неожиданные события, которые имеют место в систе- ме. Исключения дают возможность выявлять проблемы, а затем решать их. В Java, C#, C++, Objective-C и Visual Basic исключения обрабатываются при Глава.3..Прочие.объектно-ориентированные.концепции 82 использовании ключевых слов catch и throw . Это может показаться игрой в бейсбол, однако ключевая концепция в данном случае заключается в том, что пишется определенный блок кода для обработки определенного исключения. Такая методика позволяет выяснить, где проблема берет свое начало, и раскру- тить код до соответствующей точки. Вот структура для Java-блока try/catch : try { // возможный сбойный код } catch(Exception e) { // код для обработки исключения } При выбрасывании исключения в блоке try оно будет обработано блоком catch Если выбрасывание исключения произойдет, когда блок будет выполняться, то случится следующее. 1. Выполнение блока try завершится. 2. Предложения catch будут проверены с целью выяснить, надлежащий ли блок catch был включен для обработки проблемного исключения (на каждый блок try может приходиться более одного предложения catch ). 3. Если ни одно из предложений catch не обработает проблемное исключение, то оно будет передано следующему блоку try более высокого уровня (если исключение не будет перехвачено в коде, то система в конечном счете сама перехватит его, а результат будут непредсказуемым, то есть случится ава- рийное завершение приложения). 4. Если будет выявлено соответствующее предложение catch (обнаружено первое из соответствующих), то будут выполнены операторы в предложении catch 5. Выполнение возобновится с оператора, следующего за блоком try Достаточно сказать, что исключения — серьезное преимущество объектно-ори- ентированных языков программирования. Вот пример того, как исключение перехватывается при использовании Java: try { // возможный сбойный код count = 0; count = 5/count; } catch(ArithmeticException e) { // код для обработки исключения 83 Обработка.ошибок. . System.out.println(e.getMessage()); count = 1; } System.out.println("Исключение обработано."); СТЕПЕНЬ ДЕТАЛИЗАЦИИ ПРИ ПЕРЕХВАТЕ ИСКЛЮЧЕНИЙ ____________________________ Вы.можете.перехватывать.исключения.с.различной.степенью.детализации..Допу- скается.перехватывать.все.исключения.или.проводить.проверку.на.предмет.опре- деленных.исключений,.например.арифметических..Если.код.не.будет.перехватывать. исключения,.то.это.станет.делать.среда.выполнения.Java,.от.чего.она.не.будет.в.вос- торге! В этом примере деление на нуль (поскольку значением count является 0 ) в бло- ке try приведет к арифметическому исключению. Если исключение окажется сгенерировано (выброшено) вне блока try , то программа, скорее всего, завер- шится (аварийно). Однако поскольку исключение будет выброшено в блоке try , блок catch подвергнется проверке с целью выяснить, все ли запланировано на случай возникновения соответствующего исключения (в рассматриваемой нами ситуации оно является арифметическим). Поскольку блок catch включает про- верку на предмет арифметического исключения, код, содержащийся в этом блоке, выполнится и, таким образом, count будет присвоено значение 0 . После того как выполнится блок catch , будет осуществлен выход из блока try/catch , а в консоли Java появится сообщение Исключение обработано . Логическая по- следовательность этого процесса проиллюстрирована на рис. 3.5. Рис. 3.5. Перехват.исключения Если вы не поместите ArithmeticException в блок catch , то программа, вероят- но, завершится аварийно. Вы сможете перехватывать все исключения благо- даря коду, который приведен далее: try { // возможный сбойный код } catch(Exception e) { // код для обработки исключения } Глава.3..Прочие.объектно-ориентированные.концепции 84 Параметр Exception в блоке catch используется для перехвата всех исключений, которые могут быть сгенерированы в блоке try ОШИБКОУСТОЙЧИВЫЙ КОД __________________________________________________________ Хорошая.идея.—.комбинировать.описанные.здесь.методики.для.того,.чтобы.сделать. программу.как.можно.более.ошибкоустойчивой.при.ее.применении.пользовате- лями. Важность области видимости Экземпляры множественных объектов могут создаваться на основе одного класса. Каждый из этих объектов будет обладать уникальным идентификатором и состоянием. Это важно. Каждому объекту, конструируемому отдельно, вы- деляется его собственная отдельная память. Однако если некоторые атрибуты и методы объявлены соответствующим образом, они могут совместно исполь- зоваться всеми объектами, экземпляры которых созданы на основе одного и того же класса и, таким образом, при этом будет совместно использоваться память, выделенная для этих атрибутов и методов класса. СОВМЕСТНО ИСПОЛЬЗУЕМЫЙ МЕТОД _______________________________________________ Конструктор.—.это.хороший.пример.метода,.совместно.используемого.всеми. экземп.лярами.класса. Методы представляют поведения объекта, а его состояние представляют атри- буты. Существуют атрибуты трех типов: локальные; атрибуты объектов; атрибуты классов. Локальные атрибуты Локальные атрибуты принадлежат определенному методу. Взгляните на при- веденный далее код: public class Number { public method1() { int count; } |