2012-03-05 1 views
12

Скажем, у меня есть класс Foo в Java, который имеет непреложное данные:Java: метод псевдо-сеттер для неизменяемых классов

class Foo { 
    final private int x; 
    public int getX() { return this.x; } 
    final private OtherStuff otherstuff; 
    public Foo(int x, OtherStuff otherstuff) { 
     this.x = x; 
     this.otherstuff = otherstuff; 
    } 
    // lots of other stuff... 
} 

Теперь я хотел бы добавить метод утилита, которая создает «родственный» значение с идентичным состоянием, но с новым значением x. Я мог бы назвать его setX():

class Foo 
{ 
    ... 
    Foo setX(int newX) { return new Foo(newX, this.otherstuff); } 
    ... 
} 

но семантика setX() отличается от стандартной инкубационной конвенции изменяемых объектов фасоли, так или иначе это не чувствует себя хорошо.

Какое лучшее название для этого метода?

Должен ли я называть его withX() или newX() или еще чем-нибудь?


редактировать: дополнительный приоритет в моем случае: у меня есть сценарии клиенты (через JSR-223 и объектную модель я экспорт), которые можно легко получить Foo объект. Однако громоздить вызов конструкторов или создать строителей или что-то еще. Поэтому мне желательно предоставить этот метод в качестве удобства для скриптовых клиентов.

+0

Смежный вопрос: http://stackoverflow.com/questions/521893/whats-the-best-name-for-a-non-mutating-add-method-on- ан-неизменная-коллекция. (Это то же самое, что и для этого, но для 'add' вместо' setX'.) – ruakh

ответ

12

withX() звучит нормально, потому что это соглашение, используемое для некоторых моделей Builder ,

Это больше «частичного клон» или «строитель», чем «инкубатор» ...

Если посмотреть на java.lang.String (также неизменный) есть все виды методов, которые возвращают новую строку на основе на старом (подстроки, toLowerCase(), и т.д.) ...

Update: Смотрите также ответ от aioobe [deriveFoo()], который мне нравится - это, возможно, понятнее, особенно для тех, кто не знаком с шаблонами Builder ,

1

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

public Foo newFooWith(int x) { 
    return new Foo(x, other); 
} 

Альтернатива может быть вариантом конструктора копии

class Foo { 
    public Foo(Foo foo, int x) { 
     return new Foo(x, foo.getOtherStuff()); 
    } 
} 
+0

как бы «другие» были скопированы в этом случае? – DNA

+0

Я бы согласился, но частично моя проблема связана с скриптовыми клиентами, где легко получить объект Foo, но громоздкий, чтобы получить объект FooFactory. –

+0

Это не будет копировать данные других экземпляров –

12

Следуя стилю Font class в стандартном API, вы бы назвали его deriveFoo(int x).

Другим вариантом было бы создание класса-строителя, который мог бы принять объект Foo в качестве прототипа для новых объектов. В таких случаях вы обычно называете setBar() просто bar(). Это даст вам что-то вроде

Foo newFoo = new Foo.Builder(foo).x(123).build(); 
+2

Мне нравится этот подход, +1 –

+0

В первом подходе вы предлагаете, как бы вы получили 'Foo', если бы были поля' x' и 'y'' int'? –

+3

'java.awt.Font' - класс JDK 1.0, многие решения об именах этого выпуска не были хорошими. JDK 8 использует соглашение 'withFoo' в новом API' java.time', например ['java.time.YearMonth.withMonth()'] (http://download.java.net/jdk8/docs/api/java /time/YearMonth.html#withMonth-int-). – leventov

2

Я бы назвал его withX(value). В нем говорится, что это будет что-то с x = value.

Если класс было много полей, я бы боялся:

obj.withX(1).withY(2).withZ(3).withU(1)... 

Так что я бы, возможно, использовать строитель шаблон-ввести изменяемый вариант данного класса только с данными и методами для создания исходный класс с его текущим состоянием. И там я бы назвал эти методы x(), y(), z() и вернуть их this. Так это будет выглядеть:

Immutable im2 = new Mutable(im1).x(1).y(2).z(3).build(); 
+0

Подход, который иногда может быть полезен для того, чтобы методы стиля 'with' были эффективными даже тогда, когда основные элементы данных были большими и дорогими для копирования, - это добавить дополнительный уровень косвенности к основному типу абстрактного объекта и иметь этот абстрактный тип включают виртуальный метод «сглаживания». Метод 'WithXX' может затем возвратить новую оболочку, которая указывает на объект' WrapWithXX', который обычно содержит ссылку на исходный объект и значение нового свойства, если только он не обнаружил, что «цепочка» объектов была чрезмерно долго в этом случае ... – supercat

+0

... он будет называть 'Flatten()'. Метод 'Flatten' генерирует новый экземпляр базового объекта с учетом любых методов' WithXX', которые он мог бы анализировать. Вызов 'Flatten' на объект не изменил бы его * наблюдаемое * состояние, но заменило бы его реализацию на менее сложную форму. Обратите внимание, что дополнительный уровень косвенности не будет без затрат, но также не будет иметь никаких преимуществ. Например, если обнаружено, что две обертки имеют разные объекты, которые сравниваются одинаково, одна или обе оболочки могут быть изменены, чтобы указать на «канонический» экземпляр, ускоряя сравнение. – supercat