观察者设计模式

发布于 2024-08-14 06:30:48 字数 348 浏览 3 评论 0原文

在观察者设计模式中,主体通过调用每个观察者的 update() 操作来通知所有观察者。这样做的一种方法是,

void notify() {
   for (observer: observers) {
      observer.update(this);
   }
}

但这里的问题是每个观察者都按顺序更新,并且观察者的更新操作可能不会被调用,直到更新之前的所有观察者。如果有一个观察者有一个无限循环来更新,那么它之后的所有观察者将永远不会被通知。

问题:

  1. 有办法解决这个问题吗?
  2. 如果是的话,什么是一个很好的例子?

In the Observer Design Pattern, the subject notifies all observers by calling the update() operation of each observer. One way of doing this is

void notify() {
   for (observer: observers) {
      observer.update(this);
   }
}

But the problem here is each observer is updated in a sequence and update operation for an observer might not be called till all the observers before it is updated. If there is an observer that has an infinite loop for update then all the observer after it will never be notified.

Question:

  1. Is there a way to get around this problem?
  2. If so what would be a good example?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

荆棘i 2024-08-21 06:30:48

问题在于无限循环,而不是一个接一个的通知。

如果您希望同时更新事物,则需要在不同的线程上触发事物 - 在这种情况下,每个侦听器都需要与其他侦听器同步才能访问触发事件的对象。

抱怨一个无限循环阻止其他更新发生就像抱怨获取一个锁然后进入无限循环阻止其他人访问锁定的对象 - 问题在于无限循环,而不是锁管理器。

The problem is the infinite loop, not the one-after-the-other notifications.

If you wanted things to update concurrently, you'd need to fire things off on different threads - in which case, each listener would need to synchronize with the others in order to access the object that fired the event.

Complaining about one infinite loop stopping other updates from happening is like complaining that taking a lock and then going into an infinite loop stops others from accessing the locked object - the problem is the infinite loop, not the lock manager.

宣告ˉ结束 2024-08-21 06:30:48

经典的设计模式不涉及并行性和线程。您必须为 N 个观察者生成 N 个线程。但要小心,因为它们与 this 的交互必须以线程安全的方式完成。

Classic design patterns do not involve parallelism and threading. You'd have to spawn N threads for the N observers. Be careful though since their interaction to this will have to be done in a thread safe manner.

一江春梦 2024-08-21 06:30:48

您可以使用 java.utils.concurrent.Executors.newFixedThreadPool(int nThreads) 方法,然后调用 invokeAll 方法(也可以使用带有超时的方法来避免无限循环)。

您可以更改循环以添加一个可调用的类,该类采用“observer”和“this”,然后在“call”方法中调用更新方法。

查看此包以了解更多信息信息

这是我正在谈论的内容的快速而肮脏的实现:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Main
{
    private Main()
    {
    }

    public static void main(final String[] argv)
    {
        final Watched       watched;
        final List<Watcher> watchers;

        watched = new Watched();
        watchers = makeWatchers(watched, 10);
        watched.notifyWatchers(9);
    }

    private static List<Watcher> makeWatchers(final Watched watched,
                                              final int     count)
    {
        final List<Watcher> watchers;

        watchers = new ArrayList<Watcher>(count);

        for(int i = 0; i < count; i++)
        {
            final Watcher watcher;

            watcher = new Watcher(i + 1);
            watched.addWatcher(watcher);
            watchers.add(watcher);
        }

        return (watchers);
    }
}

class Watched
{
    private final List<Watcher> watchers;

    {
        watchers = new ArrayList<Watcher>();
    }

    public void addWatcher(final Watcher watcher)
    {
        watchers.add(watcher);
    }

    public void notifyWatchers(final int seconds)
    {
        final List<Watcher>         currentWatchers;
        final List<WatcherCallable> callables;
        final ExecutorService       service;

        currentWatchers = new CopyOnWriteArrayList<Watcher>(watchers);
        callables       = new ArrayList<WatcherCallable>(currentWatchers.size());

        for(final Watcher watcher : currentWatchers)
        {
            final WatcherCallable callable;

            callable = new WatcherCallable(watcher);
            callables.add(callable);
        }

        service = Executors.newFixedThreadPool(callables.size());

        try
        {
            final boolean value;

            service.invokeAll(callables, seconds, TimeUnit.SECONDS);
            value = service.awaitTermination(seconds, TimeUnit.SECONDS);
            System.out.println("done: " + value);
        }
        catch (InterruptedException ex)
        {
        }

        service.shutdown();
        System.out.println("leaving");
    }

    private class WatcherCallable
        implements Callable<Void>
    {
        private final Watcher watcher;

        WatcherCallable(final Watcher w)
        {
            watcher = w;
        }

        public Void call()
        {
            watcher.update(Watched.this);
            return (null);
        }
    }
}

class Watcher
{
    private final int value;

    Watcher(final int val)
    {
        value = val;
    }

    public void update(final Watched watched)
    {
        try
        {
            Thread.sleep(value * 1000);
        }
        catch (InterruptedException ex)
        {
            System.out.println(value + "interupted");
        }

        System.out.println(value + " done");
    }
}

You could make use of the java.utils.concurrent.Executors.newFixedThreadPool(int nThreads) method, then call the invokeAll method (could make use of the one with the timout too to avoid the infinite loop).

You would change your loop to add a class that is Callable that takes the "observer" and the "this" and then call the update method in the "call" method.

Take a look at this package for more info.

This is a quick and dirty implementation of what I was talking about:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Main
{
    private Main()
    {
    }

    public static void main(final String[] argv)
    {
        final Watched       watched;
        final List<Watcher> watchers;

        watched = new Watched();
        watchers = makeWatchers(watched, 10);
        watched.notifyWatchers(9);
    }

    private static List<Watcher> makeWatchers(final Watched watched,
                                              final int     count)
    {
        final List<Watcher> watchers;

        watchers = new ArrayList<Watcher>(count);

        for(int i = 0; i < count; i++)
        {
            final Watcher watcher;

            watcher = new Watcher(i + 1);
            watched.addWatcher(watcher);
            watchers.add(watcher);
        }

        return (watchers);
    }
}

class Watched
{
    private final List<Watcher> watchers;

    {
        watchers = new ArrayList<Watcher>();
    }

    public void addWatcher(final Watcher watcher)
    {
        watchers.add(watcher);
    }

    public void notifyWatchers(final int seconds)
    {
        final List<Watcher>         currentWatchers;
        final List<WatcherCallable> callables;
        final ExecutorService       service;

        currentWatchers = new CopyOnWriteArrayList<Watcher>(watchers);
        callables       = new ArrayList<WatcherCallable>(currentWatchers.size());

        for(final Watcher watcher : currentWatchers)
        {
            final WatcherCallable callable;

            callable = new WatcherCallable(watcher);
            callables.add(callable);
        }

        service = Executors.newFixedThreadPool(callables.size());

        try
        {
            final boolean value;

            service.invokeAll(callables, seconds, TimeUnit.SECONDS);
            value = service.awaitTermination(seconds, TimeUnit.SECONDS);
            System.out.println("done: " + value);
        }
        catch (InterruptedException ex)
        {
        }

        service.shutdown();
        System.out.println("leaving");
    }

    private class WatcherCallable
        implements Callable<Void>
    {
        private final Watcher watcher;

        WatcherCallable(final Watcher w)
        {
            watcher = w;
        }

        public Void call()
        {
            watcher.update(Watched.this);
            return (null);
        }
    }
}

class Watcher
{
    private final int value;

    Watcher(final int val)
    {
        value = val;
    }

    public void update(final Watched watched)
    {
        try
        {
            Thread.sleep(value * 1000);
        }
        catch (InterruptedException ex)
        {
            System.out.println(value + "interupted");
        }

        System.out.println(value + " done");
    }
}
维持三分热 2024-08-21 06:30:48

我更关心观察者抛出异常,而不是无限循环。您当前的实现不会通知此类事件中的其余观察者。

I'd be more concerned about the observer throwing an exception than about it looping indefinitely. Your current implementation would not notify the remaining observers in such an event.

谜兔 2024-08-21 06:30:48

1。有没有办法解决这个问题?

是的,确保观察者工作正常并及时返回。

2.有人可以用例子解释一下吗?

当然:

class ObserverImpl implements Observer {
     public void update( Object state ) {
            // remove the infinite loop.
            //while( true ) {
            //   doSomething();
            //}

            // and use some kind of control:
            int iterationControl = 100;
            int currentIteration = 0;
            while( curentIteration++ < iterationControl ) {
                 doSomething();
            }
     }
     private void doSomething(){}
}

这可以防止给定的循环进入无限循环(如果有意义,它最多应该运行 100 次)

其他机制是在一秒钟内启动新任务线程,但如果它进入无限循环,它最终会消耗所有系统内存:

class ObserverImpl implements Observer {
     public void update( Object state ) {
         new Thread( new Runnable(){ 
             public void run() {
                 while( true ) {
                     doSomething();
                 }
             }
          }).start();
     }
     private void doSomething(){}
}

这将使该观察者实例立即返回,但这只是一种幻觉,你实际上要做的是避免无限循环。

最后,如果您的观察者工作正常,但您只是想尽快通知他们所有人,您可以查看以下相关问题:在所有鼠标事件监听器执行完毕后调用代码。

1. Is there a way to get around this problem?

Yes, make sure the observer work fine and return in a timely fashion.

2. Can someone please explain it with an example.

Sure:

class ObserverImpl implements Observer {
     public void update( Object state ) {
            // remove the infinite loop.
            //while( true ) {
            //   doSomething();
            //}

            // and use some kind of control:
            int iterationControl = 100;
            int currentIteration = 0;
            while( curentIteration++ < iterationControl ) {
                 doSomething();
            }
     }
     private void doSomething(){}
}

This one prevent from a given loop to go infinite ( if it makes sense, it should run at most 100 times )

Other mechanism is to start the new task in a second thread, but if it goes into an infinite loop it will eventually consume all the system memory:

class ObserverImpl implements Observer {
     public void update( Object state ) {
         new Thread( new Runnable(){ 
             public void run() {
                 while( true ) {
                     doSomething();
                 }
             }
          }).start();
     }
     private void doSomething(){}
}

That will make the that observer instance to return immediately, but it will be only an illusion, what you have to actually do is to avoid the infinite loop.

Finally, if your observers work fine but you just want to notify them all sooner, you can take a look at this related question: Invoke a code after all mouse event listeners are executed..

决绝 2024-08-21 06:30:48

所有观察者都会收到通知,这就是您得到的所有保证。

如果你想实现一些奇特的排序,你可以这样做:

  • 仅连接一个观察者;
  • 让这个主要观察者按照您在代码中定义的顺序或通过其他方式通知他的朋友。

这使您远离了经典的观察者模式,因为您的听众是硬连线的,但如果这是您所需要的......那就去做吧!

All observers get notified, that's all the guarantee you get.

If you want to implement some fancy ordering, you can do that:

  • Connect just a single Observer;
  • have this primary Observer notify his friends in an order you define in code or by some other means.

That takes you away from the classic Observer pattern in that your listeners are hardwired, but if it's what you need... do it!

旧时模样 2024-08-21 06:30:48

如果你有一个带有“无限循环”的观察者,它就不再是真正的观察者模式。

您可以向每个观察者触发不同的线程,但必须禁止观察者更改被观察对象的状态。

最简单(也是最愚蠢)的方法就是采用您的示例并将其线程化。

void notify() {
   for (observer: observers) {
      new Thread(){
          public static void run() {
              observer.update(this);
          } 
      }.start();
   }
}

(这是手工编码的,未经测试,可能有一个或五个错误——无论如何,这都是一个坏主意)

这样做的问题是,它会让你的机器变得笨重,因为它必须立即分配一堆新线程。

因此,要解决所有线程同时启动的问题,请使用 ThreadPoolExecutor,因为它会 A) 回收线程,B) 可以限制运行的最大线程数。

在“永远循环”的情况下,这不是确定性的,因为每个永远循环都会永久占用池中的一个线程。

最好的办法是不要让它们永远循环,或者如果必须的话,让它们创建自己的线程。

如果您必须支持无法更改的类,但您可以确定哪些将快速运行,哪些将“永远”运行(用计算机术语来说,我认为这相当于超过一两秒),那么您可以使用类似的循环这个:

void notify() {
   for (observer: observers) {
      if(willUpdateQuickly(observer))
          observer.update(this);
      else
          new Thread(){
              public static void run() {
                  observer.update(this);
              } 
          }.start();
   }
}

嘿,如果它真的“永远循环”,它会为每个通知消耗一个线程吗?听起来您可能需要在设计上花费更多时间。

If you have an observer with an "infinite loop", it's no longer really the observer pattern.

You could fire a different thread to each observer, but the observers MUST be prohibited from changing the state on the observed object.

The simplest (and stupidest) method would simply be to take your example and make it threaded.

void notify() {
   for (observer: observers) {
      new Thread(){
          public static void run() {
              observer.update(this);
          } 
      }.start();
   }
}

(this was coded by hand, is untested and probably has a bug or five--and it's a bad idea anyway)

The problem with this is that it will make your machine chunky since it has to allocate a bunch of new threads at once.

So to fix the problem with all the treads starting at once, use a ThreadPoolExecutor because it will A) recycle threads, and B) can limit the max number of threads running.

This is not deterministic in your case of "Loop forever" since each forever loop will permanently eat one of the threads from your pool.

Your best bet is to not allow them to loop forever, or if they must, have them create their own thread.

If you have to support classes that can't change, but you can identify which will run quickly and which will run "Forever" (in computer terms I think that equates to more than a second or two) then you COULD use a loop like this:

void notify() {
   for (observer: observers) {
      if(willUpdateQuickly(observer))
          observer.update(this);
      else
          new Thread(){
              public static void run() {
                  observer.update(this);
              } 
          }.start();
   }
}

Hey, if it actually "Loops forever", will it consume a thread for every notification? It really sounds like you may have to spend some more time on your design.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文