Pure Aspect-Oriented Program: an example

Discussion in 'Python' started by Hung Jung Lu, Nov 6, 2003.

  1. Hung Jung Lu

    Hung Jung Lu Guest

    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.
    Hung Jung Lu, Nov 6, 2003
    #1
    1. Advertising

  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. :cool:

    --
    Richie Hindle
    Richie Hindle, Nov 6, 2003
    #2
    1. Advertising

  3. 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.
    Taliesson Wang, Nov 6, 2003
    #3
  4. Hung Jung Lu

    Hung Jung Lu Guest

    Richie Hindle <> wrote in message news:<>...
    >
    > 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. :cool:


    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/vanderb...egor_kiczales_aspect_oriented_programming.pdf

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

    Hung Jung
    Hung Jung Lu, Nov 8, 2003
    #4
  5. Hung Jung Lu

    Jay O'Connor Guest

    On 7 Nov 2003 22:37:27 -0800, (Hung Jung Lu)
    wrote:

    >Richie Hindle <> wrote in message news:<>...
    >>
    >> 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. :cool:

    >
    >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
    Jay O'Connor, Nov 8, 2003
    #5
  6. Hung Jung Lu

    Hung Jung Lu Guest

    (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
    Hung Jung Lu, Nov 9, 2003
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. New_aspect

    Aspect oriented Everything?

    New_aspect, Aug 22, 2003, in forum: Perl
    Replies:
    5
    Views:
    1,091
    Robert Will
    Aug 31, 2003
  2. Julia Donawald
    Replies:
    0
    Views:
    494
    Julia Donawald
    Dec 30, 2004
  3. christopher diggins

    Aspect Oriented Programming techniques

    christopher diggins, Feb 9, 2004, in forum: C++
    Replies:
    15
    Views:
    759
    Maciej Sobczak
    Feb 11, 2004
  4. Hung Jung Lu
    Replies:
    2
    Views:
    177
    Hung Jung Lu
    Nov 9, 2003
  5. Richard
    Replies:
    2
    Views:
    73
    Richard
    Dec 1, 2006
Loading...

Share This Page