Джава:Неизменяемые преобразования в неизменяемые

StackOverflow https://stackoverflow.com/questions/280706

Вопрос

Я думал о том, как обеспечить синтаксический сахар для структуры, над которой я работаю.Я хочу иметь дело исключительно с объектами Imitable.

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


В качестве примера использования строки:

public final class LOWERCASE {

    private LOWERCASE() {}

    public static String string( final String STRING ) {

      return STRING.toLowerCase();
    }
}

Поэтому из этого примера я мог бы написать:

String lowercaseString = LOWERCASE.string( targetString );

Что я считаю очень читабельным.


Есть ли какие-либо оговорки против такого подхода?

Это было полезно?

Решение

Я не думаю, что создавать по одному классу для каждого метода — хорошая идея.Вместо этого вы можете создать класс только статических методов с именем, например, StringUtils, и реализовать эти методы.Таким образом, вы бы позвонили:

String lowCaseString = StringUtils.lowercase(targetString);

Это также предложит вам помощь IntelliSense во время набора текста.В противном случае список ваших классов будет слишком большим.Даже в этом простом примере вам следует реализовать более одного класса в нижнем регистре, чтобы можно было учитывать обстоятельства, при которых необходимо учитывать CulutureInfo.

Я не думаю, что это каким-либо образом нарушает принципы ОО или это плохой дизайн.В других языках, например в Ruby, вы можете добавлять свои методы непосредственно в класс String.Методы, которые заканчиваются на !обозначают, что исходный объект изменен.Все остальные методы возвращают измененную копию.Фреймворк Ruby on Rails добавляет некоторые методы в класс String, и ведутся споры о том, хороший ли это метод или нет.Хотя это определенно удобно.

Другие советы

Обычно для неизменяемых объектов у меня есть метод, возвращающий измененную версию объекта.Итак, если у вас есть неизменяемая коллекция, она может иметь метод sort(), который возвращает новую отсортированную коллекцию.Однако в вашем примере String это невозможно, поскольку вы не можете коснуться класса String.

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

Серия Эрика Липперта о неизменяемых объектах в C# кстати, неплохо.

По моему мнению, единственным смыслом такого фабричного класса было бы то, что класс предоставлял бы разные виды неизменяемых объектов.

Бывший:

public final class Lowercase {

  private Lowercase() {}

  public static String string( final String string ) {

   return new String( string.toLowerCase() );
  }

  public static Foo foo( final Foo f ) {
     boolean isLowerCase = true;
     return new Foo(f, isLowerCase );
  }
}

В противном случае вам следует реализовать свой метод в самом неизменяемом классе, например toLowerCase() в классе String.

В любом случае, я не думаю, что это нарушает какой-либо объектно-ориентированный принцип.

Я согласен с Эриком.Неизменяемые объекты всегда должны иметь метод, возвращающий измененную версию объекта, а не статический метод.В JDK также есть примеры:

  • String.subString(целое)
  • BigDecimal.movePointLeft(целое)

Преимущество такого подхода заключается в том, что вам не нужно передавать экземпляр, который вы хотите изменить, в качестве аргумента метода.Для таких классов, как String или Integer, я бы предпочел использовать класс-оболочку.Затем вы можете контролировать, когда будет создан такой объект (конструктором класса-оболочки или одним из методов класса).Если бы вы использовали класс Integer, контролировать его было бы гораздо сложнее, поскольку каждый мог бы создать его экземпляр.

С другой стороны, ваш пример относится к некоторым служебным классам, таким как StringUtils пакета apache commons-lang.Просто взгляните на это, я думаю, вы хотели создать что-то подобное.(Не изобретайте велосипед)

new String() это запах кода - это почти всегда ненужно из-за неизменность из String.Когда String экземпляр имеет значение, этот экземпляр никогда и никогда не будет иметь другое значение.

В следующем методе new String() является излишним:

public static String string( final String string ) {
    return new String( string.toLowerCase() );
}

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

Вот небольшой скрипт Groovy, демонстрирующий эту концепцию (надеюсь — обратите внимание, это Java в рамках языка сценариев):

String a = 'CamelCase'
String b = a.toLowerCase()

println "a='${a}'"
println "b='${b}'"

производство

a='CamelCase'
b='camelcase'

Обратите внимание, что a не изменился - неизменяем; b это новый String ценить.

То же самое верно для BigDecimal.movePointLeft() или любой другой метод на BigDecimal который возвращает BigDecimal - это новые экземпляры, исходный экземпляр остается неизменным.

Хорошо, теперь ответим на ваш вопрос:

Наличие набора операций для Strings которые выполняют полезную функцию в вашем приложении, — хорошая идея.Использование фабрики, вероятно, не требуется для чего-то вроде String, но может относиться к другому неизменяемому классу, создание которого требует некоторых усилий.

В случае, когда невозможно расширить базовый класс, например String, а static methods, как описал @kgiannakakis, в порядке.

В противном случае, если «неизменяемый» класс является частью приложения, где у вас есть доступ к объявлению/определению класса, методы, возвращающие новый экземпляр, в модели BigDecimal, String, и т. д. было бы предпочтительнее.По сути, это то, что сказали @Erik Hesselink , @Bruno Conde и @reallyinsane.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top