Java: постоянные преобразования в неизменные - PullRequest
1 голос
/ 11 ноября 2008

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

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


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

public final class LOWERCASE {

    private LOWERCASE() {}

    public static String string( final String STRING ) {

      return STRING.toLowerCase();
    }
}

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

String lowercaseString = LOWERCASE.string( targetString );

Который я нахожу очень читабельным.


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

Ответы [ 5 ]

4 голосов
/ 11 ноября 2008

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

String lowerCaseString = StringUtils.lowercase (targetString);

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

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

1 голос
/ 11 ноября 2008

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 method s, как описано @kgiannakakis, вполне подходит.

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

1 голос
/ 11 ноября 2008

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

  • String.substring (INT)
  • BigDecimal.movePointLeft (INT)

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

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

1 голос
/ 11 ноября 2008

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

Ex:

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 Class.

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

1 голос
/ 11 ноября 2008

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

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

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

...