Smalltalk в примерах/Методы
Методы содержат код, который выполняется в ответ на присланное сообщение. В отличие от C или C++, в которых могут быть функции не возвращающие значения, в Smalltalk все методы возвращают значение. Если нет явного возвращения переменной, метод должен возвратить self, объекту вызвавшему метод.
Типы методов
[править]Есть два основных типа методов: одни которые возвращают объект отличный от self, и другие которые производят некоторые эффекты. Пример метода первого типа это asSortedCollection, который преобразует совокупность в отсортированную совокупность и возвращает её, и printString, которая возвращает объект который представляет в форме строки объект получивший сообщение printString. Примерами второго типа методов является метод печати на Транскрипт (Transcript show: 'Здравствуйте'), и обновление файла с данными.
В целом, лучше всего пытаться и делать так, чтобы ваши методы принадлежали только к одному типу. Пытайтесь избегать методов которые одновременно что-либо делают и возвращают значения. Иногда это невозможно, но в гораздо меньшем числе случаев, чем вы ожидаете. В отличие от языков, в которых проверяется тип возвращаемого значения, Smalltalk предоставляет механизм обработки исключений, который позволяет вам обходить проверку обработки ошибок.
Длина метода
[править]В библиотеке классов VisualWorks, средняя длина метода равна 7 строкам. Короткий метод хорош тем, что позволяет программисту легко понять, что делает данный метод. Короткий метод также проще использовать заново, и проще переопределить классы-потомки для того, чтобы изменить их поведение. Поэтому хорошая идея определить браузер "стандартного" размера и руководствоваться тем, что все методы должны помещаться в окно браузера. Если метод большой, обычно есть возможность разбить его на меньшие методы с помощью разделения на концепции, которые вы реализовываете, и помещая каждую концепцию в свой метод. Например, длинный метод может разделяться на короткие методы, как в примере:
МойКласс>>мойМетод сам делайПервуюВещь. (некотороеВыражение) истина: [сам делайВторуюВещь] ложь: [сам делайТретьюВещь. сам делайЧетвёртуюВещь. ]
Названия методов
[править]Из-за того, что методы, которые помещаются на экране, легче для понимания, не имеет смысла уменьшать их ясность, используя непонятные имён. Используйте имена для методов и переменных, которые точны и подробны настолько, насколько это возможно. Не сокращайте слова, если только это не стандартные аббревиатуры, понятные всем, кто работает в проекте. Так как вы не часто определяете имена в Smalltalk, не повредит иметь длинные имена, которые подробны и понятны.
Имена методов должны говорить о том, что делает метод. Не экономьте буквы; более важна ясность имени, чем его длина. Программист должен быть способен, посмотрев на метод и прочитав его имя, немедленно сказать, что он делает. Однако, не всегда просто подобрать хорошее имя, когда вы просто смотрите на метод. Оно может прийти на ум, когда вы вызываете метод, посылая ему сообщение. Так что, даже если имя кажется подходящим, пока вы описываете метод, будьте готовы вернуться и исправить имя после использования его в других методах.
Глупые методы
[править]Если метод короткий, хорошо назван и у него осмысленные имена переменных, он должен быть довольно понятным. Если комментарий класса хорошо описывает общее поведение объекта и переменные экземпляра, тогда хороший метод не нуждается в дополнительном комментарии. Мне действительно нравится понятие "глупых методов". (Я бы хотел бы выразить признательность создателю этого термина, но, к сожалению, я не могу вспомнить, где я прочитал об этой идеи.)
Понятие "глупый метод" означает, что он не нуждается в документации. Проблема с документацией заключается в том, что она делает метод длиннее, и если ваш метод настолько большой, что нуждается в серьёзной документации, вы усложняете проблему тем, что делаете метод еще больше и он может уже не поместиться на экране. Если метод не помещается на экран, то для его понимания программисту нужно больше времени. Так что наилучшим для метода будет возможность назвать его "действительно глупым".
Форматирование метода
[править]Форматировщик проделывает работу по форматированию методов вместо вас. Я придерживаюсь правила игнорировать форматирование, пока я описываю новый метод, и только после этого запускать форматировщик. Если мне не нравится то, что сделал форматировщик, я переформатирую код вручную. Есть несколько примеров, как я могу переформатировать код, когда я не удовлетворен результатами форматирования. Для сохранения места я не показываю исходное форматирование; чтобы его увидеть, вам придется набрать код и запустить форматировщик.
мойМетод: аПараметр аПараметр делайЭто; делайТо; делайЧтоНибудьЕщё
мойМетод: аПараметр сам ---
---
Некоторые находят формат форматировщика слишком сложным для чтения, особенно в случае сложного, вложенного кода. Некоторые ставят закрывающую квадратную скобку на отдельной строке. Все это понижает вероятность того, что метод будет помещаться на экране, что является одним из моих основных критериев хорошего метода. Чаще всего ответ на вопрос, почему метод сложен для чтения, кроется в том, что он или слишком длинный, или слишком запутанный, или и то и другое вместе. Поэтому вы можете попытаться улучшить ваш код, так, чтобы он стал более элегантным и понятным, при помощи запуска форматировщика
Чтобы улучшить понятность, зачастую не нужно скупиться на круглые скобки. Программист, просматривающий код и использующий правила приоритета, всегда может определить порядок вызова методов, но вы можете сократить этот процесс, если сгруппируете некоторые вещи с помощью скобок. Так как форматировщик убирает излишние скобки, но оставляет нужные, вы также можете использовать его для определения мест, где скобки необходимы. Например, первое выражение ниже вызывает исключение, второе показывает то, что вы подразумевали в этом выражении, а третье выражение получается из второго после обработки форматировщиком. Если ваш случай более сложный, возможно, стоит оставить излишние скобки.
a<b | c<d (a<b) | (c<d) a<b | (c<d)
Открытые и закрытые методы
[править]В C++ есть понятие открытых и закрытых функций. Открытые функции это те которые может вызывать каждый объект. Закрытые функции это те, которые может вызывать только экземпляр класса который их определил, или его подкласс (на самом деле защищённые функции в последнем случае).
В Smalltalk нет такой концепции. Все методы доступны для всех объектов --- они только должны посылать правильные сообщения. Однако, понятие открытых и закрытых методов полезно; вы на самом деле не хотите чтобы другие объекты посылали сообщения которые существуют только как вспомогательные. Допустим вы решили изменить способ выполнения чего-нибудь. Вы не должны предохранять всё с помощью закрытого интерфейса. Вы должны суметь скрыть поведение так, чтобы другие объекты могли использовать открытый интерфейс и вы могли свободно менять детали реализации.
Из-за того что в Smalltalk'е нет закрытых методов, любые различия между закрытыми и открытыми методами производятся с помощью использования соглашений. Одним из таких соглашений является использование слова \verb|закрытые| в имени протокола для закрытых методов. Вы можете увидеть имя протокола \verb|закрытые| или \verb|доступ-закрыт|. Неудобство этой схемы в том, что Вы не всегда обращаете внимание на имя протокола. Если Вы просматриваете методы используя отправителей, implementors и сообщения, Вы не различаете протокол, поэтому легко можно использовать особый протокол.
Техника которая мне нравится описана Bob Brodd в The Smallatlk Report, Nov-Dec, 1994. Все закрытые методы имеют приставку мой. Теперь мы используем смысл Русского языка для предотвращения использования закрытых методов. Обычно что-нибудь вроде \verb|аОбъект| \verb|мойДелайЧтоНибудь| выглядит странно, принимая во внимание что выглядит разумным сказать \verb|сам| \verb|мойДелайЧтоНибудь|. Если Вы где-нибудь видите сообщение \verb|мой| посылаемое чему-нибудь отличному от \verb|сам|, опасайтесь --- Вы возможно видите использование закрытого метода как будто он открытый. Здесь приведён пример использования приставки \verb|мой| для закрытого метода.
СоздательОбраза>>создатьОбраз я мой---
Если ты используеш технику \verb|мой|, есть два основных подхода которых nы можэш придержываться. Один делать всё открытым пока ты знаеш что эти методы не должны вызываться отдельно. Другой сначала сделать все методы закрытыми, затем делать их открытыми когда ты найдёш сообщение которое должно посылаться другим объектом.
\potom
\potom
\potom
Возвращаемое методом значение
[править]В Smalltalk'е, все методы возвращают значение. Нет такова понятия как void функцыя в C++. По умолчанию все методы возарыщают объект которому было послано сообщение. Таким образом, если нет явного возвращаемого значения, в действительности последняя строка метода выглядит подобно этой:
^сам
\verb|^| это символ возвращаемого значения и \emph{сам} ссылается на получателя сообщения --- побъект выполняющий метод. Большынство методов неявно возвращают \emph{сам}. В VisualWorks Browser, если вы нажмёте кнопку \emph{шыфт} пока метод выделен мышю вы можэте увидеть дэкомпилированный код, который действительно заканчивается \verb|^сам|. Возвращение (\verb|^|) выполняется после того как все другие посланные сообщения и присваивания выполнятся.
Возвращение объекта в соответствии с именем метода
[править]Часто имя метода сообщает тебе тип значения которое будет возвращено. Например, имя метода которое заканчивается \verb|?| обычно возвращает \emph{Булево} значение, \emph{истина} или \emph{лож}. Например, \verb|пустой?|, \verb|ноль?|. Сообщения начинающиеся с \verb|как| обычно возвращают указанную вещ. Например сообщение \verb|какСортированнаяСовокупность| посланное совокупности должно возвратить СортированнуюСовокупность. Сообщение \verb|какЧисло| посланное строке должно возвратить число соответствующего типа. (Однако, сообщение \verb|какСтрока| не опредено для чисел поэтому ты должэн использовать вместо нево сообщение \verb|строкаДляПечати| или \verb|показатьСтроку|.)
Методы которые добавляют объект в совокупность, такие как \verb|добавить:| и \verb|от:поместить:|, возвращают объект который добавлен. Методы которые удаляют один объект из совокупности, такие как \verb|удалить:| и \verb|удалитьПервый|, возвращают объект который был удалён. Из за этого стоит знать о сообщении \verb|тысам|. Следующий пример показывает как использовать сообщение \verb|тысам| \potom.
совокупность := Список новый добавить: этотОбъект; добавить: тотОбъект; тысам.
Последовательность и предсказуемость большое достоинство, поэтому при добавлении метода данного типа в твой собственный класс, следуй общеупотребительным шаблонам. Иначе ты запутаеш всех программистов которые будут читать или использовать твой код.
Возвращение объекта определённого последним выражэнием
[править]Если объект возвращает что-либо отличное от сам, последняя строка или последнее выражэние в методе должно определить что возвращать. Предполагается что когда вы видите явное возвращение в концэ метода это означает что получатель должэн позаботиться о том что делать с возвращённым значением. По этой причине, не используй \verb|^|сам в концэ метода пока это явно не подразумевается получателем.
Защитное условие
[править]Если ты хочиш выполнить тело метода при выполнении некоторого условия, очевидный способ сделать это:
МойКласс>>некоторыйМетод (некоторое условие) истина: [ сам делатьОдно. сам делатьДругое. сам выполнятьДригиеСтроки. ]
Однако, часто лучше возвратить \verb|сам| из блока \verb|лож:|, затем выполнить основной код. Таким образом мы получаем защитное условие охраняющие вход в метод.
МойКласс>>некоторыйМетод (некоторое условие) лож: [^сам]. сам делатьОдно. сам делатьДругое. сам выполнятьДригиеСтроки.
Это делает код немного проще для понимания потому что не разветвлённый код лучше условного. Это подход противоречит структурному подходу одной точки входа, выхода. Это правило было создано для решения проблемы понятности длинных функций с многими точками входа выхода. Однако, методы Смолтока обычно короткие, поэтому просто увидеть что происходит.
Постоянство возвращаемого объекта
[править]Если твой метод неявно возвращает \verb|сам| в конце, используй \verb|^сам| если ты возвращаеш значение раньше. Не возвращай другие значения, такие как \verb|ноль|, исключая случай когда \verb|ноль| имеет особый смысл для получателя.
Потеряные методы
[править]Если метод не ссылается на переменные экземпляра, а просто оперирует с параметрами, ты можеш спросить определён ли метод в правильном месте. Если метод манипулирует параметром, возможно код должен быть определён в классе параметра. Например, при работе со строками, ты можеш решить что тебе надо удалить начальные и конечные пробелы. Поэтому ты пишиш новый метод для твоего класса который удаляет пробелы.
МойКласс>>удалитьПробелыИз: аСтрока ... удаление начальных и конечных пробелов ... ^ новаяСтрокаБезПробелов
Однако, это очень процедурный способ написания кода. Если ты думаеш в терминах объектов, ты расчитываеш как распределяется ответственность, и просиш объекты делать вещи. Пока \emph{МойКласс} действительно не отвечает за удаление пробелов из строки, чьей должна быть эта ответственность? Наиболее очевидное место для этого класс \emph{Строка} (или один из её суперклассов). Затем ты можеш сказать строке убрать пробелы из себя.
урезаннаяСтрока := строка убратьПробелы.
Количество методов
[править]Правила о том сколько методов должно быть в объекте нет, но если ты находиш что у объекта много больше методов чем у других, возможно ты неудачно распределил объём работ. В хорошей объектно-ориентированной системе, у тебя нет очень умных объектов. Взамен ты имееш равнозначные взаимодействующие объекты. За более подробной информацией обращайся к Главе \potom, Объектно-ориентированное мышление.
Параметры по умолчанию
[править]В C++, ты можешь не задавать некоторые параметры при вызове функции и они будут иметь значение по умолчанию (предполагается что функция использует параметры по умолчанию). В Смолтоке нет такова свойства. Взамен, ты часто можешь видеть методы которые ничего больше не делают кроме вызова других методов с дополнительными параметрами.
Хороший пример такого метода это \verb|changed|. Когда ты вызываешь механизм зависимости, ты используешь \verb|changed| чтобы определить что что-то изменилось. Ты используеш \verb|changed:| чтобы определить что изменилось, и \verb|changed:with:| чтобы определить новое значение.
\potom
Шаблон метода
[править]Шаблон метода (который ты видиш когда просматриваеш методы но не выделил ни одного метода) определён в \verb|Behavior>>sourceCodeTemplate|. Если он тебе не нравится, ты можэш изменить ево. Одним из примеров как это сделать рассмотрен в Главе \potom, Настнойка окружэния.