Как ООП эволюционировала, чтобы включить понятие свойств

12

Я пришел из C ++ фона и полностью использую C # в своей текущей работе, и я только что прочитал много вопросов и ответов о том, в чем разница между открытыми полями и свойствами, и обо всем, что есть в вариантах и ​​воплощениях этого основной вопрос (например, этот пост SO и все связанные вопросы ). Все эти вопросы рассматриваются с точки зрения практических различий, которые принимают как должное существование системы свойств, но я думаю, что было бы хорошо подойти к этому вопросу с точки зрения того, что разработчики всех языков, которые решили поддерживать свойства в первом место было думать (проверить список в статье в Википедии здесь). Как ООП эволюционировал от C ++ / Java, чтобы распространиться на то, что в статье в Википедии интересно идентифицируется как промежуточное звено между методами и данными-членами:

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

MSDN добавляет дополнительную информацию:

«Хотя свойства технически очень похожи на методы, они весьма различны с точки зрения сценариев их использования. Их следует рассматривать как интеллектуальные поля. У них есть синтаксис вызова полей и гибкость методов».

Я хотел бы знать, как получилось, что этот промежуточный уровень инкапсуляции оказался полезным для программирования в целом. Я предполагаю, что эта концепция не присутствовала при первом воплощении языков программирования, которые выражали парадигму ООП.

jxramos
источник
8
Голая языковая особенность - просто удобный синтаксический сахар для методов получения и установки. Есть ли более глубокие причины толкования и терминологии, я не знаю.
В раунде экспертов по ОО название вашего вопроса может быть провокационным и может отвлекать от того, что вы действительно спрашивали. Не то чтобы я воспринимал себя экспертом по ОО, я просто «пользователь ОО» уже несколько лет.
Док Браун
Это синтаксическая тонкость, которую я пропустил, переходя от «методов без параметров» в Python к C ++. BMI = bob.weight/sq(bob.height)читает лучше без ()ИМО.
OJFord
Я думаю, что свойства были в исходном (не .net) Visual Basic, как способ настройки объекта Active X (COM). Сетка свойств в этом инструменте, вероятно, как-то связана с принятием свойств в языках. Обратите внимание, что оригинальный Visual Basic не был объектно-ориентированным во многих отношениях, но он имел возможность создавать что-то вроде классов.
Фрэнк Хилман
Продолжение: окно свойств было способом настройки объектов без написания кода. Это называлось RAD-разработкой. Эта ссылка содержит скриншот окна свойств VB6: microsoft.com/mspress/books/WW/sampchap/4068.aspx
Фрэнк Хайлман,

Ответы:

5

Все дело в инкапсуляции и принципе унифицированного доступа.

Объект должен иметь возможность отвечать на сообщение, возвращая существующие данные или запуская метод, но отправитель не должен иметь возможность определить, что есть что. Или, если вы просматриваете это со стороны отправителя: отправитель должен иметь доступ к существующим данным или запускать метод через единый интерфейс.

Есть несколько способов добиться этого:

  • избавиться от данных в целом, просто есть методы (это делает Newspeak)

    • менее радикальная форма из вышеперечисленного: избавиться от данных в общедоступном интерфейсе , то есть сделать данные всегда частными, в общедоступном интерфейсе только предоставлять методы (Smalltalk, Ruby делают это)
  • избавиться от методов в целом, просто иметь данные (Self делает это, «методы» - это просто Methodобъекты, назначенные переменным экземпляра, переменные экземпляра ищутся с помощью виртуальной диспетчеризации)

  • не делать синтаксических или семантических различий между методами и данными (Scala делает это, доступ к полю синтаксически неотличим от вызова метода без аргумента list ( foo.bar), присваивание полю синтаксически неотличимо от вызова специально названного метода ( foo.bar = baz) - то же самое а foo.bar_=(baz)то есть вызов метода с именем foo_=, и только для чтения значения могут быть переопределены или реализованы с помощью метода без списка параметров (то есть val foo) в суперкласс может быть переопределен (или abstract valреализовать) в подклассе с методом def foo)

Java, однако, не следует принципу унифицированного доступа. В Java можно различать доступ к данным и запуск метода. foo.barотличается от foo.bar(). Причина этого в том, что поля и методы семантически и синтаксически различны.

C # пытается исправить это, добавляя свойства в язык, в основном методы, которые выглядят как поля. Однако вызовы методов по-прежнему выглядят и отличаются от доступа к полям и свойствам. Поля и свойства теперь имеют унифицированный доступ, но методы все еще не имеют.

Таким образом, это на самом деле не решает проблему: вы не можете исправить наличие двух разных способов доступа к вещам, добавив третий способ доступа к вещам! Даже если этот третий путь выглядит как один из двух других, у вас все равно останутся (по крайней мере) два разных пути. Вы можете исправить это, либо избавившись от всех различных способов, кроме одного, либо избавившись от различий.

Прекрасно иметь язык с методами, свойствами и полями, но все три должны иметь одинаковый доступ.

Йорг Миттаг
источник
1
Почему вы (и другие) думаете, что поддержание Uniform Access так важно? С помощью метода вы можете задать его параметры, чтобы изменить то, что он делает или действует. С полем или свойством у вас нет этой возможности, вы, как правило, просто возвращаете данные (хотя свойство может быть реализовано в виде запуска метода вместо простого предоставления частного поля). Мне это кажется интуитивно понятным, может быть, только потому, что я к этому привык, но что бы вы получили в элегантности или производительности, обеспечив единообразный доступ? Боитесь ли вы, что утечка деталей реализации вне класса?
Майк поддерживает Монику
1
UAP подразумевает за собой свободу изменять детали реализации, не нарушая общедоступный интерфейс. Обычный пример - добавление регистрации. Если я предлагаю доступ через метод, я могу тривиально изменить внутреннюю часть метода для записи в журнал. С открытым полем я не могу добавить запись в журнал, не превратив его сначала в метод, который нарушает весь клиентский код. Если нельзя рисковать изменениями, использование методов обязательно. Свойства являются оптимальным компромиссом, потому что они полные методы, но выглядят как поля. Присоединение к UAP улучшает инкапсуляцию и уменьшает жесткое сцепление.
Амон
Много хороших ответов на этот вопрос, но я думаю, что этот вопрос ударил по гвоздю, вытащив более фундаментальные аспекты природы языков программирования и развивая соответствующую формальную концепцию UAP. Solid
jxramos
18

Ну, я не уверен на 100%, но я думаю, что все, вероятно, проще, чем вы ожидаете. В школах OO-моделирования 90-х годов существовала потребность в классах моделирования с инкапсулированными атрибутами членов, а при реализации в таких языках, как C ++ или Java, это обычно приводило к коду с большим количеством методов получения и установки, поэтому много «шума» код для относительно простого требования. Обратите внимание, что большинство (вероятно, все не проверяли это) языков, перечисленных в вашей связанной статье в Википедии, начали вводить «свойства» объектов в конце 90-х или позже.

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

Док Браун
источник
Он не имеет ничего общего с объектно-ориентированным программированием, за исключением того, что свойство является абстракцией поля, а поля являются частью объектно-ориентированного программирования. Но свойства не нужны для объектно-ориентированного программирования, как вы правильно заметили.
Фрэнк Хилман
2
@FrankHileman: «свойство - это абстракция поля, а поля являются частью объектно-ориентированного программирования» - ну, мне кажется, вы согласны, что концепция действительно имеет какое-то отношение к ООП. И вот почему ты отверг меня?
Док Браун
3
LOL. Вы можете винить его? Он выскользнул из туалета и ударил головой по раковине. Во всяком случае ... То, как я всегда видел это, было то, что свойства не являются строго ооо. Они помогают с инкапсуляцией, а инкапсуляция помогает с OO.
MetaFight
1
Ха! Для меня это введение в programmers.stackexchange. Lol, намного более горячий, чем мой аналогичный опыт в обычном стековом потоке :-p Хороший способ перепутать день!
jxramos
11

У вас это задом наперед (вроде). Лямбда-исчисление существует в качестве основной формальной основы для языков программирования и существует на протяжении десятилетий. У него нет полей.

Чтобы смоделировать изменяемое состояние, вам нужно сделать две абстракции. Один представляет настройку некоторого состояния, а другой возвращает это состояние (глава 13 в моей версии TaPL для справки). Звучит знакомо? С теоретической точки зрения, у ОО не было такого материала. ОО прочитал языки программирования 101 и сделал маленький шаг вперед.

С практической точки зрения, есть две довольно четкие мотивы. Вы пришли из C ++ фона, так что бы произошло, если бы у вас было открытое поле - скажем ... текст в текстовом поле. Что происходит, когда вы хотите изменить свой дизайн так, чтобы «всякий раз, когда это текстовое поле изменялось, делал бла»? Вы можете убить это поле, создать одну или две функции и подключить эту логику, поскольку вы не можете доверять разработчикам самим вызывать «UpdateTextbox». Это очень серьезное изменение в вашем API (и, к сожалению, все еще серьезное изменение в реализации свойств .NET). Такое поведение повсеместно в Windows API. Поскольку в Microsoft это большое дело, C #, вероятно, хотел сделать это менее болезненным.

Другая большая мотивация - Java Beans и их родственники. Был создан ряд фреймворков для использования отражения Java для поиска GetXи SetXсопряжения и эффективной обработки их как современных свойств. Но так как они не были реальными языковыми конструкциями, эти структуры были хрупкими и непривлекательными. Если вы напишите имя, все будет просто молча. Если один подвергся рефакторингу, ничто не переместило другую сторону собственности. И выполнение всего шаблона field / get / set было многословно и утомительно (даже для Java!). Поскольку C # разрабатывался в основном как «Java с извлеченными уроками», такой вид боли был одним из таких уроков.

Но самое главное, что концепция собственности была успешной. Это легко понять, это легко использовать. Это очень помогает принятию, и, как инструмент , программисты обнаружили, что свойства решают подмножество проблем более чисто, чем функции или поля.

Telastyn
источник
7
Я подавил вас в основном за слишком большое количество ссылок на ненужную формальную чушь. У реальных реализаций языков программирования есть намного более серьезные вещи, чтобы рассмотреть, была ли их модель включена в лямбда-исчисление.
DeadMG
9
@DeadMG - это, безусловно, правда, но, с другой стороны, разработчики языка программирования неизменно будут знакомы с этой несущественной формальной чушью . Думать, что это не повлияло на их замыслы, наивно.
Теластин
3
Это определенно не «неуместная формальная чушь». Тем не менее, лямбда-исчисление, возможно, не было принято во внимание для ранних языков программирования. Если это так, процессоры, вероятно, будут более ориентированы на этот менталитет.
Фрэнк Хайлеман,
3
@FrankHileman - Я уверен, что Лисп
обдумал
4
@Telastyn - да - и Лисп изначально не должен был быть языком программирования, помнишь?
Фрэнк Хайлеман,
3

В частности, в .net свойства берут свое начало со старых времен Visual Basic, которые, как это происходит, не были объектно-ориентированными в том виде, в каком мы думаем об этом сегодня. Она была построена вокруг новой COM-системы, которая поверхностно воспринимала все не как классы, а как компоненты, которые открывали бы свойства, к которым можно обращаться как в коде, так и в графических редакторах. Когда VB и недавно созданный C # были объединены в .net, VB приобрел множество OOP-функций и сохранили свойства, поскольку их удаление было бы шагом назад - представьте, если бы инструмент автоматического обновления кода, который они имели в Visual Studio, был заменив все ваши свойства геттерами и сеттерами и нарушив совместимость со всеми COM-библиотеками. Было бы только логично поддержать их во всем.

niwax
источник
Я не думаю, что вы должны игнорировать происхождение C #, которое пришло из Delphi и ранее, Object Pascal и Turbo Pascal с объектами. Они были объектно-ориентированными и имели свойства (хотя я не могу вспомнить, имел ли OP свойства из TP5.5 или были ли они добавлены; предположительно, они были продолжены в C # Андерсом, потому что они были прагматически хорошей идеей.
amaca
Очень интересно, что вы оказались единственной, кто затронул компонент этого вопроса. Я только добавил комментарий к своему вопросу, ссылаясь на сайт, который перечислял свойства как часть некоторой модели свойства-метода-события, которой удовлетворяет C #. Затем я снова поискал на странице своего вопроса «компонент», что дало несколько ложных срабатываний, но ваш ответ, я думаю, фактически совпадает с тем, с чем я связан.
jxramos
3

Свойства не имеют ничего общего с объектно-ориентированным программированием, потому что свойства являются только синтаксическим сахаром. Свойства внешне выглядят как поля, и в мире .net рекомендуется, чтобы они в некотором смысле вели себя как поля, но в любом случае они не являются полями. Свойства являются синтаксическим сахаром для одного или двух методов: один для получения значения и один для установки значения. Либо метод set, либо метод get могут быть опущены, но не оба. Может не быть поля, в котором хранится значение, возвращаемое методом get. Поскольку они имеют общий синтаксис с полями и поскольку они часто используют поля, люди связывают свойства с полями.

Свойства имеют преимущества перед полями:

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

Поскольку свойства являются абстракцией полей, и для удобства синтаксиса такие языки, как C #, приняли синтаксис поля для свойств.

Фрэнк Хилман
источник
2
Первая строка из статьи Википедии. «Свойство в некоторых объектно-ориентированных языках программирования - это особый вид члена класса ...». Итак, ваше первое предложение совершенно неверно. А оставшаяся часть не отвечает на вопрос.
Док Браун
1
@DocBrown: интересный ответ. Многие статьи Википедии явно неверны. Я полагаю, вы защищаете автора этой конкретной статьи?
Фрэнк Хайлеман
1
Не все особенности объектно-ориентированных языков программирования связаны с объектно-ориентированными концепциями.
Фрэнк Хайлеман
1
Это не меняет того факта, что вы не отвечаете на вопрос.
Док Браун
3
Следует отметить, что это не только установщик, который является необязательным. Вы можете иметь только сеттерские свойства.
Теластин
2

Это вопрос реализации, а не последствий . Свойства были в ООП до того, как C ++ или Java появились на сцене (они были там, с некоторой шероховатостью по краям, в Simula, и они являются фундаментальными для Smalltalk). Объекты со свойствами концептуально отличаются от значений с прикрепленным кодом. Префиксы get & set в некоторых языковых соглашениях служат только для мутной воды; они сообщают вам о разнице между полями и свойствами, предполагая, что к полям можно обращаться напрямую без получения / установки таким образом, который идиоматичен для языка, и это утечка.

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

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

Однако не стоит говорить, что эта абстракция обходится дорого. Если программист, использующий класс, не может определить, обращается ли он или она к данным, когда они хранятся, или получает / устанавливает значения, которые необходимо вычислить, тогда будет уровень, на котором язык также обязательно будет неопределенным (и, следовательно, может требует, чтобы все требовало кода для промежуточного соединения между аксессорами / селекторами и значениями). Нет ничего концептуально неправильного в «структурах с кодом» - они, безусловно, могут быть гораздо более эффективными - но они повсеместно пропускают реализацию, и это одна из вещей, которую ООП должен устранить.

Стэн Роджерс
источник
Я согласен с некоторыми пунктами и не согласен с другими, но в целом сообщением является хорошей: Некоторой информацией об потребности объекта в вычисляться , но все еще технически информация , которая является , а не действие , которое может быть сделано .
Pharap
0

Абсолютно ничего вообще. Свойства и ООП не имеют ничего общего друг с другом. Свойства являются не чем иным, как синтаксическим сахаром для вызовов функций, и, следовательно, имеют точно такое же ООП-вложение, что и вызовы функций, то есть нет.

Кстати, википедия совершенно неверна. Шаблон getMember / setMember, который вы видите в Java, предлагает те же преимущества (и недостатки), что и свойства в C #. И вы можете повторить этот шаблон даже в C, если хотите.

Свойства в C # - не более чем поддерживаемый языком синтаксический сахар.

DeadMG
источник
3
Вы когда-нибудь видели концепцию свойства в каком-либо языке программирования вне контекста класса или объекта? Я не.
Док Браун
1
@DocBrown: я реализовал это. Фактически, свойства являются довольно низкозначными объектами для авторов языков, поскольку их улучшение по сравнению с обычными вызовами функций является низким.
DeadMG
1
ОП спрашивал об истории свойств как концепции в популярных языках программирования. И на самом деле, в историческом смысле существует связь между свойствами и ООП.
Док Браун
2
Возможно, я был немного опрометчив, чтобы приписать эту концепцию свойств парадигме ООП как таковой, но как концепция она, безусловно, выходит за рамки отдельных языков и их функций. Возможно я изо всех сил пытаюсь сформулировать соответствующую онтологию между этими двумя. Я не предполагаю в названии вопроса, что свойства являются фундаментальной особенностью / концепцией ООП, которая создает или нарушает формализм ООП, и все же я чувствую, что они имеют реальность только в этом пространстве ООП, поэтому я сформулировал вопрос как таковой ,
jxramos
4
Они не просто синтаксический сахар в C #, они существуют в метаданных сборки как отдельные элементы, и вы можете делать вещи со свойствами, которые вы не можете использовать с полями, такими как привязка данных.
Энди