Eu estava escrevendo uma classe em javafx onde eu tinha duas propriedades que eram vinculadas bidirecionalmente e estava testando alguns casos extremos. Descobri que se você alterasse o valor de uma das propriedades de dentro de um ouvinte de invalidação, isso fazia com que as propriedades ficassem fora de sincronia. Aqui está um pequeno exemplo:
static class A {
IntegerProperty x = new SimpleIntegerProperty(this, "x", 0);
IntegerProperty y = new SimpleIntegerProperty(this, "y", 0);
A() {
x.bindBidirectional(y);
}
}
static void testA() {
A a = new A();
a.x.addListener( o -> { //InvalidationListener
if (a.x.get() < 0) a.x.set(0);
});
// after y is set to a negative value, x and y hold different values
// until either y is set to a value that's >= 0 or x is set to any value
a.y.set(-2);
System.out.println(a.x.get());
System.out.println(a.y.get());
}
Saída:
0
-2
Eu estava assumindo que ao usar uma ligação bidirecional, alterar uma propriedade sempre faria com que a outra fosse atualizada. Parece raro (e possivelmente imprudente) que alguém escrevesse um ouvinte de invalidação como esse, mas estou pensando defensivamente aqui. Se pelo menos uma dessas propriedades fosse exposta, não quero que seja possível quebrar nenhuma invariante da minha classe. Eu estava pensando que há três explicações possíveis aqui:
O contrato em ligações bidirecionais não é que elas sempre estejam em sincronia (ou elas mantêm o mesmo valor ou são marcadas como inválidas), é apenas em uma base de melhor esforço. Assim, invariantes de classe não devem ser baseadas nesse fato.
Alterar o valor dentro de um ouvinte de invalidação quebra a pré-condição de vínculos bidirecionais e deve ser evitado. Caso contrário, eles estão sempre em sincronia. Assim, você pode basear uma invariante de classe nesse fato, já que é razoável exigir que os clientes não escrevam ouvintes de invalidação assim.
É um bug e eles realmente devem ser sincronizados, não importa o que aconteça. Acho que se isso fosse verdade, você poderia facilmente produzir um loop infinito de notificações de alteração (como adicionar um ouvinte de invalidação a y que sempre define o valor como < 0). Então, caberia ao cliente evitar tais casos.
Alguma dessas explicações está próxima da verdade ou estou esquecendo de algo mais aqui? Além disso, eu gostaria de saber se existe algum outro tipo de operação bind que leve esses tipos de situações em consideração.