Aspect oriented Everything?

Hung Jung Lu hungjunglu at yahoo.com
Wed Aug 27 12:10:17 EDT 2003


Jason Williams <jason at jasonandali.org.uk> wrote in message news:<slrnbkot3p.1d5.jason at kotu.jasonandalishouse.org.uk>...
> In article <8ef9bea6.0308261857.1b44a5b7 at posting.google.com>, Hung Jung Lu wrote:
> > If you write programs in OOP long enough, you will realize that there
> > are code spots that are not factorized. For example, you register to a
> > event listener at the beginning and de-register at the end of a
> > method. 
> 
> Eh? What's wrong with;
> 
>   def methodThatHasToListenForAnEvent
>     listenForEvent(e) do
>       # The method stuff goes here
>     end
>   end

The question is: are there code spots that are not factored? If you
have ONE single class that has to implement the before, around, or
after methods, sure, nothing wrong with what you have said. But, if
you have

class A:
   def f1():
       register()
       ...
       deregister()
class B:
   def f2():
       register()
       ...
       deregister()
class C:
    def f3():
       register()
       ...
       deregister()

You start to ask your self: how come the register() deregister() parts
are not factor out? How can I factor out these parts of code?

Why do you want to factor them out? You may ask. Because sometimes
later, you may realize that, heh, actually you want to do one more
thing before calling f1() f2() f3(): at beginning of the calls you
want to log the method call. If you don't have AOP, you would have to
manually modify each class into:

class A:
   def f1():
       log()
       register()
       ... non-factorizable code specific to f1
       deregister()
class B:
   def f2():
       log()
       register()
       ... non-factorizable code specific to f2
       deregister()
class C:
    def f3():
       log()
       register()
       ... non-factorizable code specific to f3
       deregister()

And later, you find out that you want to catch an certain type of
exception and respond properly, without AOP, you go back to your code
and write something like:

class A:
   def f1():
       try:
           log()
           register()
           ... non-factorizable code specific to f1
           deregister()
       except:
           ...
class B:
   def f2():
       try:
           log()
           register()
           ... non-factorizable code specific to f2
           deregister()
       except:
           ...
class C:
    def f3():
       try:
           log()
           register()
           ... non-factorizable code specific to f3
           deregister()
       except:
           ...

And then you realize that, oh, when the exception happens, you need to
do some clean up, then you go back to your code and do

class A:
   def f1():
       try:
           log()
           register()
           ... non-factorizable code specific to f1
           deregister()
       except:
           ...
       finally:
           ...
class B:
   def f2():
       try:
           log()
           ... non-factorizable code specific to f2
           ...
           deregister()
       except:
           ...
       finally:
           ...
class C:
    def f3():
       try:
           log()
           register()
           ... non-factorizable code specific to f3
           deregister()
       except:
           ...
       finally:
           ...

And then, someone tells you that they want to know the time spent in
these methods, so you do:

class A:
   def f1():
       start_timer()
       try:
           log()
           register()
           ... non-factorizable code specific to f1
           deregister()
       except:
           ...
       finally:
           ...
       end_timer()
class B:
   def f2():
       start_timer()
       try:
           log()
           ... non-factorizable code specific to f2
           ...
           deregister()
       except:
           ...
       finally:
           ...
       end_timer()
class C:
    def f3():
       start_timer()
       try:
           log()
           register()
           ... non-factorizable code specific to f3
           deregister()
       except:
           ...
       finally:
           ...
       end_timer()

And it is at this point that you start to wonder, man, it's tedious
and error-prone trying to do the something to all the classes that
share similar functionalities. And at the moment, you start to wonder
whether you can factor out the similarities. Notice that OOP or class
inheritance will not allow you to factor out these types of
"horizontal common code spots". A way to see it is to have three
sheets of paper, and you write the code of class A, B, C on each
sheet, and stack the three sheets together. The common areas that
overlap are in a horizontal direction. This type of horitontal
factorization is what AOP is all about. Once you factor out the common
parts, you can modify the code spot just once, and it will be applied
automatically to all your classes.

To my, horizontal factorization is what AOP is all about. It goes
beyond the before-, around-, after- hooks. I've written codes where I
have many if statements in a base class method:

def f():
    #--------------- step 1 during calculation
    code shared under all circumstances
    #--------------- step 1 during calculation
    if self.has_fixed_rate():
       ....
    else:
       ....
    if self.is_government_bond():
       ....
    else:
       ....
    if self.is_domestic():
       ....
    else:
       ....
    #--------------- step 2 during calculation
    code shared under all circumstances
    #--------------- step 3 during calculation
    if self.has_fixed_rate():
       ....
    else:
       ....
    if self.is_domestic():
       ....
    else:
       ....
    #--------------- step 4 during calculation
    code shared under all circumstances
    #--------------- step 5 during calculation
    if self.is_domestic():
       ....
    else:
       ....
    if self.is_government_bond():
       ....
    else:
       ....

After writing so many if...else... statement, you start to wonder: can
I factor out these if...else... statements? One way is to use OOP and
subclasses to encapsulate the is_domestic(), is_government_bond(),
has_fixed_rate() features, (e.g: have a subclasses like
domestic_fixed_rate_government_bond
foreign_variable_rate_corporate_bond, etc.), but in OOP you will find
out that common steps 1,2,4 will not be factored out, and that when
you need to change the code in the common steps, you need to change in
all subclasses, which is tedious and error-prone. Worse, with 3
features you have a combination of 8 subclasses, and if one day you
include one more feature, you will have 16 subclasses. Are you going
to change the code manually in 16, 32, 64 classes? Clearly inheritance
is not the way to implement properties/features like these ones. OOP
just cannot solve the problem.

It is only when you run into this kind of situations, that you start
to think about code factorization in a different dimension.

AOP is a really need. I would agree that it is still an immature field
of research. But people really need it.

Hung Jung




More information about the Python-list mailing list