Передача копии объекта в метод - кто делает копирование?

У меня есть объект, который я передаю в вызове метода. Скажем, я использую язык, который позволяет передавать объекты только по ссылке, например, Java или PHP. Если метод вносит изменения в объект, это повлияет на вызывающего. Я не хочу, чтобы это случилось. Кажется, мне нужно сделать копию объекта.

Мой вопрос: кто несет ответственность за клонирование объекта? Вызывающий, прежде чем он вызывает метод? Или вызываемый, прежде чем он меняет объект?

РЕДАКТИРОВАТЬ: просто чтобы уточнить, я хочу, чтобы это было частью контракта этого метода - что он никогда не изменяет исходный объект. Так что, похоже, все зависит от способа сделать копию. Но тогда у вызывающей стороны нет защиты от метода, который не делает это должным образом. Я думаю, что это приемлемо - единственная другая альтернатива, кажется, состоит в том, чтобы встроить это в язык.

11 ответов

Как правило, вызывающая сторона должна сделать копию, если она обеспокоена изменениями. Если вызывающему не все равно, метод должен сделать копию, если ему нужно сделать что-то, что, как он знает, не должно сохраняться.

Звонящий Потому что иногда вы хотите внести изменения в сами объекты, а иногда - в копию.

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

(после вашего) РЕДАКТИРОВАТЬ: В этом случае ответственность за выполнение контракта лежит на вызываемом абоненте, поэтому есть два варианта:

  • Вызываемый просто не изменяет объект
  • или вызываемый объект копирует объект и впоследствии работает с копией

Мы могли бы посмотреть на рубин для руководства. Они используют! символ, указывающий, что объект изменен на месте. Таким образом, salary.add(10000) возвращает новый объект, а salary.add!(10000) возвращает исходный объект, но измененный. Вы можете использовать ту же идею в Java или PHP, используя локальное соглашение об именах.

Итак, вы хотите сделать что-то вроде

MyObject m = новый MyObject(); MyObject n = MyObjectProcessor.process(m);?

Мне кажется проще сделать что-то вроде MyObject n = MyObjectProcessor.alter(m.clone());

где понятно кто делает что кому. Можно привести аргумент, что функция класса процессора должна быть свободна от побочных эффектов, то есть она должна возвращать новый объект каждый раз, когда собирается изменить состояние, но (AFAIK) это не так последовательно следует в ОО, в отличие от функционального программирования.

Нечто подобное выше, вероятно, безвредно, если оно четко названо и задокументировано.

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

Если не изменение объекта является частью контракта метода, единственная возможность состоит в том, чтобы сделать копию внутри метода. В противном случае вы лжете своему клиенту.

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

Я думаю, что вызывающая сторона должна сделать клон, просто чтобы сделать именование проще. Вы можете назвать свой метод Foo() вместо CloneBarAndFooClone().

Для сравнения:

void RemoveZeroDollarPayments(Order order)

против

Order CloneOrderAndRemoveZeroDollarPaymentsFromClone(Order order)

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

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

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

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

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

Другие вопросы по тегам