Подход к сеттерам без побочных эффектов - PullRequest
5 голосов
/ 16 марта 2010

Мне бы хотелось узнать ваше мнение о том, как далеко продвинуться с сеттерами без побочных эффектов.

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

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

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

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

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

Ответы [ 5 ]

8 голосов
/ 16 марта 2010

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

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

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

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

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

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

1 голос
/ 16 марта 2010

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

1 голос
/ 16 марта 2010

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

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

0 голосов
/ 16 марта 2010

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

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

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

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

То есть, вы заметили, что вы делаете разбор строк. Разрешить программисту указывать строго типизированные объекты, когда это также уместно.

0 голосов
/ 16 марта 2010

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

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

...