У меня есть класс с именем, Heading
который делает несколько вещей, но он также должен иметь возможность возвращать значение, противоположное текущему значению заголовка, которое, в конечном итоге, должно использоваться посредством создания нового экземпляра самого Heading
класса.
У меня может быть простое свойство, вызываемое reciprocal
для возврата противоположного заголовка текущего значения, а затем вручную создать новый экземпляр класса Heading, или я могу создать метод, подобный createReciprocalHeading()
автоматическому созданию нового экземпляра класса Heading и возвращению его в пользователь.
Однако один из моих коллег порекомендовал мне просто создать свойство класса с именем, reciprocal
которое возвращает новый экземпляр самого класса через его метод getter.
Мой вопрос: не является ли это анти-паттерном для свойства класса, которое ведет себя так?
Я нахожу это менее интуитивным, потому что:
- На мой взгляд, свойство класса не должно возвращать новый экземпляр класса, и
- Имя свойства, которое
reciprocal
не помогает разработчику полностью понять его поведение, без получения помощи от IDE или проверки подписи получателя.
Я слишком строг в отношении того, что должно делать свойство класса, или это действительно проблема? Я всегда пытался управлять состоянием класса с помощью его полей и свойств, а его поведения - с помощью его методов, и я не вижу, как это соответствует определению свойства класса.
Heading
неизменный тип иreciprocal
возвращающий новый,Heading
является хорошей практикой "ямы успеха". (с оговоркой при двух вызовахreciprocal
должен возвращать «одно и то же», то есть они должны пройти тест на равенство.)Ответы:
Не секрет, что есть такие вещи, как Copy () или Clone (), но да, я думаю, вы правы, что беспокоитесь об этом.
возьмем для примера:
Было бы неплохо иметь предупреждение о том, что свойство каждый раз было новым объектом.
Вы также можете предположить, что:
Тем не мение. Если бы ваш класс заголовка представлял собой тип неизменяемого значения, то вы могли бы реализовать эти отношения, а взаимное может быть хорошим именем для операции.
источник
Copy()
иClone()
есть методы, а не свойства. Я полагаю, что OP ссылается на получателей свойств, возвращающих новые экземпляры.String.Substring()
,Array.Reverse()
,Int32.GetHashCode()
и т.д.If your heading class was an immutable value type
- Я думаю, вы должны добавить, что установщики свойств в неизменяемых объектах должны всегда возвращать новые экземпляры (или, по крайней мере, если новое значение не совпадает со старым значением), потому что это определение неизменяемых объектов. :)Интерфейс класса заставляет пользователя класса делать предположения о том, как он работает.
Если многие из этих предположений верны, а немногие ошибочны, интерфейс - это хорошо.
Если многие из этих допущений ошибочны, а немногие верны, интерфейс является мусором.
Распространенное предположение о свойствах заключается в том, что вызов функции get является дешевым. Другое распространенное предположение о свойствах состоит в том, что вызов функции get дважды подряд вернет одно и то же.
Вы можете обойти это, используя последовательность, чтобы изменить ожидания. Например, для небольшой трехмерной библиотеки, где вам нужно
Vector
,Ray
Matrix
и т. Д. Вы можете сделать ее такой, чтобы получатели, такие какVector.normal
иMatrix.inverse
, почти всегда были дорогими. К сожалению, даже если ваши интерфейсы постоянно используют дорогие свойства, интерфейсы в лучшем случае будут такими же интуитивно понятными, как и тот, который использует,Vector.create_normal()
иMatrix.create_inverse()
- тем не менее, я не знаю убедительных аргументов в пользу того, что использование свойств создает более интуитивный интерфейс даже после изменения ожиданий.источник
Свойства должны возвращаться очень быстро, возвращать одно и то же значение при повторных вызовах, и получение их значения не должно иметь побочных эффектов. То, что вы здесь описываете, не должно быть реализовано как свойство.
Ваша интуиция в целом верна. Заводской метод не возвращает одно и то же значение при повторных вызовах. Каждый раз он возвращает новый экземпляр. Это в значительной степени дисквалифицирует это как собственность. Это также не быстро, если последующая разработка добавляет вес созданию экземпляра, например, сетевых зависимостей.
Свойства обычно должны быть очень простыми операциями, которые получают / устанавливают часть текущего состояния класса. Фабричные операции не соответствуют этим критериям.
источник
DateTime.Now
Свойство обычно используется и относится к новому объекту. Я думаю, что имя свойства может помочь устранить неоднозначность того, возвращается ли один и тот же объект каждый раз. Это все во имя и как это используется.DateTime
это тип значения и не имеет понятия идентичности объекта. Если я правильно понимаю, проблема в том, что он недетерминирован и каждый раз возвращает новое значение.DateTime.New
является статическим, и поэтому вы не можете ожидать, что он будет представлять состояние объекта.Я не думаю, что есть независимый от языка ответ на этот вопрос, поскольку то, что составляет «свойство», является языковым вопросом, и то, что ожидает вызывающий «свойство», также является языковым вопросом. Я думаю, что самый плодотворный способ думать об этом - думать о том, как это выглядит с точки зрения звонящего.
В C # свойства отличаются тем, что они (условно) пишутся с большой буквы (например, методы), но не содержат скобок (например, переменные общедоступного экземпляра). Если вы видите следующий код, отсутствие документации, что вы ожидаете?
Как относительный новичок в C #, но тот, кто читал Руководство по использованию собственности Microsoft , я ожидаю
Reciprocal
, среди прочего:Heading
классаReciprocalChanged
мероприятиеИз этих предположений (3) и (4), вероятно, являются правильными (предполагая,
Heading
что это тип неизменяемого значения, как в ответе Юана ), (1) является дискуссионным, (2) неизвестен, но также дискуссионен, и (5) вряд ли производить семантический смысл (хотя то , что имеет заголовок , возможно , следует иметьHeadingChanged
событие). Это наводит меня на мысль, что в C # API «получить или вычислить обратное значение» не следует реализовывать как свойство, но особенно если вычисление дешевое иHeading
неизменное, это пограничный случай.(Тем не менее, обратите внимание, что ни одна из этих проблем не имеет никакого отношения к тому, создает ли вызов свойства новый экземпляр , даже (2). Создание объектов в CLR само по себе довольно дешево.)
В Java свойства являются соглашением именования методов. Если я увижу
мои ожидания аналогичны приведенным выше (если они изложены менее четко): я ожидаю, что звонок будет дешевым, идемпотентным и не будет иметь побочных эффектов. Однако вне платформы JavaBeans концепция «свойства» не так уж и значима в Java, и, в частности, при рассмотрении неизменяемого свойства, не имеющего соответствующего
setReciprocal()
,getXXX()
соглашение теперь несколько старомодно. Из Effective Java , второе издание (уже более восьми лет):В современном, более свободном API я бы ожидал увидеть
- что еще раз говорит о том, что вызов дешев, идемпотентен и не имеет побочных эффектов, но ничего не говорит о том, выполняется ли новый расчет или создается новый объект. Это хорошо; в хорошем API, мне все равно.
В Ruby нет такой вещи как собственность. Есть «атрибуты», но если я увижу
У меня нет непосредственного способа узнать, обращаюсь ли я к переменной экземпляра с
@reciprocal
помощьюattr_reader
или с помощью простого метода доступа, или я вызываю метод, который выполняет дорогостоящие вычисления. Тот факт, что имя метода является простым существительным, хотя, скорее, чем, скажемcalcReciprocal
, предполагает, опять же, что вызов, по крайней мере, дешево и, вероятно, не имеет побочных эффектов.В Scala соглашение об именах заключается в том, что методы с побочными эффектами принимают скобки, а методы без них - нет, но
может быть любым из:
(Обратите внимание, что Scala допускает различные вещи, которые, тем не менее, не поощряются руководством по стилю . Это одно из моих главных раздражений в Scala.)
Отсутствие скобок говорит мне, что у звонка нет побочных эффектов; имя, опять же, предполагает, что звонок должен быть относительно дешевым. Кроме того, мне все равно, как это приносит мне ценность.
Короче говоря: знайте язык, который вы используете, и знайте, чего ожидают другие программисты от вашего API. Все остальное - деталь реализации.
источник
Как уже говорили другие, это довольно распространенный шаблон для возврата экземпляров того же класса.
Наименование должно идти рука об руку с соглашениями об именовании языка.
Например, в Java я бы, вероятно, ожидал, что он будет вызван
getReciprocal();
При этом я бы рассмотрел изменчивые против неизменных объектов.
С неизменными все довольно просто, и не повредит, если вы вернете тот же объект или нет.
С изменчивыми это может стать очень страшным.
Теперь, что б имеет в виду? Взаимное от первоначального значения? Или ответная реакция измененного? Это может быть слишком коротким примером, но вы понимаете.
В этих случаях ищите лучшее наименование, которое сообщает, что происходит, например,
createReciprocal()
может быть лучшим выбором.Но это действительно зависит от контекста.
источник
getReciprocal()
, поскольку это «звучит» как «нормальный» метод получения стиля JavaBeans. Предпочитают «createXXX ()» или, возможно, «CalcuXXXX ()», которые указывают илиВ интересах единоличной ответственности и ясности, я бы использовал ReverseHeadingFactory, которая бы взяла объект и вернула его обратно. Это сделало бы более понятным, что возвращался новый объект, и означало бы, что код для создания обратного был инкапсулирован от другого кода.
источник
Это зависит. Я обычно ожидаю, что свойства будут возвращать вещи, которые являются частью экземпляра, поэтому возвращение другого экземпляра того же класса будет немного необычным.
Но, например, строковый класс может иметь свойства "firstWord", "lastWord" или, если он обрабатывает Unicode, "firstLetter", "lastLetter", которые будут объектами с полными строками - просто обычно меньшими.
источник
Поскольку другие ответы касаются части недвижимости, я просто расскажу о вашем # 2 о
reciprocal
:Не используйте ничего, кроме
reciprocal
. Это единственный правильный термин для того, что вы описываете (и это формальный термин). Не пишите некорректное программное обеспечение , чтобы сохранить разработчик от обучения домена они работают. В навигации, термины имеют весьма специфические значения и использовать что - то , как , казалось бы , безобидные , какreverse
иopposite
могут привести к путанице в определенных контекстах.источник
По логике нет, это не свойство заголовка. Если бы это было так, вы могли бы также сказать, что отрицательное число является свойством этого числа, и, откровенно говоря, это может привести к тому, что «свойство» потеряет всякий смысл, почти все будет собственностью.
Взаимное является чистой функцией заголовка, так же, как отрицательное число является чистой функцией этого числа.
Как правило, если установка не имеет смысла, вероятно, это не должно быть свойство. Конечно, его установка может быть запрещена, но теоретически возможно и целесообразно добавить сеттер.
Теперь, в некоторых языках, может иметь смысл иметь его как свойство, как указано в контексте этого языка, в любом случае, если это дает некоторые преимущества из-за механики этого языка. Например, если вы хотите сохранить его в базе данных, возможно, целесообразно сделать его как свойство, если это позволяет автоматизировать обработку в рамках ORM. Или, может быть, это язык, в котором нет различия между свойством и функцией-параметром без параметров (я не знаю такого языка, но я уверен, что они есть). Затем разработчик и документатор API должен различать функции и свойства.
Изменить: В частности, для C #, я бы посмотрел на существующие классы, особенно из стандартной библиотеки.
BigInteger
иComplex
структуры. Но они являются структурами и имеют только статические функции, а все свойства имеют различный тип. Так что не так много дизайна поможет вамHeading
класс.Vector
класс , который имеет очень мало свойств и, кажется, довольно близок к вашемуHeading
. Если вы пойдете этим путем, то у вас будет функция для взаимности, а не свойство.Возможно, вы захотите взглянуть на библиотеки, фактически используемые вашим кодом, или на существующие подобные классы. Старайтесь быть последовательными, это, как правило, лучше, если один путь не совсем верен, а другой нет.
источник
Действительно очень интересный вопрос. Я не вижу нарушения SOLID + DRY + KISS в том, что вы предлагаете, но, тем не менее, оно плохо пахнет.
Метод, который возвращает экземпляр класса, называется конструктором , верно? поэтому вы создаете новый экземпляр из неконструктивного метода. Это не конец света, но как клиент я бы этого не ожидал . Обычно это делается при определенных обстоятельствах: завод или, иногда, статический метод одного и того же класса (полезно для одиночных игр и для ... не очень объектно-ориентированных программистов?)
Плюс: что произойдет, если вы вызовете
getReciprocal()
возвращенный объект? Вы получаете другой экземпляр класса, который может быть точной копией первого! И если вы вызываетеgetReciprocal()
на ЭТОГО? Кто-нибудь растянулся?Опять же: что, если invoker требуется обратная величина (я имею в виду скаляр)?
getReciprocal()->getValue()
? Мы нарушаем Закон Деметры ради небольших выгод.источник