2

そこで、プログラム クラスの内部に含めることができるクラスを作成し、Thread特定の変数が変更されるたびに基本的に警告を発するクラスを作成しました。この種の情報を迅速かつ効率的に取得するためのより良いアイデアを誰かが持っているかどうか、もしそうでない場合は、このクラスに関する提案があるかどうか疑問に思っています. 参考までに、alert()メソッドは基本的に単純な System.out.println() です。

public class OutputState extends Thread{

  String   className = this.getClass().toString().replace("class ","").replace("$OutputState",""); 
  String[] desc = {"canvasWidth","canvasHeight","relativeCenterX","relativeCenterY","zoom","hAdjust","vAdjust"};
  int[]    delay = {0,0,99,99,0,0,0,};
  Object[] var;
  int maxDescLength = 0;

  public void init(){
     Object[] v = {canvasWidth,canvasHeight,relativeCenterX,relativeCenterY,zoom,hAdjust,vAdjust};
     var = v;
  }

  public void run(){
     init();
     while(true){
        boolean newLine = false;
        Object[] v = {canvasWidth,canvasHeight,relativeCenterX,relativeCenterY,zoom,hAdjust,vAdjust};
        for(int i = 0; i < var.length; i++){
           if(maxDescLength < desc[i].length()){
              maxDescLength = desc[i].length();
           }
           if(!var[i].equals(v[i])){
              var[i]=v[i];
              String spaces = " ";
              int count = desc[i].length()+1;
              while(count <= maxDescLength){
                 spaces += " ";
                 count++;
              }
              alert(className + "." + desc[i] + spaces + "= " + var[i]);
              newLine = true;
              if(delay[i] > 0){try{Thread.sleep(delay[i]);}catch(InterruptedException e){}}
           }
        }
        if(newLine){
           alert("------------------------------------");
        }
        try{Thread.sleep(1);}catch(InterruptedException e){}
     }   
  }
} 

基本的な概念は、v追跡したい変数に設定され、プログラム内の変化する変数で自分自身を更新し続け、最後に知っていたvarものを含むことです。変更OutputStateの何かがそれをキャッチし、カスタム メッセージを出力するたびに。多くの人がこのような便利なものを見つけてくれることを知っているので、私がまだ見つけていないより良い方法があると信じています. ご意見をいただければ幸いです。vvar

4

3 に答える 3

1

追跡したいプロパティを含む Bean ごとに、PropertyChangeSupport と、それに PropertyChangeListener を追加するメソッドを追加できます。次に、セッターで、PropertyChangeSupport で firePropertyChange を呼び出します。この構成では、構成を使用してリッスンするオブジェクトを決定し、必要に応じてリスナーを動的に追加および削除することもできます。別のスレッドは必要なく、ループとスリープも必要ありません。これを示す小さなサンプルを次に示します。

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public class ChangeTest {

  private String val1;
  private String val2;
  private PropertyChangeSupport pcs = new PropertyChangeSupport(this);

  public String getVal1() {
    return val1;
  }

  public void setVal1(String val1) {
    pcs.firePropertyChange("val1", this.val1, val1);
    this.val1 = val1;
  }

  public String getVal2() {
    return val2;
  }

  public void setVal2(String val2) {
    pcs.firePropertyChange("val2", this.val2, val2);
    this.val2 = val2;
  }

  public void addPropertyChangeListener(PropertyChangeListener listener ) {
    pcs.addPropertyChangeListener(listener);
  }

  public static void main( String[] args ) {
    ChangeTest test = new ChangeTest();
    test.addPropertyChangeListener(new Listener());
    for ( int i = 0; i < 5; i++ ) {
      test.setVal1("value " + i );
      test.setVal2(test.getVal1() + test.getVal1());
    }
  }

  public static class Listener implements PropertyChangeListener {

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
  System.out.println( evt.getPropertyName() + " changed from '" + evt.getOldValue() + "' to '" + evt.getNewValue() + "'" );
    }
  }
}

そのプログラムからの出力は次のとおりです。

val1 changed from 'null' to 'value 0'
val2 changed from 'null' to 'value 0value 0'
val1 changed from 'value 0' to 'value 1'
val2 changed from 'value 0value 0' to 'value 1value 1'
val1 changed from 'value 1' to 'value 2'
val2 changed from 'value 1value 1' to 'value 2value 2'
val1 changed from 'value 2' to 'value 3'
val2 changed from 'value 2value 2' to 'value 3value 3'
val1 changed from 'value 3' to 'value 4'
val2 changed from 'value 3value 3' to 'value 4value 4'
于 2013-09-27T13:35:39.033 に答える
1

変数がいつ変更されるかを確実に知る唯一の方法は、それを宣言privateし、setter メソッドを介してのみ変更することです。次に、setter各呼び出しが変数の状態を変更するかどうかをテストするように を記述します。

例えば:

private int summit;

public void setSummit(int value) {
    if (value != summit) {
        System.err.println("Ey up: summit's changed!");
    }
}

変数の型が配列の場合、直接アクセスから配列を隠蔽しない限り、配列要素がいつ変更されるかを確実に判断することは不可能になります。


AOP やその他の形式のコード インジェクションを使用する場合は、ソース コードを変更せずにこの種のことを実行できる場合があります。ただし、実行時には、上記を効果的に実行します。

于 2013-09-27T13:27:16.900 に答える
1

やろうとしていることを達成する簡単な方法はありません。(現在行っているように) ループしても、(バス トラフィックのために) 残りの計算が遅くなるだけでなく、いくつかの更新を見逃す可能性もあります (モニター スレッドがあまり頻繁にスケジュールされていないか、ビジー状態でスケジュールされている場合)。ワーカー スレッドよりもプロセッサ)。

コメントに記載されているように、アスペクト指向プログラミング言語、Java プロキシ、またはより単純なデリゲート パターンを使用することをお勧めします。いずれにせよ、既存のコードの変更が必要になる場合があります。

interface IWorkState {
    //Declares the method of WorkState
}

class WorkState implements IWorkState {
    //Whatever java bean
}

class WorkTask implements Runnable {
    private final IWorkState state;

    public void addStateObserver(IWorkState initialState) {
         state = initialState
    }

    @Override
    public void run() {
        //Task instructions here
        //instructions should operate on the state variable
    }
}

class VarMonitor {
    public void update(Object changed) {
        //subject has changed, react to it
        eventQueue.submit(/* a Runnable that handles the state change,
                           with embedded information about the change */);

    }
}

class MainClass {
    public static void main(String[] args) {
        final VarMonitor mon = new VarMonitor();
        WorkTask t = new WorkTask(Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {IWorkState.class}, new InvocationHandler() {
            private final WorkState realWorkState = new WorkState();
            Object invoke(Object proxy, Method method, Object[] args) {
                Object res = method.invoke(realWorkState, args);
                //An annotation would work better
                if (method.getName().substring(0,3).equals("set")) {
                    mon.update(this);
                }
            }
        });
    }
}

java.util.Observable3 番目のオプションは、オブザーバー パターンのサブクラスを使用することです。何かのようなもの:

class WorkState extends java.util.Observable {
    //A thread-safe observable java bean
}

class WorkTask implements Runnable {
    private final WorkState state = new WorkState();

    public void addStateObserver(Observer ob) {
         state.addObserver(ob);
    }

    @Override
    public void run() {
        //Task instructions here
        //instructions should operate on the state variable
    }
}

class VarMonitor implements Observer {
    private final ExecutorService eventQueue
            = Executors.newSingleThreadExecutor();

    @Override
    public void update(Observable subject, Object info) {
        //subject has changed, react to it
        eventQueue.submit(/* a Runnable that handles the state change,
                           with embedded information about the change */);

    }
}

不変オブジェクトでこのパターンを使用している場合は、すべてのオブジェクトで機能する ObservableReference クラスを簡単に実装できます。

PS: 前に言及しませんでしたが、共有変数へのマルチスレッド アクセスを同期するか、変数をスレッド セーフまたは揮発性にする必要があります。共有変数をやみくもにポーリングすることはできません。そうしないと、一部の結果が間違っている可能性があります (部分的に更新されたオブジェクトが表示されるなど)。

于 2013-09-27T13:30:17.553 に答える