Как наблюдать, когда пользовательский объект изменяется даже после его расширения

Итак, скажем, у меня есть пользовательский объект в java:

   public class TestObject {

    private int value = 0;

        public TestObject(int value) {
                this.value = value;
        }

    public void increaseValue() {
        value++;
    }

    }

Теперь я хочу знать, когда этот объект изменен. Или, точнее, я хочу знать, когда изменилось какое-либо из его полей (в данном случае значение). Кроме того, если я расширяю TestObject, я все еще хочу иметь возможность прослушивать любые изменения полей, которые могут произойти с этим объектом, в том числе, если это изменение относится к новому полю новых полей.

Я провел некоторое исследование и обнаружил множество слушателей, которые поставляются с java, но все они, похоже, терпят неудачу в той области, в которой они требуют, чтобы вы помещали вызовы слушателям в конец ваших методов. Например, функцияувеличитьзначение() также должна уведомить всех слушателей объекта TestObject об изменении значения. Очевидно, это не работает для меня, потому что расширяемость обязательна, и я не знаю, будут ли люди, которые наследуют от этого объекта, придерживаться требования о том, что они должны уведомлять слушателей. Не говоря уже о том, что программировать это для каждого метода мутатора кажется мучением.

Если бы можно было пролить свет на эту тему, это было бы очень признательно.


person Zargoon    schedule 10.10.2011    source источник


Ответы (3)


Вы можете использовать подход, который использует Hibernate, т.е. классы для добавления дополнительной логики (слушателя) вокруг геттеров и сеттеров ваших POJO. Но тогда вам нужно убедиться, что все используют только проксированные классы.

person Andrey Adamovich    schedule 10.10.2011
comment
См. мой ответ - производственный аспект решит проблему использования прокси. - person Miserable Variable; 10.10.2011
comment
Хотя это выглядит как потенциально сложное решение, оно больше всего соответствует тому, что я искал. - person Zargoon; 02.06.2012


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

Ваш подход не на правильном пути.

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

В вашем случае производный класс МОЖЕТ или МОЖЕТ не вызывать уведомление при изменении.

В подобных случаях вам следует моделировать примерно Composition, а не Inheritence
Композиция и наследование

Для вашего примера:

class Value{    
    private int value;   
     public void increaseValue() {  
            value++;  
     }  
}

Значение знает, как увеличивать себя.

class ValueHolder extends Observable {  
       private Value theValue;  
       public ValueHolder(Value v){  
          theValue = v;  
       }
       public void modifyValue(String methodName)(){  
           Method method = theValue.getClass().getMethod(methodName, null);  
           method.invoke(theValue,null);//Since it has no args           
           setChanged();  
           notifyObservers();  
       }  
    }

Таким образом, весь код будет следующим:

ValueHolder vh = new ValueHolder(new Value(10));  
//registration of listeners
vh.modifyValue("increaseValue");  

//So if you extend the Value 
class DerivedValue extends Value{    
    private int y;   
     public void increaseY() {  
            y++;  
     }  
}  

ValueHolder vh = new ValueHolder(new DerivedValue());  
//registration of listeners
vh.modifyValue("increaseY);    

Итак, загвоздка в том, что использование объектов осуществляется через держатель.
Тогда произойдет уведомление.

person Cratylus    schedule 10.10.2011
comment
Это неправильно. Идея наследования заключается в том, что производный класс должен подчиняться инвариантам базового класса. В этом весь смысл LSP, не так ли? В частности, инвариантами метода базового класса являются параметры типа pf, тип возвращаемого значения и исключения. Проблема здесь в том, что базовый класс хочет навязать инварианты производному классу. - person Miserable Variable; 10.10.2011
comment
Да, вы правы. Ответственность за соблюдение инвариантов лежит на программистах. Я согласен. Это то, о чем я также говорю здесь. Невозможно применить инварианты базового класса. - person Cratylus; 10.10.2011
comment
Нет, программисты не обязаны применять инварианты, язык не позволяет вам нарушать инварианты. Этот аргумент не работает с UnsupportedOperationException, но теоретически идея состоит в том, что подклассы не смогут нарушить инварианты суперкласса. - person Miserable Variable; 10.10.2011
comment
@Hemal: я не уверен, что мы говорим об одном и том же. Если, например, в базовом классе условие a < b должно оставаться действительным во всех случаях (a и b должны быть полями-членами), как вы можете убедиться, что программист, расширяющий класс и переопределение методов сохраняет этот инвариант? Вы не можете. Это моя точка зрения здесь - person Cratylus; 10.10.2011
comment
Извините, но я немного запутался. Не могли бы вы, ребята, дать мне немного больше направлений того, что я должен исследовать? - person Zargoon; 11.10.2011
comment
@Zargoon: Как упоминалось в ответе, наследование не гарантирует того, что вы хотите, т.е. вы не можете гарантировать, что производные классы следуют поведению базового класса по уведомлению слушателей. Вместо этого вам следует изучить композицию, а также отражение. Я привел вам небольшой пример, чтобы понять, о чем я говорю. В этом примере класс Value, который ваш исходный TestObject может быть расширен по мере необходимости, не беспокоясь об уведомлении. Предостережение заключается в том, что классом нужно манипулировать только через класс Holder, передающий имя метода для вызова. Держатель уведомляет всех слушателей - person Cratylus; 11.10.2011
comment
Я не понимаю, как это помогает, это просто перекладывает ответственность с Value на ValueHolder, с дополнительным багажом размышлений и потерей проверки времени компиляции. - person Miserable Variable; 11.10.2011
comment
Ну, это просто первое, что пришло мне в голову, что, похоже, помогает в его случае. Это действительно переносит ответственность с Value на ValueHolder, и я упомянул об этом в ответе, но теперь ему не нужно заботиться о реализации производные классы Value. Может быть, есть лучшие подходы. Я не знаю AspectJ, чтобы понять ваше предложение, но это отправная точка для ОП. - person Cratylus; 11.10.2011