블로그 이미지
Sunny's

calendar

1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Notice

'Observer Pattern'에 해당되는 글 2

  1. 2011.10.31 The Observer Pattern in Objective-C
  2. 2011.10.30 IObservable<T> Observer Pattern
2011. 10. 31. 13:33 IPhone

The Motivation

I've always wondered why the Foundation and Cocoa frameworks in Objective-C are so biased towards the delegate pattern. Don't get me wrong, I don't have anything against it. But there are some cases where using the observer pattern would make more sense. Take, for instance, the NSAnimationDelegate protocol. Most of the 'delegate' methods are actually just state change notifications. Out of the five methods in this protocol only one is an actual delegate method:animation:valueForProgress:.

The absence of the observer pattern, especially in the UI classes, prompted me write a reusable observable class to use in my custom UI code. Similar to Java's Observable class.

The Problem

Probably the biggest implementation difference between the observer pattern and the delegate pattern is that observable objects support multiple observers, while the delegate is just one object. But this is precisely what makes the observer pattern one of my favorites. It is also what makes it harder to implement.

Also, doing a bullet-proof implementation requires handling the case of an observer being added or removed inside a notification call. The observer collection cannot mutate while iterating so it needs a clever way of handling this (for some ways of doing this look here).

The Solution

This is how the Observable class looks like:
@interface Observable : NSObject
- (void)addObserver:(id<NSObject>)observer;
- (void)removeObserver:(id<NSObject>)observer;
- (void)notifyObservers:(NSInvocation*)invocation;
@end
It is pretty standard, except that you pass an NSInvocation when you want to send a notification. This is so that we have flexibility with observer protocols. If you haven't, you might want to check out the previous post: Making NSInvocations, but more on this later.

We start by creating the observer collection. Since this is Objective-C (possibly for iOS, e.g. iPhone), we need to be careful not to retain the observers to avoid circular references. So we need to create a non-retaining mutable set:
observers = (NSMutableSet*)CFSetCreateMutable(NULL, 0, NULL);
The reason it is a set and not an array is to make adding and removing observers faster.

And here is how you notify the observers:
- (void)notifyObservers:(NSInvocation*)invocation {
  notifying = YES;
  for (id<NSObject> observer in observers) {
    if (![pendingRemoves containsObject:observer] && [observer respondsToSelector:[invocation selector]]) {
      [invocation setTarget:observer];
      [invocation invoke];
    }
  }
  notifying = NO;
  [self commitPending];
}
The notifying flag and the commitPending method call are there to handle addition and removal of observers inside a notification. If the notifying flag is set then we don't add the observer to the main observers collection. We instead add it to a temporary collection (pendingAdds) and only in commitPending do we actually add it to the main observers collection. Here is the code:
- (void)addObserver:(id<NSObject>)observer {
  if (notifying) {
    [pendingRemoves removeObject:observer];
    [pendingAdds addObject:observer];
  } else {
    [observers addObject:observer];
  }
}
The code for removeObserver: is very similar and the code for commitPending is straight forward.

Conclusion

Let me finish by showing how you would use this. Let's say you have an animation class you want to make observable. You want to receive notification for the animation starting or stopping:
@protocol AnimationObserver
- (void)animationDidStart;
- (void)animationDidStop;
@end
Here is how you would implement the Animation class:
@interface Animation : Observable
...
- (void)start;
- (void)stop;
@end

@implementation Animation
...
- (void)start {
  ...
  NSInvocation* inv = [NSInvocation invocationWithProtocol:@protocol(AnimationObserver)
                                                  selector:@selector(animationDidStart)]
  [self notifyObservers:inv];
}

- (void)stop {
  ...
  NSInvocation* inv = [NSInvocation invocationWithProtocol:@protocol(AnimationObserver)
                                                  selector:@selector(animationDidStop)]
  [self notifyObservers:inv];
}
@end
Notice how it uses the invocationWithProtocol:selector: method from the previous post, Making NSInvocations. Actually this is precisely the use case I had in mind when I implemented the NSInvocation additions. 

So there you have it. For more information on the observer pattern you can check out the Wikipedia entry or the GOF book. If you have questions or further improvements feel free to leave a comment.

The source code for this article, including unit tests, is available at GitHub. To compile you will need Google Toolbox for Mac.

출처 : http://www.a-coding.com/2010/10/observer-pattern-in-objective-c.html 
posted by Sunny's
2011. 10. 30. 22:37 .NET Framework

Defines a provider for push-based notification.

Namespace: System
Assembly: mscorlib (in mscorlib.dll)

Parameters

value
Type: T
The current notification information.

The IObserver(Of T) and IObservable(Of T) interfaces provide a generalized mechanism for push-based notification, also known as the observer design pattern. The IObservable(Of T) interface represents the class that sends notifications (the provider); the IObserver(Of T) interface represents the class that receives them (the observer). T represents the class that provides the notification information. In some push-based notifications, the IObserver(Of T) implementation and T can represent the same type.

The provider must implement a single method, Subscribe, that indicates that an observer wants to receive push-based notifications. Callers to the method pass an instance of the observer. The method returns an IDisposable implementation that enables observers to cancel notifications at any time before the provider has stopped sending them.

At any given time, a given provider may have zero, one, or multiple observers. The provider is responsible for storing references to observers and ensuring that they are valid before it sends notifications. The IObservable(Of T) interface does not make any assumptions about the number of observers or the order in which notifications are sent.

The provider sends the following three kinds of notifications to the observer by calling IObserver(Of T) methods:

  • The current data. The provider can call the IObserver(Of T).OnNext method to pass the observer a T object that has current data, changed data, or fresh data.

  • An error condition. The provider can call the IObserver(Of T).OnError method to notify the observer that some error condition has occurred.

  • No further data. The provider can call the IObserver(Of T).OnCompleted method to notify the observer that it has finished sending notifications.


public struct Location
{
   double lat, lon;

   public Location(double latitude, double longitude)
   {
      this.lat = latitude;
      this.lon = longitude;
   }

   public double Latitude
   { get { return this.lat; } }

   public double Longitude
   { get { return this.lon; } }
}


public class LocationTracker : IObservable<Location>
{
   public LocationTracker()
   {
      observers = new List<IObserver<Location>>();
   }

   private List<IObserver<Location>> observers;

   public IDisposable Subscribe(IObserver<Location> observer)
   {
      if (! observers.Contains(observer))
         observers.Add(observer);
      return new Unsubscriber(observers, observer);
   }

   private class Unsubscriber : IDisposable
   {
      private List<IObserver<Location>>_observers;
      private IObserver<Location> _observer;

      public Unsubscriber(List<IObserver<Location>> observers, IObserver<Location> observer)
      {
         this._observers = observers;
         this._observer = observer;
      }

      public void Dispose()
      {
         if (_observer != null && _observers.Contains(_observer))
            _observers.Remove(_observer);
      }
   }

   public void TrackLocation(Nullable<Location> loc)
   {
      foreach (var observer in observers) {
         if (! loc.HasValue)
            observer.OnError(new LocationUnknownException());
         else
            observer.OnNext(loc.Value);
      }
   }

   public void EndTransmission()
   {
      foreach (var observer in observers.ToArray())
         if (observers.Contains(observer))
            observer.OnCompleted();

      observers.Clear();
   }
}


public class LocationUnknownException : Exception
{
   internal LocationUnknownException()
   { }
}


using System;

public class LocationReporter : IObserver<Location>
{
   private IDisposable unsubscriber;
   private string instName;

   public LocationReporter(string name)
   {
      this.instName = name;
   }

   public string Name
   {  get{ return this.instName; } }

   public virtual void Subscribe(IObservable<Location> provider)
   {
      if (provider != null)
         unsubscriber = provider.Subscribe(this);
   }

   public virtual void OnCompleted()
   {
      Console.WriteLine("The Location Tracker has completed transmitting data to {0}.", this.Name);
      this.Unsubscribe();
   }

   public virtual void OnError(Exception e)
   {
      Console.WriteLine("{0}: The location cannot be determined.", this.Name);
   }

   public virtual void OnNext(Location value)
   {
      Console.WriteLine("{2}: The current location is {0}, {1}", value.Latitude, value.Longitude, this.Name);
   }

   public virtual void Unsubscribe()
   {
      unsubscriber.Dispose();
   }
}


using System;

class Program
{
   static void Main(string[] args)
   {
      // Define a provider and two observers.
      LocationTracker provider = new LocationTracker();
      LocationReporter reporter1 = new LocationReporter("FixedGPS");
      reporter1.Subscribe(provider);
      LocationReporter reporter2 = new LocationReporter("MobileGPS");
      reporter2.Subscribe(provider);

      provider.TrackLocation(new Location(47.6456, -122.1312));
      reporter1.Unsubscribe();
      provider.TrackLocation(new Location(47.6677, -122.1199));
      provider.TrackLocation(null);
      provider.EndTransmission();
   }
}
// The example displays output similar to the following:
//      FixedGPS: The current location is 47.6456, -122.1312
//      MobileGPS: The current location is 47.6456, -122.1312
//      MobileGPS: The current location is 47.6677, -122.1199
//      MobileGPS: The location cannot be determined.
//      The Location Tracker has completed transmitting data to MobileGPS.



출처 : http://msdn.microsoft.com/en-us/library/dd990377.aspx

posted by Sunny's
prev 1 next