473,408 Members | 2,832 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,408 software developers and data experts.

Pure Aspect-Oriented Program: an example

Hi,

I have been looking into AOP (Aspect-Oriented Programming) for
sometime, now. I frankly don't like the syntax of any of the
approaches I have seen so far. I am kind playing around with some
ideas, and maybe write up an article later.

AOP is not just buzzword. It's not just callback, it's not just Ruby's
MixIn, it's not just Python's metaclass, it's not just C++'s template.
AOP can be implemented/considered as a subset of metaprogramming... an
important subset that stands on its own. AOP deserves its name,
because one really can think and program in "aspects" instead of
"objects". That being said, I have never seen an example of purely
aspect-based program. So, I thought I'd write up one.

Here it is, and let's get some feedback.

thanks,

Hung Jung

--------------------

Suppose you have to implement a checking account that allows
withdrawal of money. Your first simple implementation may look like:

class Account {
real balance
method withdraw(amount) {
this.balance = this.balance – amount
}
}

A program that withdraws money may look like:

account = new Account()
...
amount = 100
account.withdraw(amount)

In order to simplify the discussion, in the following I will omit all
initialization code details, and assume that variable values have been
properly assigned elsewhere. Also notice that everything is in
pseudocode, I am not using any particular language as base.

After you have implemented the program, your bank manager comes and
tells you there are constraints to the withdrawal of money. For
instance, an account maybe disabled. In OOP, you would modify your
Account class to:

class Account {
real balance
bool enabled
method withdraw(amount) {
if not this.enabled
raise AccountDisabledException
this.balance = this.balance – amount
}
}

Now, stop thinking in OOP. Let us try to think in AOP.

//---------------------------------------------------
class Account {
real balance
method withdraw(amount):
this.balance = this.balance – amount
}

//---------------------------------------------------
aspect AccountStatus {
bool enabled
codeblock check_status {
if not this.enabled:
raise AccountDisabledException
}
method withdraw(...) {
this.check_status
this.withdraw.code
}
}

//---------------------------------------------------
endow Account with AccountStatus
The general idea is to have the concerns implemented outside the
object. We have added a new feature to the account (namely, the
account status,) without having to tweak the original code of the
account. Notice that we have three parts to the above code: a class
definition, an aspect definition, and a final part to endow the aspect
to the class. The last part is also known as the "aspect weaver".

Let us proceed to the next step. Say, there is a maximum daily
withdrawal limit.

//---------------------------------------------------
class Account {
real balance
method withdraw(amount) {
this.balance = this.balance – amount
}
}

//---------------------------------------------------
aspect AccountStatus {
bool enabled
codeblock check_status {
if not this.enabled:
raise AccountDisabledException
}
method withdraw(...) {
this.check_status
this.withdraw.code
}
}

//---------------------------------------------------
aspect WithdrawalLimit {
real daily_limit
real withdrawn_today
codeblock check_and_update_withdraw {
new_withdrawn_today = this.withdrawn_today + amount
if new_withdrawn_today > this.daily_limit:
raise WithdrawalLimitExceededException
&inner_code
this.withdrawn_today = new_withdrawn_today
}
method withdraw(...) {
this.check_and_update_withdraw {
...
&inner_code = this.withdraw.code
...
}
}
}

//---------------------------------------------------
endow Account with AccountStatus
endow Account with WithdrawalLimit

Notice the usage of a hook (also known as pointcut): &inner_code. In
general, a code block or a method can contain one or many hooks. Hooks
allow future enhancement of code. There are two implicit hooks: the
before and the after hooks. But for readability of AOP code, in our
approach we treat named hooks very differently from implicit hooks.
Ruby users should notice that a code block here can contain multiple
hooks, and that the direction of hooking process is kind of opposite
to Ruby's MixIn.

The above way of programming of course takes longer to write. But, the
advantage is in the degree of decoupling (or "separation of concerns")
that is achieved. Say, one day, we want to eliminate the feature on
withdraw limit, it is as simple as commenting out one single line of
code:

endow Account with AccountStatus
//endow Account with WithdrawalLimit

The same is true if one day we want to eliminate the account status
feature:

//endow Account with AccountStatus
endow Account with WithdrawalLimit

Also, once the concerns are separated, it is easier to modify the
aspects individually.

A careful reader would point out that we may want to check the account
status before checking withdrawal limit. Notice also that we have
applied two aspects in sequence. But we can pre-compose two aspects
into one, and apply the composed aspect just once.

//---------------------------------------------------
aspect AccountAspects inherits AccountStatus, WithdrawalLimit {
method withdraw(...) {
this.check_status
this.check_and_update_withdraw {
...
&inner_code = this.withdraw.code
...
}
}
}
//---------------------------------------------------
endow Account with AccountAspects

At this point you may say: "Hey! We started with a non-trivial class…
but we could have started with an empty class, and endow the
balance-calculating feature as an aspect, too." Bingo! Now you are
thinking in AOP.

//---------------------------------------------------
class Account {
}
//---------------------------------------------------
aspect BalanceKeeping {
real balance
method withdraw(amount) {
this.balance = this.balance – amount
}
}

//---------------------------------------------------
aspect AccountStatus {
bool enabled
codeblock check_status {
if not this.enabled:
raise AccountDisabledException
}
method withdraw(...) { // this meta-method is overriden later
this.check_status
this.withdraw.code
}
}

//---------------------------------------------------
aspect WithdrawalLimit {
real daily_limit
real withdrawn_today
codeblock check_and_update_withdraw {
new_withdrawn_today = this.withdrawn_today + amount
if new_withdrawn_today > this.daily_limit:
raise WithdrawalLimitExceededException
&inner_code
this.withdrawn_today = new_withdrawn_today
}
method withdraw(...) { // this meta-method is overriden later
check_and_update_withdraw {
...
&inner_code = this.withdraw.code
...
}
}
}

//---------------------------------------------------
aspect AccountAspects: inherits BalanceKeeping,
AccountStatus,
WithdrawalLimit {
method withdraw(...) {
check_status
check_and_update_withdraw { // this meta-method overrides
...
&inner_code = this.withdraw.code
...
}
}
}
//---------------------------------------------------
endow Account with AccountAspects
print Account.codeString()

Notice that we have started with a bare class with no attributes. All
the features of the class Account have been implemented by aspects,
instead of interfaces or base classes. You may complain: the code is
now too hard to read and follow. But that actually is a problem of IDE
(Integrated Development Environment). For more advanced IDEs, editing
aspects actually should not be bad at all. It's almost like editing
the header and footer information in a Microsoft Word document. At any
rate, the last statement above would print out the code for the
"aspected class", which may look something like:

class Account {
real balance
bool enabled
real daily_limit
real withdrawn_today
method withdraw(amount) {
if not this.enabled:
raise AccountDisabledException
new_withdrawn_today = this.withdrawn_today + amount
if new_withdrawn_today > this.daily_limit:
raise WithdrawalLimitExceededException
this.balance = this.balance – amount
this.withdrawn_today = new_withdrawn_today
}
}

A good IDE/compiler/debugger can provide additional information on
where each code line or code block comes from, hence making debugging
and editing a snap. But we don't need to get into that discussion,
now.

What have we learned? We have learned that:

1. We can write a purely-AOP program.
2. Aspect inheritance can be used to compose derived aspects.
3. A new type of object: "codeblock", becomes the fundamental building
block of programs. So, in general we have to consider the (data,
codeblock, method) trio when building classes or programs. A method
consists of its "header/prototype" and a reference to its codeblock.
Also, in a real program, most codeblocks would be anonymous.
4. Despite of its innocent look, the code specifying an aspect
contains disguised meta-programming instructions.

Codeblock-based AOP actually is more suitable in programming languages
powered with meta-programming features. Also, (data, codeblock,
method) are all supposed to be virtual and overridable. Data
overriding does not do anything, if the data is already present.
Codeblock overriding does not present problem. The main problem is
with method overriding. If the new method refers to the existing
method, there is a problem as how to deal with the names and who will
hold reference to the old method once the new method is in place. For
the AOP practitioners, this means that the "before" and "after"
advices can be implemented more easily, where as the "around" advice
is trickier. A possible solution is to "decorate" the name of the old
implementation. Here I only present a possible syntax of the "around"
advice.

class C {
method f() {
return 3
}
}

aspect A {
method f_before_A = f
method f(...) {
print 'begin around-advice'
result = this.f_before_A()
print 'end around-advice'
return result
}
}

endow C with A
c = new C()
print c.f()

//------ output
begin around-advice
end around-advice
3

One thing I have not mentioned is pattern matching for method names.
An aspect may affect many or all the methods in a class. In that case,
the name of the methods should be listed, or wildcards (possibly
regular expressions) must used to pattern-search for them. Possible
syntax variations are:

aspect A {
method <f>(...) for f in f1, f2, f3 {
}
}

aspect B {
method <f>(...) for f matching 'f*' {
}
}

Another thing that I have not mentioned is that aspects can be endowed
to different classes. For instance, if later we have checking accounts
and savings accounts, we can apply the same aspect to both classes.
That's what other people refer to as "cross-cutting".

.... That's all for now.
Jul 18 '05 #1
5 3077
Yes, AOP is more like a metaprogramming.
It supplies a vertical-cut reconstrcution way to OOP.
I'm now writing something about rule-base system & meta-analysis.And hope
this will do something for AOP.

Jul 18 '05 #2

[Hung Jung]
I have never seen an example of purely
aspect-based program. So, I thought I'd write up one.
Here it is, and let's get some feedback.


Fantastic! That's the best introduction to the concepts of AOP I've seen.
Many thanks for a very clear and thought-provoking article.

Now please implement it for Python, including your proposed IDE. 8-)

--
Richie Hindle
ri****@entrian.com
Jul 18 '05 #3
Richie Hindle <ri****@entrian.com> wrote in message news:<ma************************************@pytho n.org>...

Fantastic! That's the best introduction to the concepts of AOP I've seen.
Many thanks for a very clear and thought-provoking article.

Now please implement it for Python, including your proposed IDE. 8-)


Yeah, right. :)

I do realize that people are converging in their ideas. AOP is
becoming more and more clear with each passing day. That's really
exciting. But I believe things have not yet finalized, and I am not a
"marketoid" to go out and sell half-baked ideas. :)

Java folks have economical resources, but their language puts so much
handcuff on them that AOP there is kind of hard to use. Ruby is
perhaps the language that's easiest to accomodate, and Python comes a
close second. As for IDEs, you can bet Java folks will have something
before anyone else. Although I am not sure why people'd keep investing
effort into such a rigid language.

See for instance:

http://www.hpcc.gov/iwg/sdp/vanderbi...rogramming.pdf

where people are focusing a bit on IDE already, though theirs is not
nearly as good as my proposed one. :)

Hung Jung
Jul 18 '05 #4
On 7 Nov 2003 22:37:27 -0800, hu********@yahoo.com (Hung Jung Lu)
wrote:
Richie Hindle <ri****@entrian.com> wrote in message news:<ma************************************@pytho n.org>...

Fantastic! That's the best introduction to the concepts of AOP I've seen.
Many thanks for a very clear and thought-provoking article.

Now please implement it for Python, including your proposed IDE. 8-)


Yeah, right. :)

I do realize that people are converging in their ideas. AOP is
becoming more and more clear with each passing day. That's really
exciting. But I believe things have not yet finalized, and I am not a
"marketoid" to go out and sell half-baked ideas. :)

Java folks have economical resources, but their language puts so much
handcuff on them that AOP there is kind of hard to use. Ruby is
perhaps the language that's easiest to accomodate, and Python comes a
close second. As for IDEs, you can bet Java folks will have something
before anyone else. Although I am not sure why people'd keep investing
effort into such a rigid language.


AFAIK, Aspect Oriented Programming is already available in a few
Smalltalk dialects, possibly even support within the IDEs
Jul 18 '05 #5
hu********@yahoo.com (Hung Jung Lu) wrote in message
Here I only present a possible syntax of the "around" advice.
...
aspect A {
method f_before_A = f
method f(...) {
print 'begin around-advice'
result = this.f_before_A()
print 'end around-advice'
return result
}
}


(I changed comp.lang.java to comp.lang.java.programmer)

Yikes, that's awfully bad. It violates functional programming
philosophy of the meta-methods.

I finally figured out a cleaner syntax for the "around" advice, using
@-decorated names and method hooks.

class M {
method f(x) {
print 'multiply by 2'
result = 2 * x
print 'result =', result
return result
}
}

aspect A {
codeblock f_with_A {
print 'post-multiply by 3'
result = 3 * &f(...)
print 'result =', result
return result
}
method f@A(...) {
this.f_with_A {
&f = this.f@
}
}
}

aspect B {
codeblock f_with_B {
print 'pre-multiply by 4'
x = 4 * x
result = &f(...)
print 'result =', result
return result
}
method f@B(...) {
this.f_with_B {
&f = this.f@
}
}
}

aspect C inherits A, B {
method f@C(...) {
this.f_with_B {
&f = this.f@A
}
}
}

endow M with C
m = new M()
x = 1
print 'input =', x
print m.f(x)

//------ output
input = 1
pre-multiply by 4
post-multiply by 3
multiply by 2
result = 8
result = 8
result = 24

Notice that I have used a method hook &f, not a codeblock hook. Notice
also the usage of (...) for signature decoupling.

Aspect C will create the "@-decorated" methods f@A, f@B and f@C for
class M. The original method can always be accessed as f@(x). When
f(x) is accessed without name decoration, the latest @-decorated
implemention is used. However, in order to avoid meta-method
ambuiguities in multiple-aspect inheritance, the hooking process
should always be done with explicit @-decorated names. If you really
don't like a particular meta-method, you can always override it. So I
think usage of explicit @-decorated name inside a meta-method should
be OK.

I guess the @-decoration could be used in all meta-methods, that is,
even for the cases of "before" and "after" advices.

Hung Jung
Jul 18 '05 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

51
by: Noam Raphael | last post by:
Hello, I thought about a new Python feature. Please tell me what you think about it. Say you want to write a base class with some unimplemented methods, that subclasses must implement (or...
5
by: Fuzzyman | last post by:
Python 2.4 is built with Microsoft Visiual C++ 7. This means that it uses msvcr7.dll, which *isn't* a standard part of the windows operating system. This means that if you build a windows installer...
11
by: santosh | last post by:
Hello, I was going through the Marshal Cline's C++ FAQ-Lite. I have a doubt regarding section 33.10. Here he is declaring a pure virtual destructor in the base class. And again defining...
13
by: junky_fellow | last post by:
I have read certain articles that encourage to use/write pure functions (if possible) as they are better suited for optimization. I got one example that expalins how the code can be optimised....
5
by: Ron Vecchi | last post by:
I know the math I need to perform on width and height to keep an aspect ratio but where and how would I implement keeping a set aspect ratio on a form when a user resizes it. Override OnResize?...
8
by: Ted Miller | last post by:
Hi folks, I'm looking at moving a large base of C++ code to .Net under tight time constraints. The code runs in mission-critical environments, and I am extremely concerned about the loader lock...
6
by: pakis | last post by:
I am having a problem of pure virtual function call in my project. Can anyone explaine me the causes of pure virtual function calls other than calling a virtual function in base class? Thanks
5
by: =?Utf-8?B?UmljaA==?= | last post by:
Hello, If I create a form in Java with controls like Panels, textboxes... when I stretch/shrink the form, all the controls can grow/shrink - along with the text contained in the textboxes. This...
5
by: =?Utf-8?B?U2hhcm9u?= | last post by:
I have a class that is writen in unmanaged pure native C++. This class files (h and cpp) are inserted to a managed C++ (VC++ 2005, C++/CLI) DLL compoenet. This DLL compoenet is used in a C#...
14
by: Jack | last post by:
Hi, I meet a question with it , I did not get clear the different betteen them, for example: #include <iostream>
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.