Подход к методам set без побочных эффектов

Я хотел бы надеть Ваше мнение как, как далеко пойти с методами set без побочных эффектов.

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

Activity activity;
activity.Start    = "2010-01-01";
activity.Duration = "10 days";   // sets Finish property to "2010-01-10"

Обратите внимание, что значения для даты и продолжительности показывают только в показательных целях.

Так с помощью метода set для любого из свойств Start, Finish и Duration следовательно, изменит другие свойства и таким образом не может считаться без побочных эффектов. То же запрашивает экземпляры Rectangle класс, где метод set для X изменяет значения Top и Bottom и так далее.

Вопрос состоит в том, где Вы чертили бы линию между использованием методов set, которые имеют побочные эффекты изменяющихся значений логически связанных свойств и методы использования, которые не могли быть намного более описательными так или иначе. Например, определяя названный метод SetDurationTo(Duration duration) также не отражает, что или Запустите или Конец, будет изменен.

5
задан Ricardo Altamirano 10 July 2012 в 18:17
поделиться

5 ответов

Я думаю, вы неправильно понимаете термин "побочный эффект" применительно к дизайну программы. Установка свойства является побочным эффектом, независимо от того, насколько сильно или слабо оно изменяет внутреннее состояние, если только оно изменяет какое-то состояние. Сеттер без побочных эффектов" был бы не очень полезен.

Побочные эффекты - это то, чего вы хотите избежать в свойствах getters. Чтение значения свойства - это то, от чего вызывающая сторона не ожидает изменения состояния (т.е. возникновения побочных эффектов), поэтому если это происходит, то обычно это неправильно или, по крайней мере, сомнительно (есть исключения, например, ленивая загрузка). Но и геттеры, и сеттеры в любом случае являются просто обертками для методов. Свойство Duration, с точки зрения CLR, является просто синтаксическим сахаром для метода set_Duration.

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

Итак, отвечая на прямой вопрос: Где я провожу черту? Нигде, пока метод/свойство действительно делает то, что подразумевает его название. Если установка Duration также изменяет ActivityName, это может быть проблемой. Если оно изменяет свойство Finish, это должно быть очевидно; должно быть невозможно изменить Duration, чтобы Start и Finish остались неизменными. Основная предпосылка ООП заключается в том, что объекты достаточно интеллектуальны, чтобы самостоятельно управлять этими операциями.

Если это беспокоит вас на концептуальном уровне, тогда вообще не используйте свойства мутатора - используйте неизменяемую структуру данных со свойствами только для чтения, где все необходимые аргументы предоставляются в конструкторе. Затем сделайте две перегрузки, одна из которых принимает Start/Duration, а другая - Start/Finish. Или сделайте только одно из свойств записываемым - скажем, Finish, чтобы оно соответствовало Start - и затем сделайте Duration доступным только для чтения. Используйте соответствующую комбинацию изменяемых и неизменяемых свойств, чтобы гарантировать, что существует только один способ изменить определенное состояние.

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

8
ответ дан 14 December 2019 в 04:35
поделиться

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

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

0
ответ дан 14 December 2019 в 04:35
поделиться

Лично я считаю, что для поддержания согласованного состояния имеет смысл иметь побочный эффект. Как вы сказали, имеет смысл изменить логически связанные значения. В некотором смысле побочный эффект ожидается. Но важно прояснить этот момент. То есть должно быть очевидно, что задача, которую выполняет метод, имеет какой-то побочный эффект. Поэтому вместо SetDurationTo вы можете вызвать свою функцию ChangeDurationTo , что означает, что происходит что-то еще. Вы также можете сделать это другим способом, используя функцию / метод, который регулирует продолжительность AdjustDurationTo и передает значение дельты . Было бы полезно, если бы вы задокументировали функцию как имеющую побочный эффект.

Я думаю, что еще один способ взглянуть на это - посмотреть, ожидается ли побочный эффект. В вашем примере с прямоугольником я ожидал, что он изменит значения top или bottom , чтобы поддерживать внутренне согласованное состояние. Не знаю, субъективно ли это; мне просто кажется, что это имеет смысл. Как всегда, я считаю, что документация побеждает. Если есть побочный эффект, хорошо задокументируйте его. Желательно по названию метода и через сопроводительную документацию.

1
ответ дан 14 December 2019 в 04:35
поделиться

Один из вариантов - сделать ваш класс неизменяемым и заставить методы создавать и возвращать новые экземпляры класса, все соответствующие значения которых изменены. Тогда нет никаких побочных эффектов или сеттеров. Подумайте о чем-то вроде DateTime , где вы можете вызывать такие вещи, как AddDays и AddHours , которые вернут новый экземпляр DateTime с примененными изменениями.

1
ответ дан 14 December 2019 в 04:35
поделиться

Думаю, это в основном вопрос здравого смысла.

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

Я бы предпочел увидеть что-то вроде этого:

Activity activity;
activity.Start    = DateTime.Parse("2010-01-01");
activity.Duration = Duration.Parse("10 days");

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

-2
ответ дан 14 December 2019 в 04:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: