ruby

Что такое объект, что такое класс


Объект в программировании — некоторая сущность в цифровом пространстве, обладающая определённым состоянием и поведением, имеющая определенные свойства (атрибуты) и операции над ними (методы). Как правило, при рассмотрении объектов выделяется то, что объекты принадлежат одному или нескольким классам, которые определяют поведение (являются моделью) объекта. Термины «экземпляр класса» и «объект» взаимозаменяемы. Объект, наряду с понятием класс, является важным понятием объектно-ориентированного подхода. Объекты обладают свойствами наследования, инкапсуляции и полиморфизма. Термин объект в программном обеспечении впервые был введен в языке Simula и применялся для моделирования реальности.

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

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

Объект считается экземпляром класса объекта ( обычно он называется просто классом). Класс можно представлять себе как чертеж, образец или модель, а объект как нечто, изготовленную по этому чертежу. Также класс часто называют абстрактным типом, то есть типом, более сложным, нежели целое или строка символов.

Создание объекта (экземпляра класса) называют инстаицированием (от instance). В некоторых языках имеются явные конструкторы и деструкторы - функции, выполняющие действия, необходимые соответственно для инициализации и уничтожения объекта. Отметим попутно, что в Ruby есть нечто, что можно назвать конструктором, но никакого аналога деструктора не существует (благодаря наличию механизма сборки мусора). Иногда возникает ситуация, когда некоторые данные имеют широкую область видимости, не ограниченную одним объектом, и помещать копию такого атрибута в каждый экземпляр класса неправильно. Рассмотрим, к примеру, класс MyDogs и три объекта этого класса: fido, rover и spot. У каждой собаки могут быть такие атрибуты, как возраст и дата вакцинации. Предположим, однако, что нужно сохранить еще и имя владельца всех собак. Можно, конечно, поместить его в каждый объект, но это пустая трата памяти, к тому же искажающая смысл дизайна. Ясно, что атрибут "имя владельца" принадлежит не отдельному объекту, а классу в целом. Такие атрибуты (синтаксис их определения в разных языках различен) называются атрибутами класса (или перемениыми класса).

Есть немало ситуаций, в которых может понадобиться переменная класса. Допустим, например, что нужно знать, сколько всего было создано объектов некоторого класса. Можно было бы завести переменную класса, инициализировать ее нулем и увеличивать на единицу при создании каждого объекта. Эта переменная ассоциирована именно с классом, а не с каким-то конкретным объектом. С точки зрения области видимости, она не отличается от любого другого атрибута, но существует лишь одна ее копия для всего множества объектов данного класса. Чтобы отличить атрибуты класса от обыкновенных атрибутов, последние просто называют атрибутами обьекта (или атрибутами экземпляра). Условимся, что под словом "атрибут" понимается атрибут экземпляра, если явно не оговорено, что это атрибут класса. Точно также методы объекта служат для управления доступом к его атрибутам и предоставляют четко определенный интерфейс для этой цели. Но иногда жела-тельно или даже необходимо определить метод, ассоциированный с самим классом. Не удивительно, что метод класса управляет доступом к переменным класса, а также выполняет действия, распространяющиеся на весь класс, а не на какой-то конкретный объект. Как и в случае атрибутов, мы будем считать, что метод принадлежит объекту, если явно не оговорено противное.

Стоит отметить, что в некотором смысле все методы являются методами класса. Не нужно думать, что, создав 100 объектов, мы породили 100 копий кода методов! Однако правила ограничения области видимости гласят, что метод каждого объекта оперирует данными только того объекта, от имени которого вызван. Тем самым у нас создается иллюзия, будто методы объекта ассоциированы с самими объектами.

Что такое наследование

Одна из самых сильных сторон ООП - наследование. Наследование - это механизм, позволяющий расширять ранее определенную сущность путем добавления новых возможностей. Короче говоря, наследование это способ повторного использования кода. (Простой и эффективный механизм повторного использования долго был "Святым Граалем" в информатике. Много десятилетий назад его поиски привели к изобретению параметризованных процедур (хранимая процедура, процедура имеющая значение (параметры), как и параметризированные команды) и библиотек. OOП это лишь одна из последних попыток реализации искомого). Обычно наследование рассматривается на уровне класса. Если нам необходим какой-то класс, а в наличии имеется более общий, то можно определить свой класс так, чтобы он наследовал поведение уже существующего класса. Предположим, например, что есть класс Polygon, описывающий выпуклые многоугольники. Тогда класс прямоугольника Rectangle можно унаследовать от Polygon. При этом Rectangle будет иметь все атрибуты и методы класса Polygon. Так, может уже быть написан метод, вычисляющий периметр путем суммирования длин всех сторон. Если все было реализовано правильно, то этот метод автоматически будет работать и для нового класса, переписывать код не придется.

Если класс В наследует классу А, мы говорим, что В является подклассом А, а А будет суперкласс В. По другому говорят, что А это базовый или родительский класс, а В производный или дочерний класс.

Как мы видели, производный класс может трактовать методы своего базового класса как свои собственные. С другой стороны, он может переопределить метод базового класса, предоставив иную его реализацию. Кроме того, в большинстве языков есть возможность вызвать из переопределенного метода метод базового класса с тем же именем. Иными словами, метод foo класса В знает, как вызвать метод foo класса А. (Любой язык, не предоставляющий такого механизма, можно заподозрить в отсутствии истинной объектной ориентированности.) То же вернои в отношении атрибутов.

Отношение между классом и его суперклассом интересно и важно, и обычно его называют отношением «является». Действительно, квадрат Square «является» прямоугольником Rectangle, а прямоугольник Rectangle «является» многоугольником Polygon и т.д. Поэтому, рассматривая иерархию наследования (а такие иерархии в том или ином виде присутствуют в любом объектно-ориентированном языке), мы видим, что в любой ее точке специализированные сущности «являются» подклассами более общих. Отметим, что это отношение транзитивно, - если обра-титься к предыдущему примеру, то квадрат «является» многоугольником. Однако отношение «является» не коммутативно - каждый прямоугольник есть многоугольник, но не каждый многоугольник - прямоугольник.

Это подводит нас к теме множественного наследования. Можно представить себе класс, который наследует нескольким классам. Например, классы Dog и Cat могут наследовать классу Mammal (млекопитающее), а Sparrow (воробей) и Raven (ворон) классу WingedCreature (крылатое). Но как быть с классом Bat (летучая мышь)? Он с равным успехом может наследовать и Мammal, и WingedCreature. Это хорошо согласуется с нашим жизненным опытом, ведь многие вещи можно отнести не к одной категории, а сразу к нескольким, не вложенным друг в друга.

Множественное наследование, вероятно, наиболее противоречивая часть ООП. Некоторые указывают на потенциальные неоднозначности, которые необходимо разрешать. Например, если в обоих классах Mammal и WingedCreature есть атрибут size (или метод eat), то какой из них имеется в виду, когда мы обращаемся к нему из объекта класса Bat? С этой трудностью тесно связана проблема ромбовидного наследования; она называются так из-за формы диаграммы наследования, возникающей, когда оба суперкласса наследуют одному классу. Представьте себе,что классы Мammal и WingedCreature наследуют общему предку Organiso, тогда иерархия наследования от Organisо к Bat будет иметь форму ромба. Но как быть с атрибутами, которые оба промежуточных класса наследуют от своего родителя? Получает ли Bat две копии? Или они должны быть объединены в один атрибут, поскольку все равно заимствованы у общего предка?

Это скорее проблемы проектировщика языка, а не программиста. В разных объектно-ориентированных языках они решаются по-разному. Иногда вводятся правила, согласно которым какое-то одно определение атрибута "выигрывает". Или предоставляется возможность различать одноименные атрибуты. Иногда даже язык позволяет вводить псевдонимы или переименовывать идентификаторы. Многими это рассматривается как аргумент против множественного наследования о механизмах разрешения подобных конфликтов имен нет единого мнения, поэтому все они языково-зависимы. В языке С++ предлагается минимальный набор средств для разрешения неоднозначностей, механизмы языка Eiffel, наверное, получше, а в Perl проблема решается совсем по-другому. Есть и альтернатива полностью запретить множественное наследование. Такой подход принят в языках Jаvа и Ruby. На первый взгляд, это даже не назовешь компромиссным решением.

Полиморфизм

Термин «полиморфизм», наверное, вызывает самые жаркие семантические споры. Каждый знает, что это такое, но все понимают его по-разному. Если вас спросят "что такое полиморфизм", рекомендую процитировать какого-нибудь признанного авторитета, например, Бертрана Мейера или Бьярна Страуструпа, если собеседник не согласится, то пусть он спорит с классиком, а не с вами.

Буквально слово «полиморфизм» означает «способность принимать разные формы или обличья. В самом широком смысле так называют ситуацию, когда различные объекты по-разному отвечают на одно и то же сообщение (или вызов метода).

Дамиан Конвей (Damian Conway) в книге «Object-Oriented Perl» проводит смысловое различие между двумя видами полиморфизм. Первый, наследственный полиморфиз, это то что, имеют в виду большинство программистов, говорящих о полиморфизме. Если некоторый класс наследует своему суперклассу, то по определению все методы суперкласса присутствуют также и в подклассе. Таким образом, цепочка наследования представляет собой линейную иерархию классов, отвечающих на одни и те же методы. Нужно, конечно, помнить, что в любом подклассе метод может быть переопределен, именно это и составляет сильную сторону наследования. При вызове метода объекта обычно отвечает либо метод, унаследованный от суперкласса, либо более специализированный вариант этого метода, созданный в интересах именно данного подкласса. В языках со статической типизацией, например в С++, наследственный полиморфизм гарантирует совместимость типов вниз по цепочке наследования (но не в обратном направлении). Например, если В наследует А, то указатель на объект класса А может указывать также и на объект класса В, обратное же неверно. Совместимость типов - существенная черта ООП в подобных языках, можно даже сказать, что полиморфизм ей и исчерпывается. Но конечно же, полиморфизм можно реализовать и в отсутствии статической типизации (как в Ruby). Второй вид полиморфизма, упомянутый Конвеем, это интерфейсиый полиморфизм. Для него не требуется наличия отношения наследования между классами, нужно лишь, чтобы в интерфейсах объектов были методы с одним и тем же именем. Такие объекты можно трактовать как принадлежащие одному виду, и потому мы имеем некую разновидность полиморфизма (хотя в большинстве работ он так не называется).

Читатели, знакомые с языком jаvа, понимают, что в нем реализованы оба вида полиморфизма. Класс в Java может расширять другой класс, наследуя ему с помощью ключевого слова extends, а может с помощью ключевого слова implements реализовывать интерфейс, за счет чего приобретает заранее известный набор методов (которые необходимо переопределить). Такой синтаксис позволяет интерпретатору Java во время компиляции определить, можно ли вызывать данный метод для конкретного объекта.

Ruby поддерживает интерфейсный полиморфизм, но по-другому. Он позволяет определять модули, методы которых можно подмешивать к существующим классам. Но обычно модули так не используются. Модуль состоит из методов и констант, которые можно использовать так, будто они являются частью класса или объекта. Когда модуль подмешивается с помощью предложения include, мы получаем ограниченную форму множественного наследования. (По словам проектировщика языка Юкихиро Мацумото, это можно рассматривать как одиночное наследование с разделением реализации.) Таким образом удается сохранить преимущества множественного наследования, не страдая от его недостатков.

Абстрактный класс

В языках, подобных С++, существует понятие абстрактного класса. Такому классу разрешается наследовать, но создать его экземпляр невозможно. В более динамичном языке Ruby такого понятия нет, но если программист пожелает, то может смоделировать его, потребовав, чтобы все методы были переопределены в производных классах.

Создатель языка С++ Бьярн Страуструп определяет также понятие конкретного типа. Это класс, существующий только для удобства. Он спроектирован не для наследования более того, ожидается, что ему никто никогда наследовать не будет. Другими словами, преимущества ООП в этом случае сводятся только к инкапсуляции. Ruby не поддерживает такой конструкции синтаксически (как и С++), но по природе своей прекрасно приспособлен для создания подобных классов. Считается, что некоторые языки поддерживают более «чистую» модель ООП, чем другие. (Мы также применяем термин «радикалыю обьектно-орuентuрованный».) Это означает, что любая сущность в языке является объектом, даже примитивные типы представлены полноценными классами, а переменные и константы рассматриваются как экземпляры. В таких языках, как Jаvа, С++ и Eiffel, дело обстоит иначе. В них примитивные типы (особенно константы) не являются настоящими объектами, хотя иногда могут рассматриваться как таковые с помощью классов-оберток. Вероятно, есть языки, которые более радикально объектно ориентированы, чем Ruby, но их немного. Большинство объектно-ориентированных языков статичны; методы и атрибуты, принадлежащие классу, глобальные переменные и иерархия наследования определяются во время компиляции. Быть может, самый сложный концептуальный переход заключается в том, что в Ruby все это происходит дuиамuчески. И определения, и даже порядок наследования можно задавать во время испол-нения. Честно говоря, каждое объявление или определение исполняется во время работы программы. Помимо прочих достоинств, это позволяет избавиться от условной компиляции, и во многих случаях получается более эффективный код.

На этом мы завершаем беглую экскурсию в мир ООП.

Ruby - модный, гибкий (agile) интерпретируемый язык, ориентированный на выражения. Он пластичен и поощряет частую переработку (refactoring), которая выполняется без особого труда. Ruby ориентирован на выражения, зачем писать предложение, когда выраже­ния достаточно? Это означает, в частности, что программа становится более ком­пактной, поскольку общие части выносятся в отдельное выражение и повторенияудается избежать. Ruby -- язык сверхвысокого уровия (VHLL). Под "плотностью" Ruby понимают тот факт, что сложные, запутанные операции можно записать гораздо проще, чем в языках бо­лее низкого уровня.

Ruby - чистый объектно-ориентированный язык программирования. Он был создан в 1993 году Юкихиро Мацумото. Вы можете найти имя Юкихиро Мацумото в списке рассылки Ruby на сайте www.ruby-lang.org. Мацумото также известен как Мац в сообществе руби.

Ruby имеет функции, аналогичные функциям Smalltalk, Perl и Python.

Perl, Python и Smalltalk - это языки сценариев. Smalltalk - настоящий объектно-ориентированный язык. Ruby, как и Smalltalk, является идеальным объектно-ориентированным языком. Использовние синтаксиса Ruby намного проще, чем синтаксис Smalltalk.

Особенности Ruby

  • Ruby имеет открытый исходный код и свободно доступен в Интернете, но требует лицензии.

  • Ruby - это интерпретируемый язык программирования общего назначения.

  • Ruby - настоящий динамический объектно-ориентированный язык программирования.

  • Ruby - это язык сценариев на стороне сервера, похожий на Python и PERL.

  • Ruby можно использовать для написания сценариев Common Gateway Interface (CGI).

  • Ruby можно встроить в язык гипертекстовой разметки (HTML).

  • Ruby имеет чистый и простой синтаксис, который позволяет новому разработчику очень быстро и легко изучить Ruby.

  • Ruby имеет синтаксис, аналогичный синтаксису многих языков программирования, таких как C ++ и Perl.

  • Ruby очень масштабируем, и большие программы, написанные на Ruby, легко обслуживаются.

  • Ruby можно использовать для разработки приложений для Интернета и интрасети.

  • Ruby можно установить в средах Windows и POSIX.

  • Ruby поддерживает множество инструментов с графическим интерфейсом, таких как Tcl / Tk, GTK и OpenGL.

  • Ruby можно легко подключить к DB2, MySQL, Oracle и Sybase.

  • Ruby имеет богатый набор встроенных функций, которые можно использовать непосредственно в сценариях Ruby.

Metts_Sendi_Ruby_Obektno-orientirovannoe_proektirovanie.pdf
Rails_4_Gibkaya_razrabotka_veb-prilozheniy_Obnovlennoe_izdanie.pdf
Head_First_O_39_Reilly_-_Makgavren_Dzh_-_Izuchaem_Ruby_-_2016.pdf
put_rails_rukovodstvo_po_sozdaniyu_prilozhenii_v_srede_ruby_on_rails.pdf
Lucas_Carlson_Leonard_Richardson_-_Ruby_Cookbook_-_2nd_Edition_-_Early_Release_-_2014.pdf
Програмирование на Ruby
Ruby on Rails для начинающих. Изучаем разработку веб-приложений на основе Rails ( PDFDrive.com ).pdf
Flenagan_D__Matsumoto_Yu_Yazyk_programmirovania.pdf