Introduction

You are currently browsing the weblog archives for June, 2004.

Archive for June, 2004

Saturday, June 19th, 2004

DP: Singleton

A thread about the Singleton Design Pattern arised in the Flashforum (German). We (Nico Zimmermann, Ralf Bokelberg, Florian Kruesch and myself) discussed different implementations of the Singleton Pattern and what the advantages and disadvantages of each are. All started very basic with the simplest implementation all of you probably know.

  1. /**
  2. * Singleton shows a basic implementation of the Singleton Design
  3. * Pattern.
  4. *
  5. * @author Simon Wacker
  6. */
  7. class org.swacker.designpattern.Singleton {
  8.   /** Stores the onliest instance existing of this class. */
  9.   private static var instance:Singleton;
  10.  
  11.   /**
  12.    * Private constructor to prevent instantiation from outside of
  13.    * this class.
  14.    */
  15.   private function Singleton(Void) {
  16.   }
  17.  
  18.   /**
  19.    * Returns the onliest existing instance of this class.
  20.    *
  21.    * @return the onliest instance of this class
  22.    */
  23.   public static function getInstance(Void):Singleton {
  24.     // creates a new instance of this class if none exists
  25.     if (!instance) instance = new Singleton();
  26.     // returns the stored instance
  27.     return instance;
  28.   }
  29. }

Then we thought about the problems that emerge using this kind of implementation.
The first problem is that it is awkward to substitute the currently used Singleton with a new one. You had to change ALL references.
The second and maybe not that obvious issue is that only one instance of the Singleton class can exist in the whole project.
You probably think: Why should that be a problem? Doesn’t it lie in the nature of a Singleton that only one instance can exist of it?
These two statements are probably right BUT you normally want to have one instance of the Singleton in one context and not only one in a whole project. Thus my definition of a Singleton would be the following: A Singleton is a class that has exactly one instance in one context. This instance is shared among all classes in that context.
This definition sadly conflicts with the one of GoF (Gang of Four): Ensure a class only has one instance, and provide a global point of access to it.
But I think that the variation is acceptable. My definition maybe reminds you more of the Multiton Design Pattern.
So, how would one implement such a context spcific Singleton. That’s rather easy. You just have to use a factory that decides which instance to instantiate and whether to store this instance for sharing, thus making it a Singleton. You must store the instance of a specific factory global to one context.

  1. /**
  2. * Singleton is just representing any normal class. It does not have
  3. * any specific features.
  4. *
  5. * @author Simon Wacker
  6. */
  7. class org.swacker.designpattern.Singleton {
  8.   /**
  9.    * Constructs a new Singleton instance.
  10.    */
  11.   public function Singleton(Void) {
  12.   }
  13. }
  1. /**
  2. * SingletonFactory is a factory that always returns the same instance
  3. * when #getSingleton() is called. This instance thus gets shared among
  4. * all referencing classes in the factory’s context. At least if an
  5. * instance of the specific class gets obtained via SingletonFactory#getSingleton().
  6. *
  7. * @author Simon Wacker
  8. */
  9. class org.swacker.designpattern.SingletonFactory {
  10.   /** The shared instance of the Singleton. */
  11.   private var singleton:Singleton;
  12.  
  13.   /**
  14.    * Constructs a new SingletonFactory.
  15.    */
  16.   public function SingletonFactory(Void) {
  17.   }
  18.  
  19.   /**
  20.    * Returns the instance stored in the instance variable ’singleton’.
  21.    *
  22.    * @return the Singleton instance shared among all referencing classes
  23.    */
  24.   public function getSingleton(Void):Singleton {
  25.     if (!singleton) singleton = new Singleton();
  26.     return singleton;
  27.   }
  28. }

As you can see, what we have done is moving the storing mechanism out of the Singleton and into the SingletonFactory. In our case the class that gets instantiated by the SingletonFactory is hard-coded. We could create a dynamic SingletonFactory to not have to create a new factory every time. The only difference would be that the SingletonFactory’s constructor would take a ‘clazz’ parameter of type Function and return a new instance of the passed class.

That of course isn’t the neatest way to handle the issue. We will now go a step further and use a Context class to bind classes to string identifiers. The Context checks if the class implements the Singleton interface. If that is the case it creates a new SingletonFactory instance assigning the class to it. If you now use the Context.lookup(String) method passing in the identifier, the Factory’s getInstance() operation is being called returning an instance of the assigned class. As you can see this implementation is much more flexible. You do not have to care about whether the class is a Singleton or not. You can also easily substitute the class returned by the Context.lookup(String) method for a given identifier. All you have to care about is that the new class substituting the old one implements the same interface. All this should of course be done declaratively.
A little variation of this approach would be to determine whether to use the SingletonFactory or a normal one not by implementing the Singleton interface but by calling the Context’s bindSingleton() operation or by doing this declaratively. Doing it that way every class could be a potential Singleton without having to change anything inside of it. To achieve all this we need a bunch of new classes and interfaces. These classes or interfaces are:

  • Interface org.swacker.designpattern.Singleton:

    This interface declares no operations. It is just used to distinguish a Singleton from a normal class.
  • Interface org.swacker.naming.Context:

    The Context interface declares the operations to bind, unbind and rebind an object to a specific name.
  • Interface org.swacker.naming.Factory:

    Factory declares the getInstance() operation that should be implemented by all factories.
  • Class org.swacker.naming.ClassContext:

    ClassContext implements the Context interface. Only classes are allowed to be bound to string identifiers. It gets internally checked whether the passed class is a Singleton using the above explained check mechanism.
  • Class org.swacker.naming.SimpleFactory:

    SimpleFactory is a simple implementation of the Factory interface that just returns a new instance of the passed class, not storing old ones.
  • Class org.swacker.naming.SingletonFactory:

    SingletonFactory stores the firstly instantiated instance of a class and returns this instance every time the SingletonFactory.getInstance() operation gets called.
  1. interface org.swacker.designpattern.Singleton {
  2. }
  1. interface org.swacker.naming.Context {
  2.   public function bind(name:String, object):Void;
  3.   public function rebind(name:String, object):Void;
  4.   public function rename(oldName:String, newName:String):Void;
  5.   public function unbind(name:String):Void;
  6.   public function lookup(name:String);
  7. }
  1. interface org.swacker.naming.Factory {
  2.   public function getInstance(Void);
  3. }
  1. import org.as2lib.env.except.Exception;
  2. import org.as2lib.util.ClassUtil;
  3. import org.swacker.naming.Context;
  4. import org.swacker.naming.Factory;
  5. import org.swacker.naming.SimpleFactory;
  6. import org.swacker.naming.SingletonFactory;
  7. import org.swacker.designpattern.Singleton;
  8.  
  9. class org.swacker.naming.ClassContext implements Context {
  10.   private var registry:Object;
  11.    
  12.   public function ClassContext(Void) {
  13.   }
  14.    
  15.   public function bind(name:String, object):Void {
  16.     if (registry[name]) {
  17.       throw new Exception("The name [" + name + "] is already in use.", this, arguments);
  18.     }
  19.     var factory:Factory;
  20.     if (ClassUtil.isImplementationOf(object, Singleton)) {
  21.       factory = new SingletonFactory(object);
  22.     } else {
  23.       factory = new SimpleFactory(object);
  24.     }
  25.     registry[name] = factory;
  26.   }
  27.    
  28.   public function rebind(name:String, object):Void {
  29.     unbind(name);
  30.     bind(name, object);
  31.   }
  32.    
  33.   public function rename(oldName:String, newName:String):Void {
  34.     bind(newName, registry[oldName]);
  35.     unbind(oldName);
  36.   }
  37.    
  38.   public function unbind(name:String):Void {
  39.     if (!registry[name]) {
  40.       throw new Exception("You tried to unbind a name [" + name + "] that has no binding.", this, arguments);
  41.     }
  42.     registry[name] = undefined;
  43.   }
  44.    
  45.   public function lookup(name:String) {
  46.     return Factory(registry[name]).getInstance();
  47.   }
  48. }
  1. import org.swacker.naming.Factory;
  2.  
  3. class org.swacker.naming.SimpleFactory implements Factory {
  4.   private var clazz;
  5.    
  6.   public function SimpleFactory(clazz:Function) {
  7.     this.clazz = clazz;
  8.   }
  9.    
  10.   public function getInstance(Void) {
  11.     return (new clazz());
  12.   }
  13. }
  1. import org.swacker.naming.Factory;
  2.  
  3. class org.swacker.naming.SingletonFactory implements Factory {
  4.   private var instance;
  5.    
  6.   public function SingletonFactory(clazz:Function) {
  7.     instance = new clazz();
  8.   }
  9.    
  10.   public function getInstance(Void) {
  11.     return instance;
  12.   }
  13. }

Not every thought has been implemented yet. Including the declarative stuff. The code shown here is also not fully worked out. It are all basic implementations that are right now only intended for explanation reasons. I am planning to offer support for everything I have written above in one of the next releases of the as2lib. If you have any questions or suggestions leave a comment.

To complete this post I’m now going to show you why not just use static methods and variables to implement Singletons. The problems using this approach:

  • Everything I told you above would not work using static methods and variables.
  • You could not use the constructor to do the core initialization. That means you had to do the core initialization in a static manner. Doing that causes the initialization to be done whether the class is needed or not which would create unnecessary overhead.
  • Another major disadvantage is that you could not take advantage of interfaces and inheritance.
  • You also could not use ‘this’ directly in one of the static methods becuase it would cause a compile time error. You had to outwit it using eval(”th” + “is”).

This should be enough disadvantages to not even think about implementing a Singleton using static methods and variables.

Sunday, June 6th, 2004

AOP: How to Use the Flash AOP Framework?

To show how you can use the AOP Framework for Flash, I announced in my previous post: AOP: AOP Framework for Flash Version 0.1, I have built a little example that logs the Account.debit() and Account.credit() operations using an around advice. Following is the class Account:

  1. import org.as2lib.core.BasicClass;
  2. import InsufficientBalanceException;
  3.  
  4. /**
  5. * @author Simon Wacker
  6. */
  7. class Account extends BasicClass {
  8.   private var balance:Number;
  9.    
  10.   public function Account(Void) {
  11.     balance = 0;
  12.   }
  13.  
  14.   public function credit(amount:Number):Number {
  15.     setBalance(balance + amount);
  16.     return balance;
  17.   }
  18.   public function debit(amount:Number):Number {
  19.     if ((balance - amount) < 0) {
  20.       throw new InsufficientBalanceException("Balance [" + balance + "] is not sufficient.", this, arguments);
  21.     }
  22.     setBalance(balance - amount);
  23.     return balance;
  24.   }
  25.   public function getBalance(Void):Number {
  26.     return balance;
  27.   }
  28.   public function setBalance(newBalance:Number):Void {
  29.     balance = newBalance;
  30.   }
  31. }

As you can see this class is quite simple. I declares four operations, one to credit money from the Account, one to debit money, one to get the balance and one to set a new balance. The operation that is a little more complex than the other ones is the Account.debit() operation. It first checks if the balance is sufficient and if it is not the InsufficientBalanceException will be thrown.

  1. import org.as2lib.env.except.Exception;
  2.  
  3. /**
  4. * @author Simon Wacker
  5. */
  6. class InsufficientBalanceException extends Exception {
  7.   public function InsufficientBalanceException(message:String, thrower, args:FunctionArguments) {
  8.     super(message, thrower, args);
  9.   }
  10. }

We now have all classes to begin writing our LoggingAspect and AroundLoggedOperationsAdvice.

  1. import org.swacker.aop.Aspect;
  2. import org.swacker.aop.aspect.AbstractAspect;
  3.  
  4. /**
  5. * @author Simon Wacker
  6. */
  7. class LoggingAspect extends AbstractAspect implements Aspect {
  8.   public function LoggingAspect(Void) {
  9.     // this ensures the the class Account is loaded when the weaving process starts
  10.     Account;
  11.     // adds the affected type ‘Account’ to search for advised join points in
  12.     addAffectedTypes("Account");
  13.     // adds the advice ‘AroundLoggedOperationsAdvice’ that advices join points it captures
  14.     addAdvice(new AroundLoggedOperationsAdvice(this));
  15.   }
  16.    
  17.   public function getLoggedOperationsPointcut(Void):String {
  18.     // the pointcut in form of a string specifying the join points to be captured
  19.     return "execution(Account.debit()) || execution(Account.credit())";
  20.   }
  21. }

What we are doing here is extending the class AbstractAspect that provides us with a few basic functionalities like the weaving algorithm, the addAdvice() and the addAffectedTypes() operation. In the constructor we add the affected type ‘Account’ and the advice ‘AroundLoggedOperationsAdvice’. When the LoggingAspect.weave() operation is called, the method will look inside the added affected types for join points that are being captured by added advices.

  1. import org.as2lib.env.except.Throwable;
  2. import org.swacker.aop.advice.AroundAdvice;
  3. import org.swacker.aop.advice.AbstractAdvice;
  4. import org.swacker.aop.JoinPoint;
  5.  
  6. /**
  7. * @author Simon Wacker
  8. */
  9. class AroundLoggedOperationsAdvice extends AbstractAdvice implements AroundAdvice {
  10.   public function AroundLoggedOperationsAdvice(aspect:LoggingAspect) {
  11.     // sets the pointcut
  12.     // this operation is declared by the class AbstractAdvice
  13.     setPointcut(aspect.getLoggedOperationsPointcut());
  14.   }
  15.    
  16.   public function execute(joinPoint:JoinPoint, args:FunctionArguments) {
  17.     // the code to be executed when a captured join point is called
  18.     trace(joinPoint.getInfo().getDeclaringType().getName() + "." + joinPoint.getInfo().getName() + "(" + args + ")");
  19.     trace("Before: " + Account(joinPoint.getThis()).getBalance());
  20.     var result;
  21.     try {
  22.       result = joinPoint.proceed(args);
  23.     } catch (exception:InsufficientBalanceException) {
  24.       trace(joinPoint.getInfo() + " throwed: " + exception.getClass().getFullName());
  25.     }
  26.     trace("After: " + Account(joinPoint.getThis()).getBalance());
  27.     trace("——————————————");
  28.     return result;
  29.   }
  30. }

We are extending the AbstractAdvice that like the AbstractAspect offers needed operations like setPointcut() and an implementation of the captures() operation. We add the captured pointcut in the constructor. Then we implement the execute() operation that will be executed when any captured join point is being called. Because we are working with an around advice we have to call the proceed() operation on the join point. Otherwise the execution of the join point will be bypassed.
Now let’s write a simple test to look if it works.

  1. import org.swacker.aop.Aspect;
  2.  
  3. var aspect:Aspect = new LoggingAspect();
  4. aspect.weave();
  5.  
  6. var account:Account = new Account();
  7. account.credit(1000);
  8. account.debit(500);
  9. account.debit(520);
  10. account.debit(480);

The output should look like this:

  1. Account.credit(1000)
  2. Before: 0
  3. After: 1000
  4. ——————————————
  5. Account.debit(500)
  6. Before: 1000
  7. After: 500
  8. ——————————————
  9. Account.debit(520)
  10. Before: 500
  11. Account.debit() throwed: InsufficientBalanceException
  12. After: 500
  13. ——————————————
  14. Account.debit(480)
  15. Before: 500
  16. After: 20
  17. ——————————————

That’s it. Easy isn’t it? ;) Download the source code of the example.

Saturday, June 5th, 2004

AOP: AOP Framework for Flash Version 0.1

I have been playing around with an AOP framework for Flash for some time now and I think it is time to publish it. Right now it offers the basic AOP constructs, represented by classes, like different kinds of Advices, Pointcuts, JoinPoints and Aspects. The framework is also extensible. You could ‘easily’ add new kinds of Advices or Pointcuts. What it is lacking are lexical-structure, control-flow based pointcuts and performance (I’m not a good speed optimizer or algorithm writer). But I hope to solve all this issues in future releases. If you wanna help me, you are welcome.
Click the following link for a little UML class diagram that depicts the whole structure: AOP Framework Class Diagram.
I have also written an example to show how to use it. It is contained in the download (test/org/swacker/aop/test.fla).
Download the AOP framework and the example.
You also have to download the as2lib becuause the framework is based on it.
Download the as2lib (this version is a reviewed/newer one and not the one you can download on www.as2lib.org).