В большинстве языков ООП объекты обычно изменяемы с ограниченным набором исключений (например, кортежи и строки в Python). В большинстве функциональных языков данные неизменны.
Как изменяемые, так и неизменяемые объекты имеют целый ряд собственных преимуществ и недостатков.
Существуют языки, которые пытаются объединить обе концепции, например, scala, где у вас есть (явно объявленные) изменяемые и неизменные данные (пожалуйста, исправьте меня, если я ошибаюсь, мои знания по scala более чем ограничены).
Мой вопрос: Есть ли в комплекте может мутировать (так!) Неизменность -ie не объекта , как только это было created- никакого смысла в контексте ООП?
Есть проекты или реализации такой модели?
По сути, являются ли (полная) неизменность и ООП противоположными или ортогональными?
Мотивация: В ООП обычно работает на данных, изменение (мутация) основополагающую информации, сохраняя ссылки между этими объектами. Например, объект класса Person
с членом, father
ссылающимся на другой Person
объект. Если вы изменяете имя отца, это сразу видно дочернему объекту без необходимости обновления. Будучи неизменным, вам нужно будет создавать новые объекты для отца и ребенка. Но у вас было бы намного меньше путаницы с общими объектами, многопоточностью, GIL и т. Д.
источник
Ответы:
ООП и неизменность почти полностью ортогональны друг другу. Однако императивное программирование и неизменность не являются.
ООП можно обобщить двумя основными функциями:
Инкапсуляция : я не буду напрямую обращаться к содержимому объектов, а буду общаться через этот объект через определенный интерфейс («методы»). Этот интерфейс может скрыть от меня внутренние данные. Технически это специфично для модульного программирования, а не ООП. Доступ к данным через определенный интерфейс примерно эквивалентен абстрактному типу данных.
Динамическая отправка : когда я вызываю метод для объекта, исполняемый метод будет разрешен во время выполнения. (Например, в ООП на основе классов я мог бы вызвать
size
метод дляIList
экземпляра, но вызов мог бы быть разрешен для реализации вLinkedList
классе). Динамическая диспетчеризация - это один из способов разрешить полиморфное поведение.Инкапсуляция не имеет смысла без изменчивости (нет никакого внутреннего состояния, которое могло бы быть повреждено внешним вмешательством), но оно все же имеет тенденцию облегчать абстракции, даже когда все является неизменным.
Императивная программа состоит из операторов, которые выполняются последовательно. Оператор имеет побочные эффекты, такие как изменение состояния программы. С неизменяемостью состояние не может быть изменено (конечно, может быть создано новое состояние). Поэтому императивное программирование принципиально несовместимо с неизменностью.
Теперь случается, что ООП исторически всегда было связано с императивным программированием (Simula основана на Algol), и все основные языки ООП имеют императивные корни (C ++, Java, C #,… все корни в C). Это не означает, что сам ООП будет обязательным или изменчивым, это просто означает, что реализация ООП на этих языках допускает изменчивость.
источник
Обратите внимание, что среди объектно-ориентированных программистов существует культура, в которой люди предполагают, что если вы выполняете ООП, большинство ваших объектов будут изменяемыми, но это отдельная проблема от того, требует ли ООП изменчивость. Кроме того, эта культура, похоже, постепенно меняется в сторону большей неизменности из-за подверженности людей функциональному программированию.
Scala - действительно хорошая иллюстрация того, что изменчивость не требуется для объектной ориентации. Хотя Scala поддерживает изменчивость, его использование не рекомендуется. Идиоматическая Scala очень ориентирована на объект и почти полностью неизменна. В основном это допускает изменчивость для совместимости с Java, а также потому, что в определенных обстоятельствах неизменяемые объекты неэффективны или сложны для работы.
Сравнить в список Scala и список Java , например. Неизменяемый список Scala содержит все те же методы объекта, что и изменяемый список Java. Более того, потому что Java использует статические функции для таких операций, как сортировка , а Scala добавляет методы функционального стиля, такие как
map
. Все отличительные признаки ООП - инкапсуляция, наследование и полиморфизм - доступны в форме, знакомой объектно-ориентированным программистам, и используются соответствующим образом.Единственное отличие, которое вы увидите, состоит в том, что при изменении списка вы получаете новый объект. Это часто требует от вас использования других шаблонов проектирования, чем при работе с изменяемыми объектами, но это не требует от вас полного отказа от ООП.
источник
Неизменность можно смоделировать на языке ООП, выставляя только точки доступа к объектам как методы или свойства только для чтения, которые не изменяют данные. В языках ООП неизменность работает так же, как и в любом функциональном языке, за исключением того, что вам могут не хватать некоторых функциональных языковых функций.
Похоже, вы предполагаете, что изменчивость является основной особенностью объектной ориентации. Но изменчивость - это просто свойство объектов или значений. Объектная ориентация включает в себя ряд внутренних понятий (инкапсуляция, полиморфизм, наследование и т. Д.), Которые практически не имеют ничего общего с мутацией, и вы все равно получите преимущества этих функций, даже если вы сделали все неизменным.
Не все функциональные языки также требуют неизменности. Clojure имеет специальную аннотацию, позволяющую изменять типы, и большинство «практических» функциональных языков имеют возможность указывать изменяемые типы.
Лучше задать вопрос: «Имеет ли смысл полная неизменность в императивном программировании?» Я бы сказал, что очевидный ответ на этот вопрос - нет. Чтобы достичь полной неизменности в императивном программировании, вам придется отказаться от таких вещей, как
for
циклы (поскольку вам придется мутировать переменную цикла) в пользу рекурсии, и теперь вы, по сути, программируете в любом случае функционально.источник
Часто полезно классифицировать объекты как инкапсулирующие значения или сущности, с отличием в том, что если что-то является значением, код, содержащий ссылку на него, никогда не должен видеть изменения своего состояния каким-либо образом, который сам код не инициировал. Напротив, код, который содержит ссылку на объект, может ожидать, что он изменится способами, не зависящими от держателя ссылки.
Хотя можно использовать инкапсулированное значение, используя объекты изменяемого или неизменяемого типов, объект может вести себя как значение, только если применяется хотя бы одно из следующих условий:
Никакая ссылка на объект никогда не будет подвергаться воздействию чего-либо, что может изменить состояние, заключенное в нем.
Держатель хотя бы одной из ссылок на объект знает все виды использования, к которым может быть применена любая существующая ссылка.
Поскольку все экземпляры неизменяемых типов автоматически удовлетворяют первому требованию, их легко использовать в качестве значений. С другой стороны, обеспечить выполнение любого из этих требований при использовании изменяемых типов намного сложнее. Принимая во внимание, что ссылки на неизменяемые типы могут быть свободно переданы как средство инкапсуляции заключенного в них состояния, для обхода состояния, хранящегося в изменяемых типах, требуется либо создание неизменяемых объектов-оболочек, либо копирование состояния, инкапсулированного частными объектами, в другие объекты, которые либо предоставлены, либо созданы для получателя данных.
Неизменяемые типы очень хорошо работают для передачи значений и часто, по крайней мере, в некоторой степени пригодны для манипулирования ими. Однако они не так хороши в обращении с сущностями. Наиболее близкой вещью, которую можно иметь к сущности в системе с чисто неизменяемыми типами, является функция, которая, учитывая состояние системы, сообщит об этих атрибутах некоторой ее части или создаст новый экземпляр состояния системы, который похож на поставляется один, за исключением какой-то конкретной его части, которая будет отличаться по выбору. Кроме того, если цель объекта состоит в том, чтобы связать некоторый код с чем-то, что существует в реальном мире, для объекта может быть невозможно избежать демонстрации изменяемого состояния.
Например, если кто-то получает данные по TCP-соединению, он может создать новый объект «состояние мира», который включает эти данные в свой буфер, не затрагивая ссылки на старое «состояние мира», но старые копии состояние мира, которое не включает последнюю порцию данных, будет дефектным и не должно использоваться, так как они больше не будут соответствовать состоянию реального сокета TCP.
источник
В c # некоторые типы неизменны как строка.
Это, кроме того, предполагает, что выбор был решительно рассмотрен.
Наверняка это действительно производительность, требующая использования неизменяемых типов, если вам нужно модифицировать этот тип сотни тысяч раз. Вот почему в этом случае предлагается использовать
StringBuilder
класс вместоstring
класса.Я провел эксперимент с профилировщиком, и использование неизменяемого типа действительно требует больше ресурсов процессора и оперативной памяти.
Это также интуитивно понятно, если учесть, что для изменения одной буквы в строке из 4000 символов необходимо скопировать каждый символ в другой области ОЗУ.
источник
string
конкатенации. Для практически всех видов данных / вариантов использования может быть изобретена (часто уже была) эффективная постоянная структура. Большинство из них имеют примерно одинаковую производительность, даже если постоянные факторы иногда хуже.string
(традиционное представление). «Строка» (в представлении, о котором я говорю) после 1000 модификаций будет похожа на вновь созданную строку (содержимое по модулю); никакая полезная или широко используемая постоянная структура данных не ухудшает качество после операций X. Фрагментация памяти не является серьезной проблемой (да, у вас было бы много выделений, да, но у современных сборщиков мусора фрагментация вполне не проблема)Полная неизменность всего не имеет большого смысла в ООП или большинстве других парадигм в этом отношении по одной очень важной причине:
У каждой полезной программы есть побочные эффекты.
Программа, которая ничего не меняет, ничего не стоит. Вы можете даже не запускать его, так как эффект будет идентичным.
Даже если вы думаете, что ничего не меняете и просто суммируете список чисел, которые вы каким-то образом получили, подумайте, что вам нужно что-то сделать с результатом - печатаете ли вы его в стандартный вывод, записываете ли вы его в файл, или где угодно. И это включает в себя изменение буфера и изменение состояния системы.
Может иметь смысл ограничивать изменчивость частями, которые необходимо изменить. Но если абсолютно ничего не нужно менять, значит, вы ничего не делаете.
источник
Я думаю, это зависит от того, использует ли ваше определение ООП стиль передачи сообщений.
Чистые функции не должны ничего мутировать, потому что они возвращают значения, которые вы можете сохранить в новых переменных.
Благодаря стилю передачи сообщений вы указываете объекту сохранять новые данные, а не спрашиваете, какие новые данные следует хранить в новой переменной.
Можно иметь объекты и не изменять их, делая их методы чистыми функциями, которые живут внутри объекта, а не снаружи.
Но невозможно смешать стиль передачи сообщений и неизменные объекты.
источник