2009-09-12 3 views
3

Насколько я понимаю, геттеры/сеттеры всегда должны делать копии, чтобы защитить данные.Оборонительная копия: следует ли указывать в Javadoc?

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

b = a.getB(); 
b.setC(someValue); 

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

b = a.getB(); 
b.setC(someValue); 
a.setB(b); 

Спасибо заранее S

ответ

4

В приведенном выше примере есть хороший аргумент: поскольку A поддерживает ссылку на B, A должен следить за B, а не раздавать его, а манипулировать им от вашего имени. В противном случае вы можете утверждать, что вы нарушаете инкапсуляцию (поскольку A показывает, что она имеет ссылку на B), и в идеале объекты должны делать что-то для вас, а не экспортировать их содержимое, чтобы вы могли ими манипулировать.

Сказав все это, вышесказанное, безусловно, не является обычной практикой и часто прагматичным выбором.

Когда вы подвергаете объект через Get(), у вас есть три варианта:

  1. открывающих реальный объект
  2. сделать защитную копию
  3. выставить объект, который оборачивает оригинал, но запрещает модификацию , например вы можете обернуть исходный объект в ограниченном интерфейсе. См. (Например) Collections.unmodifiableCollection(), который обертывает исходную коллекцию (и не копирует ее), но предоставляет интерфейс, который не позволяет изменять.

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

+0

+1 для обозначения ограниченного интерфейса. Для читателей .NET ознакомьтесь с List (T) .AsReadOnly и ReadOnlyCollection (T). – TrueWill

+0

Я не знал вариант 3, и мне действительно (действительно) понравилось. Большое спасибо! – Sebastien

+0

Я часто создаю версию коллекции только для чтения и удерживаю ее как переменную-член, поэтому я могу передать ее в своих геттерах (кажется «лучше», чем повторное создание списка только для чтения). –

4

Ну, setC нарушает Law of Demeter, поэтому я не думаю, что назвал бы это лучшей практикой. («Закон» немного силен - например, он, как правило, не применяется к плавным интерфейсам.)

Это означает, что геттеры должны не всегда делают копии ИМХО. Выполнение глубокого клона может быть дорогостоящим. Существуют и другие варианты, такие как неизменяемые объекты.

И, в сущности, существуют прагматические соображения.

Но я ошибался на стороне TMI (слишком много информации) в JavaDoc.

+0

Спасибо за ваш ответ. Так что, полагая, что-то вроде «модификация возвращаемого экземпляра B будет безопасно модифицировать этот объект» в JavaDoc, было бы приемлемо? – Sebastien

+0

@Sebastien: Да, для не скопированной ссылки (хотя я могу оставить «безопасно»). ;) Лично я бы пошел с ответом Брайана Агнью и, если это возможно, использовал объект-обертку или неизменяемый. – TrueWill

-2

Вы не ошибетесь, прежде чем узнаете об этом! И тогда у вас есть реальная ошибка, а не потенциал один.

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

Это очень академичный, и у меня лично никогда не было проблем с ним. Я также сразу же отключу проверку в FindBugs (Java), например ...

И если есть проблема, кто все равно читает JavaDoc и т.п.? Кто-нибудь там?

+1

Я почтительно не согласен со всем этим сообщением, за исключением «Вы не ошибетесь, прежде чем вы это узнаете». Ключ **, если вы доверяете коду клиента **. Если ваш метод общедоступен в открытом классе, вы, как правило, не имеете контроля над будущими клиентами. – TrueWill

+1

Кто читает Джавадока? - Ну, я делаю это для одного. – Adamski

+0

Однако у меня никогда не было проблем с этим. Но вы делаете то, что вам нужно делать, справедливо. – raoulsson

3

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

Обычно я придерживаюсь такого подхода, если я пишу API, который я, вероятно, буду использовать самостоятельно или внутри своей команды разработчиков; то есть где я знаю, что «клиенты» будут хорошими гражданами!

// Immutable interface definition. 
public interface Record { 
    String getContent(); 
} 

// Mutable implementation of Record interface. 
public class MutableRecord implements Record { 
    private final String content; 

    public MutableRecord(String content) { 
    this.content = content; 
    } 

    public String getContent() { 
    return content; 
    } 

    public void setContent(String content) { 
    this.content = content; 
    } 
} 

// API that only exposes the object via its Record interface. 
public class MyApi { 
    private final MutableRecord mutableRecord; 

    public Record getRecord() { 
    return mutableRecord; 
    } 
} 
Смежные вопросы