Melhores práticas de uso de sinalizadores no método Java
Pergunta
Qual é a prática recomendada para especificar sinalizadores em um método Java?
Já vi SWT usando int como bitfields, como:
(exemplo parcialmente de "Effective Java, 2nd Ed." página 159):
public class Text {
public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
void printText(String text, int flags) {
}
}
e sua chamada de cliente se parece com:
printText("hello", Text.STYLE_BOLD | Text.STYLE_ITALIC);
..mas isso é desencorajado, pois você pode misturar sinalizadores (valores int) de classes diferentes sem nenhuma verificação do compilador.
No mesmo livro ("Effective Java"), vejo o uso de EnumSet, mas então sua chamada de usuário se torna:
printText("hello", EnumSet.of(Style.Bold, Style.ITALIC));
Acho isso um pouco prolixo e prefiro a elegância do SWT.
Existe alguma outra alternativa ou são basicamente os dois sabores que você deve escolher?
Solução
Acho que você bateu em uma parede.Não vejo outra opção.Java é detalhado, isso é um fato.Em situações como essa costumo adicionar uma variável local para tornar o código mais legível.Você consegue fazer isso,
EnumSet<Style> styles = EnumSet.of(Style.Bold, Style.ITALIC);
printText("hello", styles);
Outras dicas
Se você quiser sinalizadores de estilo de bit, o Java os agrupa em um BitSet
.Já existe há muito tempo, mas poucas pessoas se preocupam em usá-lo (preferindo incorporar o tratamento de bits no estilo C em ints).
A API do BitSet pode ser encontrada aqui.
Juntamente com algumas entradas estáticas bem escolhidas, ele funciona muito bem até você começar a verificar e definir vários bits em uma única passagem.
Aconselho que você vá com o EnumSet
abordagem.
EnumSet<Style> styles = EnumSet.of(Style.Bold, Style.Italic);
Esta abordagem fornece melhor segurança de tipo e Style
ser um enum terá recursos OO completos.
Resposta tardia para quem se deparar com isso.Aqui está uma maneira de reduzir a memória e ter uma boa enumeração como a API:
public static class MyFlag {
public static final MyFlag A = new MyFlag(1<<0);
public static final MyFlag B = new MyFlag(1<<1);
public static final MyFlag C = new MyFlag(1<<2);
public static final MyFlag ALL = A.and(B).and(C);
private final int flag;
private MyFlag(int flag){
this.flag = flag;
}
public MyFlag and(MyFlag limit){
return new MyFlag(flag & limit.flag);
}
public MyFlag not(MyFlag limit){
return new MyFlag(flag | ~limit.flag);
}
public boolean isSet(MyFlag limit){
if(limit ==null){
return false;
}
return (this.flag & limit.flag) != 0;
}
}
método:
public void doFoo(MyFlag flag){
if(MyFlag.A.isSet(flag)){
....
}
if(MyFlag.C.isSet(flag)){
....
}
}
chamar:
x.doFoo(MyFlag.A.and(MyFlag.C));
Se você tiver apenas um número limitado de métodos que usarão um conjunto de estilos (como printText
, no seu exemplo), você pode ajustar sua assinatura para obter um número variável de parâmetros de estilo:
void printText(String text, Style... flags) {
EnumSet<Style> style = logicalOr(flags); // see comment below
...
}
E então suas chamadas ficam muito próximas da rota do sinalizador não digitado (int):
printText("hello", Style.BOLD, Style.ITALIC);
Infelizmente, não há EnumSet.of(E... )
fábrica, apenas EnumSet.of(E first, E... more)
, então você precisará de um genérico logicalOr
método para dividir sua matriz nos primeiros pedaços + restantes. Deixado como exercício para o leitor =).