Это улучшение для FlowTextHelper (из ответа vorrtex). Я добавил возможность добавить дополнительное дополнение между текстом и изображением и улучшить расчет строки, чтобы также учитывать отступы. Наслаждайтесь!
public class FlowTextHelper {
private static boolean mNewClassAvailable;
/* class initialization fails when this throws an exception */
static {
try {
Class.forName("android.text.style.LeadingMarginSpan$LeadingMarginSpan2");
mNewClassAvailable = true;
} catch (Exception ex) {
mNewClassAvailable = false;
}
}
public static void tryFlowText(String text, View thumbnailView, TextView messageView, Display display, int addPadding){
// There is nothing I can do for older versions, so just return
if(!mNewClassAvailable) return;
// Get height and width of the image and height of the text line
thumbnailView.measure(display.getWidth(), display.getHeight());
int height = thumbnailView.getMeasuredHeight();
int width = thumbnailView.getMeasuredWidth() + addPadding;
messageView.measure(width, height); //to allow getTotalPaddingTop
int padding = messageView.getTotalPaddingTop();
float textLineHeight = messageView.getPaint().getTextSize();
// Set the span according to the number of lines and width of the image
int lines = (int)Math.round((height - padding) / textLineHeight);
SpannableString ss = new SpannableString(text);
//For an html text you can use this line: SpannableStringBuilder ss = (SpannableStringBuilder)Html.fromHtml(text);
ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, ss.length(), 0);
messageView.setText(ss);
// Align the text with the image by removing the rule that the text is to the right of the image
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)messageView.getLayoutParams();
int[]rules = params.getRules();
rules[RelativeLayout.RIGHT_OF] = 0;
}
}
Это вполне приемлемо, возможно, даже стандарт.
(public/private) static final TYPE NAME = VALUE;
где TYPE
- это тип, NAME
- это имя во всех заглавных буквах с подчеркиванием для пробелов, а VALUE
- это постоянное значение;
Я настоятельно рекомендую НЕ ставить ваши константы в своих собственных классах или интерфейсах.
В качестве примечания: переменные, объявленные как final и изменяемые, все еще могут быть изменены; однако переменная никогда не может указывать на другой объект.
Например:
public static final Point ORIGIN = new Point(0,0);
public static void main(String[] args){
ORIGIN.x = 3;
}
Это законно, и тогда ORIGIN
будет точкой в (3, 0).
Один из способов сделать это - создать класс «Global» с постоянными значениями и выполнить статический импорт в классы, которым требуется доступ к константе.
В чем разница
1.
public interface MyGlobalConstants {
public static final int TIMEOUT_IN_SECS = 25;
}
2.
public class MyGlobalConstants {
private MyGlobalConstants () {} // Prevents instantiation
public static final int TIMEOUT_IN_SECS = 25;
}
и используя MyGlobalConstants.TIMEOUT_IN_SECS
везде, где нам нужна эта константа. Я думаю, что оба одинаковы.
Один общий класс констант - плохая идея. Константы должны быть сгруппированы вместе с классом, с которым они наиболее логически связаны.
Вместо того, чтобы использовать какие-либо переменные (особенно перечисления), я бы предложил использовать методы. Создайте метод с тем же именем, что и у переменной, и пусть он возвращает значение, которое вы присвоили переменной. Теперь удалите переменную и замените все ссылки на нее вызовами только что созданного вами метода. Если вы чувствуете, что константа достаточно универсальна, и вам не нужно создавать экземпляр класса просто для ее использования, тогда сделайте метод константы методом класса.
Я согласен с тем, что большинство говорят, лучше всего использовать перечисления при работе с набором констант. Однако, если вы программируете на Android, есть лучшее решение: IntDef Annotation .
@Retention(SOURCE)
@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST,NAVIGATION_MODE_TABS})
public @interface NavigationMode {}
public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;
...
public abstract void setNavigationMode(@NavigationMode int mode);
@NavigationMode
public abstract int getNavigationMode();
Аннотация IntDef превосходит перечисления одним простым способом, она занимает значительно меньше места, поскольку является просто маркером времени компиляции. Он не является классом и не имеет свойства автоматического преобразования строк.
static final
- это мое предпочтение, я бы использовал enum
, только если бы элемент был действительно перечислимым.
FWIW, значение тайм-аута в секундах, вероятно, должно быть параметром конфигурации (считанным из файла свойств или путем внедрения, как в Spring), а не константой.
Для констант Enum - лучший выбор, ИМХО. Вот пример
открытый класс myClass {
public enum myEnum {
Option1("String1", 2),
Option2("String2", 2)
;
String str;
int i;
myEnum(String str1, int i1) { this.str = str1 ; this.i1 = i }
}
Константа любого типа может быть объявлена путем создания неизменного свойства внутри класса (то есть переменной-члена с модификатором final
). Обычно предоставляются также модификаторы static
и public
.
public class OfficePrinter {
public static final String STATE = "Ready";
}
Существует множество приложений, в которых значение константы указывает выбор из n-кортежей (например, перечисление ) из вариантов. В нашем примере мы можем выбрать определение перечисляемого типа, которое будет ограничивать возможные присвоенные значения (т.е. улучшена безопасность типов ):
public class OfficePrinter {
public enum PrinterState { Ready, PCLoadLetter, OutOfToner, Offline };
public static final PrinterState STATE = PrinterState.Ready;
}
Я бы не назвал класс таким же (кроме регистра), как константа ... У меня был бы как минимум один класс «Настройки», или «Значения», или «Константы», где все константы будет жить. Если бы у меня их было много, я бы сгруппировал их в классы логических констант (UserSettings, AppSettings и т. Д.)
. Я предпочитаю использовать геттеры, а не константы. Эти геттеры могут возвращать постоянные значения, например, public int getMaxConnections() {return 10;}
, но все, что нуждается в константе, будет проходить через геттер.
Одним из преимуществ является то, что если ваша программа перерастает константу - вы обнаружите, что она должна быть настраиваемой - вы можете просто изменить способ получения константой.
Другое преимущество заключается в том, что для изменения константы не нужно перекомпилировать все, что ее использует. Когда вы ссылаетесь на статическое конечное поле, значение этой константы компилируется в любой байт-код, который ссылается на него.
Для хорошего объектно-ориентированного проектирования не нужно много общедоступных констант. Большинство констант должны быть инкапсулированы в классе, который нуждается в них для выполнения своей работы.
Я использую следующий подход:
public final class Constants {
public final class File {
public static final int MIN_ROWS = 1;
public static final int MAX_ROWS = 1000;
private File() {}
}
public final class DB {
public static final String name = "oups";
public final class Connection {
public static final String URL = "jdbc:tra-ta-ta";
public static final String USER = "testUser";
public static final String PASSWORD = "testPassword";
private Connection() {}
}
private DB() {}
}
private Constants() {}
}
Чем, например, я использую Constants.DB.Connection.URL
для получения константы. Это выглядит более «объектно-ориентированным», как для меня.
Просто избегайте использования интерфейса:
public interface MyConstants {
String CONSTANT_ONE = "foo";
}
public class NeddsConstant implements MyConstants {
}
Это заманчиво, но нарушает инкапсуляцию и размывает различия в определениях классов.
Это правильный путь.
Как правило, константы не хранятся в отдельных классах «констант», потому что их нельзя обнаружить. Если константа относится к текущему классу, их сохранение помогает следующему разработчику.
Это ПЛОХАЯ ПРАКТИКА , использующая интерфейсы только для хранения констант (названная константным шаблоном интерфейса Джоша Блоха). Вот что советует Джош:
Если константы сильно привязаны к существующему классу или интерфейсу, вы должны добавить их в класс или интерфейс. Например, все классы числовых примитивов в штучной упаковке, такие как Integer и Double, экспортируют константы MIN_VALUE и MAX_VALUE. Если константы лучше всего рассматривать как члены перечислимого типа, вы должны экспортировать их с типом enum . В противном случае вы должны экспортировать константы с неинстансируемым служебным классом.
Пример:
// Constant utility class
package com.effectivejava.science;
public class PhysicalConstants {
private PhysicalConstants() { } // Prevents instantiation
public static final double AVOGADROS_NUMBER = 6.02214199e23;
public static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
public static final double ELECTRON_MASS = 9.10938188e-31;
}
Об соглашении об именах:
По соглашению, такие поля имеют имена, состоящие из заглавных букв со словами, разделенными подчеркиванием. Крайне важно, чтобы эти поля содержали либо примитивные значения, либо ссылки на неизменяемые объекты.
В Effective Java (2-е издание) рекомендуется использовать перечисления вместо статических целочисленных значений для констант.
Здесь есть хорошая рецензия на перечисления в Java: http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html
Обратите внимание, что в конце этой статьи поставлен вопрос:
Итак, когда вы должны использовать перечисления?
С ответом:
Каждый раз, когда вам нужен фиксированный набор констант
Основываясь на комментариях выше, я думаю, что это хороший подход для изменения старомодного глобального константного класса (имеющего открытые статические конечные переменные) на его эквивалентный перечислению таким образом:
public class Constants {
private Constants() {
throw new AssertionError();
}
public interface ConstantType {}
public enum StringConstant implements ConstantType {
DB_HOST("localhost");
// other String constants come here
private String value;
private StringConstant(String value) {
this.value = value;
}
public String value() {
return value;
}
}
public enum IntConstant implements ConstantType {
DB_PORT(3128),
MAX_PAGE_SIZE(100);
// other int constants come here
private int value;
private IntConstant(int value) {
this.value = value;
}
public int value() {
return value;
}
}
public enum SimpleConstant implements ConstantType {
STATE_INIT,
STATE_START,
STATE_END;
}
}
Так что тогда я могу отнести их к как:
Constants.StringConstant.DB_HOST
Чтобы сделать еще один шаг вперед, вы можете поместить глобально используемые константы в интерфейс, чтобы их можно было использовать в масштабе всей системы. Э.Г.
public interface MyGlobalConstants {
public static final int TIMEOUT_IN_SECS = 25;
}
Но не воплощайте это в жизнь. Просто обратитесь к ним напрямую в коде через полное имя класса.
Я согласен, что использование интерфейса - это не тот путь. Избегание этого паттерна даже имеет свой собственный элемент (# 18) в «Эффективной Java» Блоха .
Аргумент, который Блох делает против шаблона константного интерфейса, заключается в том, что использование констант - это деталь реализации, но реализация интерфейса для их использования раскрывает эту деталь реализации в экспортированном API.
Шаблон public|private static final TYPE NAME = VALUE;
является хорошим способом объявления константы. Лично я думаю, что лучше избегать создания отдельного класса для размещения всех ваших констант, но я никогда не видел причины не делать этого, кроме личных предпочтений и стиля.
Если ваши константы могут быть смоделированы как перечисление, рассмотрите структуру enum , доступную в версии 1.5 или более поздней.
Если вы используете версию более раннюю, чем 1.5, вы все равно можете извлечь перечисления, безопасные для типов, используя обычные классы Java. (Подробнее об этом см. на этом сайте ).
Я использую static final
для объявления констант и перехожу на нотацию имен ALL_CAPS. Я видел довольно много реальных случаев, когда все константы объединялись в интерфейс. Несколько постов справедливо назвали это плохой практикой, в первую очередь потому, что интерфейс не для этого. Интерфейс должен обеспечивать выполнение контракта и не должен быть местом для помещения несвязанных констант. Объединение его в класс, экземпляр которого не может быть создан (с помощью частного конструктора), тоже хорошо, если семантика констант не принадлежит конкретному классу ( эс). Я всегда помещаю в класс константу, с которой она больше всего связана, потому что это имеет смысл и также легко поддерживается.
Перечисления являются хорошим выбором для представления диапазона значений, но если вы храните автономные константы с акцентом на абсолютное значение (например, TIMEOUT = 100 мс), вы можете просто пойти на подход static final
.
Создание статических конечных констант в отдельном классе может привести к неприятностям. Компилятор Java фактически оптимизирует это и поместит фактическое значение константы в любой класс, который ссылается на нее.
Если позже вы измените класс 'Constants' и не будете жестко перекомпилировать другие классы, которые ссылаются на этот класс, вы получите комбинацию старых и новых значений.
Вместо того, чтобы думать о них как о константах, думайте о них как о параметрах конфигурации и создайте класс для управления ими. Пусть значения не являются окончательными, и даже подумайте об использовании геттеров. В будущем, когда вы определите, что некоторые из этих параметров должны настраиваться пользователем или администратором, это будет гораздо проще.
Я бы настоятельно не рекомендовал иметь один класс констант. В то время это может показаться хорошей идеей, но когда разработчики отказываются документировать константы, и класс увеличивается до 500 констант, которые вообще не связаны друг с другом (относящихся к совершенно другим аспектам приложения), это обычно превращается в файл констант, который полностью не читается. Вместо этого:
Ошибка номер один, которую вы можете совершить, - это создание глобально доступного класса с общим именем, например, Constants. Это просто завален мусором, и вы теряете все возможности выяснить, какая часть вашей системы использует эти константы.
Вместо этого константы должны входить в класс, который «владеет» ими. У вас есть константа под названием TIMEOUT? Вероятно, он должен перейти в ваш класс Communications () или Connection (). MAX_BAD_LOGINS_PER_HOUR? Заходит в пользователя (). И так далее.
Другим возможным использованием являются файлы .properties Java, когда «константы» могут быть определены во время выполнения, но их нелегко изменить пользователем. Вы можете упаковать их в ваши .jars и ссылаться на них с помощью Class resourceLoader.
Есть определенное количество мнений, чтобы ответить на это. Начнем с того, что константы в Java обычно объявляются как public, static и final. Ниже приведены причины:
public, so that they are accessible from everywhere
static, so that they can be accessed without any instance. Since they are constants it
makes little sense to duplicate them for every object.
final, since they should not be allowed to change
Я бы никогда не использовал интерфейс для объекта доступа / объекта CONSTANTS просто потому, что обычно ожидается, что интерфейсы будут реализованы. Разве это не выглядит смешно:
String myConstant = IMyInterface.CONSTANTX;
Вместо этого я бы выбрал несколько разных способов, основанных на некоторых небольших компромиссах, и поэтому это зависит от того, что вам нужно:
1. Use a regular enum with a default/private constructor. Most people would define
constants this way, IMHO.
- drawback: cannot effectively Javadoc each constant member
- advantage: var members are implicitly public, static, and final
- advantage: type-safe
- provides "a limited constructor" in a special way that only takes args which match
predefined 'public static final' keys, thus limiting what you can pass to the
constructor
2. Use a altered enum WITHOUT a constructor, having all variables defined with
prefixed 'public static final' .
- looks funny just having a floating semi-colon in the code
- advantage: you can JavaDoc each variable with an explanation
- drawback: you still have to put explicit 'public static final' before each variable
- drawback: not type-safe
- no 'limited constructor'
3. Use a Class with a private constructor:
- advantage: you can JavaDoc each variable with an explanation
- drawback: you have to put explicit 'public static final' before each variable
- you have the option of having a constructor to create an instance
of the class if you want to provide additional functions related
to your constants
(or just keep the constructor private)
- drawback: not type-safe
4. Using interface:
- advantage: you can JavaDoc each variable with an explanation
- advantage: var members are implicitly 'public static final'
- you are able to define default interface methods if you want to provide additional
functions related to your constants (only if you implement the interface)
- drawback: not type-safe
Каков наилучший способ реализации констант в Java?
Один из подходов, который нам действительно следует избегать : использование интерфейсов для определения констант.
[1 156]
Создание интерфейса, специально предназначенного для объявления констант, на самом деле является наихудшей вещью: оно побеждает причину, по которой интерфейсы были разработаны: определение метода (ов) контракта.
Даже если интерфейс уже существует для удовлетворения конкретной потребности, объявление констант в них не имеет смысла, так как константы не должны составлять часть API и контракта, предоставляемого клиентским классам.
Чтобы упростить, мы имеем в целом 4 действительных подхода .
С полем static final String/Integer
:
С Java 5 enum
:
TLDR: какой путь лучше и где найти константы?
В большинстве случаев путь enum, вероятно, лучше, чем static final String/Integer
путь и лично я считаю, что путь static final String/Integer
следует использовать только в том случае, если у нас есть веские причины не использовать перечисления.
И о том, где мы должны объявлять постоянные значения, идея состоит в том, чтобы искать, существует ли единственный существующий класс, который обладает определенной и сильной функциональной связностью с постоянными значениями. Если мы найдем такой класс, мы должны использовать его в качестве держателя констант . В противном случае константа не должна быть связана ни с одним конкретным классом.
static final String
/ static final Integer
против enum
Использование перечислений действительно является способом, который настоятельно рекомендуется.
Перечисления имеют большое преимущество перед постоянным полем String
или Integer
.
Они устанавливают более сильное ограничение компиляции. Если вы определяете метод, который принимает перечисление в качестве параметра, вы можете передать только значение перечисления, определенное в классе перечисления (или ноль).
С помощью String и Integer вы можете заменить их любыми значениями совместимого типа, и компиляция будет в порядке, даже если значение не является определенной константой в полях static final String
/ static final Integer
.
Например, ниже двух констант, определенных в классе как поля static final String
:
public class MyClass{
public static final String ONE_CONSTANT = "value";
public static final String ANOTHER_CONSTANT = "other value";
. . .
}
Здесь метод, который предполагает использовать одну из этих констант в качестве параметра:
public void process(String constantExpected){
...
}
Вы можете вызвать его следующим образом:
process(MyClass.ONE_CONSTANT);
или
process(MyClass.ANOTHER_CONSTANT);
Но никакое ограничение компиляции не мешает вам вызывать его таким образом:
process("a not defined constant value");
У вас будет ошибка только во время выполнения и только в том случае, если вы будете одновременно проверять переданное значение.
При использовании enum проверки не требуются, поскольку клиент может передать только значение enum в параметре enum.
Например, здесь два значения, определенные в классе enum (поэтому постоянные из коробки):
public enum MyEnum {
ONE_CONSTANT("value"), ANOTHER_CONSTANT(" another value");
private String value;
MyEnum(String value) {
this.value = value;
}
...
}
Здесь метод, который ожидает иметь одно из этих значений enum в качестве параметра:
public void process(MyEnum myEnum){
...
}
Вы можете вызвать его следующим образом:
process(MyEnum.ONE_CONSTANT);
или
process(MyEnum.ANOTHER_CONSTANT);
Но компиляция никогда не позволит вам вызвать его таким образом:
process("a not defined constant value");
Где мы должны объявлять константы?
Если ваше приложение содержит единственный существующий класс, который обладает специфической и сильной функциональной связностью с постоянными значениями, 1) и 2) кажутся более интуитивными.
Как правило, это облегчает использование констант, если они объявлены в главном классе, который ими манипулирует, или у которого есть имя, и очень естественно догадываться, что мы найдем его внутри.
Например, в библиотеке JDK экспоненциальные значения и значения констант pi объявляются в классе, который объявляет не только объявления констант (java.lang.Math
).
public final class Math {
...
public static final double E = 2.7182818284590452354;
public static final double PI = 3.14159265358979323846;
...
}
Клиенты, использующие математические функции, часто полагаются на класс Math
. Таким образом, они могут легко найти константы и могут также вспомнить, где E
и PI
определены очень естественным образом.
Если ваше приложение не содержит существующего класса, который имеет очень специфическую и сильную функциональную сплоченность с постоянными значениями, варианты 1 и 2 вариантов выглядят более интуитивно.
Как правило, это не облегчает использование констант, если они объявлены в одном классе, который ими манипулирует, в то время как у нас есть также 3 или 4 других класса, которые манипулируют ими так же, как и ни один из этих классов не кажется более естественным, чем другие. постоянные значения хоста.
Здесь определение пользовательского класса для хранения только постоянных значений имеет смысл.
Например, в библиотеке JDK перечисление java.util.concurrent.TimeUnit
не объявлено в определенном классе, поскольку на самом деле не существует одного и только одного конкретного класса JDK, который представляется наиболее интуитивным для его хранения:
public enum TimeUnit {
NANOSECONDS {
.....
},
MICROSECONDS {
.....
},
MILLISECONDS {
.....
},
SECONDS {
.....
},
.....
}
Многие классы, объявленные в java.util.concurrent
, используют их: BlockingQueue
, ArrayBlockingQueue<E>
, CompletableFuture
, ExecutorService
, ... и на самом деле ни один из них не кажется более подходящим для хранения перечисления.
Не имеет значения, каковы были первоначальные намерения и ментальные состояния отцов-основателей, которые разработали Конституцию Соединенных Штатов. Мы могли бы обсудить первоначальные намерения отцов-основателей, но все, что меня волнует, - это письменные заявления Конституции США. И каждый гражданин США несет ответственность за использование письменного литературного фундаментализма, а не неписаных оснований Конституции США.
Точно так же мне все равно, какие «оригинальные» намерения основатели платформы Java и языка программирования имели для интерфейса. Меня волнуют эффективные функции, предоставляемые спецификацией Java, и я намерен использовать эти функции в полной мере, чтобы помочь мне выполнить основные законы ответственного программирования. Мне все равно, если меня воспринимают как «нарушающее намерение для интерфейсов». Меня не волнует, что Гослинг или кто-то другой Блох говорит о «правильном способе использования Java», если только то, что они говорят, не нарушает мою потребность в ЭФФЕКТИВНОМ выполнении основ.
Неважно, как ваша модель данных размещается или передается. Используете ли вы интерфейсы, перечисления или другие, реляционные или не-SQL, если вы не понимаете необходимости и процесса нормализации модели данных.
Сначала мы должны определить и нормализовать модель данных набора процессов. И когда у нас есть связная модель данных, ТОЛЬКО тогда мы можем использовать поток процессов его компонентов, чтобы определить функциональное поведение и блоки процессов в области или области приложений. И только тогда мы можем определить API каждого функционального процесса.
Даже аспекты нормализации данных, предложенные EF Codd, в настоящее время подвергаются серьезным и серьезным испытаниям. например его заявление о 1NF было подвергнуто критике как неоднозначное, смещенное и чрезмерно упрощенное, как и остальные его заявления, особенно в связи с появлением современных услуг передачи данных, технологий репо и передачи. ИМО, заявления EF Codd должны быть полностью исключены, и должен быть разработан новый набор более математически правдоподобных утверждений.
Ярким недостатком Э. Ф. Кодда и причиной его несоответствия эффективному человеческому пониманию является его вера в то, что воспринимаемые человеком многомерные данные изменяемого измерения могут быть эффективно восприняты посредством набора кусочных двумерных отображений.
То, что Э. Ф. Кодд не смог выразить.
В каждой когерентной модели данных, это последовательный градуированный порядок достижения согласованности модели данных.