2009-07-02 2 views
3

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

Когда исходная структура была спроектирована, мы решили сохранить множество постоянных значений, определенных как статические поля в одном классе. Со временем этот список статических членов рос. Класс используется в очень многих местах нашего кода. В нашем текущем рефакторинге мы будем поднимать некоторых членов этого класса на новые рамки, но оставим других на месте. Наша головная боль заключается в извлечении членов фонда этого класса для использования в нашем новом проекте и, более конкретно, о том, как мы должны обращаться к тем извлеченным членам в нашем существующем коде.

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

перед:

public class ConstantsA { 
    public static final String CONSTANT1 = "constant.1"; 
    public static final String CONSTANT2 = "constant.2"; 
    public static final String CONSTANT3 = "constant.3"; 
} 

после:

public class ConstantsA extends ConstantsB { 
    public static final String CONSTANT1 = "constant.1"; 
} 

public class ConstantsB { 
    public static final String CONSTANT2 = "constant.2"; 
    public static final String CONSTANT3 = "constant.3"; 
} 

В нашей существующем коде отрасли, все вышеперечисленное будет доступны таким образом:

ConstantsA.CONSTANT2 

Я хотел бы запрашивать аргументы о том, является ли это «приемлемым» и/или какова наилучшая практика.

ответ

9
  • Класс с только статическими полями является запахом кода. Это не класс.

  • Некоторые люди используют интерфейсы, поэтому они могут реализовать его, чтобы легче использовать константы. Но интерфейс должен использоваться только для моделирования поведения класса. (http://pmd.sourceforge.net/rules/design.html#AvoidConstantsInterface) Использование статического импорта из Java 5 устраняет необходимость в простом постоянном использовании.

  • Являются ли ваши константы действительно строками или просто используются как строки. Если это разные варианты для некоторого типа (так называемые перечисления), вы должны использовать typesafe enumerations, используя перечисление в Java 5 или Enum, предоставленное Commons Lang. Конечно, преобразование кода для использования перечислений может быть небольшой работой.

  • Вы должны по крайней мере разделить константы на группы связанных констант в файлах с надлежащим названием. Перемещение конечных членов в IDE легко и будет обновлять все обычаи.

  • Если вы можете себе это позволить, тогда конвертируйте их в перечисления. (Подумайте о том, как использовать скрипт для этого, часто это возможно.) Иерархии классов полезны только, если существует связь между константами/перечислениями. Вы можете сохранить строки, если вам нужно, но все же думать о них как сущности, тогда расширения могут иметь смысл для некоторых (описание is-a relation). Первые перечисления могут быть простыми классами, сделанными самим собой, если сериализация не является проблемой. Перечисления всегда благоприятны из-за их типа безопасного характера и дополнительного имени, показывающего намерение или деловые/предметные вещи.

  • Если константы действительно являются константами String, используйте Properies или ResourceBundle, которые могут быть сконфигурированы текстовыми файлами. Снова вы можете выполнить сценарий рефакторинга с использованием имен констант в качестве ключей пакета ресурсов и автоматически генерировать оба файла.

+0

Повторите свою последнюю пулю: все равно будут иметь струны повсюду. Я не понимаю, почему properties.get («SYMBOL») лучше, чем Constants.SYMBOL –

+0

Вы правы, но это зависит от использования константы, которую я бы сказал. Если это текст для отображения, он принадлежит в комплекте. И тогда мы хорошие разработчики, но имена пучков в постоянные классы - рекурсия hurra –

+0

спасибо за ваш ответ. Я согласен с комментарием интерфейса (я даже не рассматривал его). Вопрос, который вы задаете, заключается в следующем: для чего используются эти константы? У нас есть множество применений - некоторые являются значениями String для отображения (например: заголовки столбцов таблицы), некоторые из них являются постоянными значениями в примитивах, третьи - постоянными ключами для использования в Maps. – akf

0

Я видел это, поставив статическую финальную строку на интерфейс, чтобы вы могли «реализовать» ее и не беспокоиться о том, что делать, когда вам нужен другой базовый класс. Это так же доступно.

В общем, перечисления довольно хороши в том, что вы пытаетесь сделать, и можете избавиться от чувства «Я не уверен», которое вы испытываете, поскольку это намерение перечислений.

+0

Когда вы реализуете интерфейс, вы делаете все члены этого интерфейса частью постоянного API вашего класса. Посмотрите на классы Swing, чтобы увидеть, почему это плохая идея. – kdgregory

+0

@kdgregory - вы не должны его реализовать. Просто используйте его так же, как класс констант, поскольку все они являются статическими значениями. – Robin

+0

Это правильно. Однако я отвечал на плакат, который прямо сказал «реализовать». – kdgregory

2

Мне это не нравится, но это, наверное, самое лучшее, что вы можете сделать прямо сейчас.

Правильный ответ заключался бы в том, чтобы разбить константы на когерентные группы, фиксируя разрывы кода по мере продвижения. В C# я буду использовать перечисления.

0

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

Вы должны быть усердными, чтобы ConstantsB содержал только константы, которые можно обобщить среди всех ваших проектов, а ConstantsA содержит только конкретные для проекта константы. Если позже вы поймете, что в ConstantsB есть константа, которую вы, кажется, слишком много переопределяете в своих подклассах, то это указывает на то, что она никогда не должна была быть вставлена ​​в ConstantsB в первую очередь.

0

Я думаю, что у вас есть хороший первый шаг. Следующим шагом будет постепенное замещение всех ссылок на ConstantsA.CONSTANT2 и ConstantsA.CONSTANT3 с ConstantsB.CONSTANT2 и ConstantsB.CONSTANT3, пока вы не сможете удалить extends.

Большинство IDE могут быть настроены для отображения предупреждения, если вы ссылаетесь на константу суперкласса через подкласс, и я бы предположил, что статические аналитические инструменты, такие как FindBugs, тоже могут это сделать.

Одна из идей, которые могли бы быть немного чище:

  1. сделать все постоянные классы интерфейсов
  2. переместить все константы из ConstantsA и назвать это что-то вроде LegacyConstants
  3. имеют LegacyConstants расширить все другие, модульные Constants интерфейсы
  4. derecate LegacyConstants

Целью было бы не иметь наследования между интерфейсами Constants. LegacyConstants было бы единственным местом, где есть наследование, оно не объявляло бы никаких констант, а когда оно больше не использовалось - когда каждый класс, который его использовал, ссылается на соответствующий интерфейс Constants - вы закончили рефакторинг ,

0

Когда вы извлекаете свои константы, ставьте ссылку на класс константу, определенную в новом классе. Здесь действительно нет необходимости создавать отношения наследования.

0

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

+0

+1 тоже верно. В настоящее время я реализую модель возможностей для некоторого программного обеспечения, которое я пишу, а вместо этого hardcoding MaxSize и MaxCount, я параметризую их через интерфейсы. Теперь мой предел будет любым целым типом данных, который я выбираю. –

2

Peter Kofler уже обсудил, как вы можете лучше организовать константы. Я расскажу, как автоматизировать переход:

Рефакторинг затмений «Inline» может автоматически заменять константы по их определению, что избавляет вас от необходимости выслеживать и изменять каждое использование вручную. Таким образом, вы бы просто изменить код:

public class ConstantsA { 
    public static final String CONSTANT1 = "constant.1"; 
    public static final String CONSTANT2 = ConstantsB.CONSTANTFOO; 
    public static final String CONSTANT3 = ConstantsB.CONSTANTBAR; 
} 

public class ConstantsB { 
    public static final String CONSTANTFOO = "constant.2"; 
    public static final String CONSTANTBAR = "constant.3"; 
} 

... а затем затмение встроенный COONSTANT2 и CONSTANT3 (в то время как все затронутые проекты проверены, если вы не можете сделать это, смотрите в рефакторинга скриптов) , и вы сделали.

Смежные вопросы