Jason Williams <ja***@jasonandali.org.uk> wrote in message news:<sl******************@kotu.jasonandalishouse. org.uk>...
In article <8e**************************@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