Ho una classe Product che ha tra l'altro un attributo Ean13 che incapsula un codice EAN13 .
Ecco un prototipo della classe Product:
@Entity
@Table(name = "tb_produtos")
public class Product implements Serializable {
public Product() {
}
@Id
@GeneratedValue
private Integer id;
@ManyToOne
@JoinColumn(name = "ID_FABRICANTE")
private Manufacturer manufacturer;
@Column(name = "DESCRICAO")
private String description;
@Column(name = "URL")
private String url;
@Embedded
private Ean13 ean;
@Transient
private Keywords keywords;
... getters and setters.
}
Inizialmente, implemento la classe EAN come segue:
@Embeddable
public class Ean13 {
private static final RuntimeException notValidEanException = new RuntimeException("NOT VALID EAN CODE");
@Column(name = "ean_code", nullable = true, length = 13)
private String code;
public Ean13() {
}
public Ean13(String code) {
validate(code);
this.code = code;
}
private void validate(String code) {
if (code == null || code.length() != 13) {
throw notValidEanException;
}
if (!CharMatcher.DIGIT.matchesAllOf(code)) {
throw notValidEanException;
}
String codeWithoutVd = code.substring(0, 12);
int pretendVd = Integer.valueOf(code.substring(12, 13));
int e = sumEven(codeWithoutVd);
int o = sumOdd(codeWithoutVd);
int me = o * 3;
int s = me + e;
int dv = getEanVd(s);
if (!(pretendVd == dv)) {
throw notValidEanException;
}
}
private int getEanVd(int s) {
return 10 - (s % 10);
}
//mover estes metodos para outra classe.
//TODO: Java 8.
private int sumEven(String code) {
int sum = 0;
for (int i = 0; i < code.length(); i++) {
if (isEven(i)) {
sum += Character.getNumericValue(code.charAt(i));
}
}
return sum;
}
private int sumOdd(String code) {
int sum = 0;
for (int i = 0; i < code.length(); i++) {
if (!isEven(i)) {
sum += Character.getNumericValue(code.charAt(i));
}
}
return sum;
}
private boolean isEven(int i) {
return i % 2 == 0;
}
@Override
public String toString() {
return code;
}
}
Come puoi vedere, il modo in cui implemento il costruttore genera una RuntimeException quando l'oggetto viene istanziato con un codice non valido.
Una spiegazione sulla classe del prodotto:
In questo caso specifico userò questa classe per conservare informazioni sui prodotti in un'applicazione crawler, che eseguono la scansione di alcuni siti Web e raccolgono informazioni su questi prodotti. Una di queste informazioni è il codice EAN. In questo caso il prodotto e l'oggetto EAN effettueranno un'istanza in una classe di servizio. Alcuni codici EAN che le applicazioni catturano dal sito Web non sono codici EAN validi (può essere un altro codice o qualche altra stringa). All'inizio voglio salvare questo prodotto senza alcun codice fino a quando non ho creato un modo per memorizzarlo. Quindi all'inizio devo convalidare questo codice EAN prima di persisterlo.
Come puoi vedere nel codice sopra, implemento la convalida nel metodo 'validate' che viene chiamato dal costruttore. Nel modo in cui viene implementato, la convalida funzionerà più o meno come segue:
Product p = new Product ();
try {
Ean13 e = new Ean13("somecode");
p.setEan13 (e);
} catch (InvalidEanCodeRuntimeException e) {
//Log invalid ean code and product information.
}
persist(p);
Ma ci sarebbero altri modi per convalidarlo.
Posso rendere implementare un metodo pubblico nella classe Ean13 come segue:
Product p = new Product ();
Ean13 ean = new Ean13 ("somecode");
if (ean.isValidEan ()){
p.setEan13(ean);
}
o potrebbe ancora essere eseguito come segue, inserendo la convalida in una classe di utilità esterna:
Product p = new Product ();
String somecode = "somecode";
if (CodeUtil.isValidEan13 (somecode)){
p.setEan13(new Ean13(somecode));
}
Certamente ci sono molti altri modi per implementarlo. Ma vorrei implementare questa convalida nel modo più corretto, pulito ed elegante possibile e / o promuovere una discussione sui modi per implementare questo tipo di convalida.