Page 1 of 3

Event-driven programming

Posted: Fri Jan 07, 2011 12:07 am
by Benjamin
I'd like to get your opinions on this because it's not anything I've ever ran into with a web based application. I came up with an idea for an application to write. 40 hours into it I realized it was quickly turning into an unmanageable codebase, even though it's 100% MVC.

The problem with the code is that there are many interdependencies between the models that grow exponentially each time features are added. To put it another way, lets say the system receives an order for a car. There can be many types of car. Each car has thousands of parts. Each part needs to be ordered, customized (painted etc.), assembled, tested, cleaned and any number of things. Even identical cars may require different parts, different assembly processes etc depending on the options. Thousands of things need to happen in the correct order when an order is received.

Obviously, putting all this into models would be a nightmare and duplicate code would start to suddenly appear everywhere. I searched in vain and pondered, what would be the best way to add some sort of layer where I could monitor all the method calls to the models and inject code on the fly. There's no elegant solution to this problem in PHP. I'm not sure there is in any programming language.

Next I looked into the observer pattern. It didn't quite "fit" into what I needed to accomplish nor did it feel right, but I think it put me on the right track.

I think the best solution to this problem is to be able to inject "drivers" or "modules" into the code on the fly in order to keep the code understandable by our feeble minds. I like calling them drivers but I guess technically a driver is a hardware interface so I called them modules.

Here is the class I wrote that stores modules which are executed when events are triggered. I wanted to type hint the $class coming in through the attach method in order to ensure it was implementing a specific interface but this requires sending the event handler the object. I didn't want to do this for performance reasons.

Code: Select all

<?php
class event_handler {
    private static
        $_events = array();

    private function __construct() { }

    /**
     * Adds an event handler to the event stack
     *
     * @param string $event The event name
     * @param string $class The class name to trigger
     * @param string $method The method name to trigger
     */
    public static function attach($event, $class, $method) {
        self::$_events[$event][$class] = $method;
    }

    /**
     * Executes all event handlers assigned to the specified event
     *
     * @param string $event The event name
     * @return bool true or false if no events were executed
     */
    public static function trigger($event) {
        if (!isset(self::$_events[$event])) {
            return false;
        }

        $args = array_slice(func_get_args(), 1);

        foreach (self::$_events[$event] as $class => $method) {
            $i = new $class();
            $i->$method($args);
        }

        return true;
    }

    /**
     * Removes an event handler from the event stack
     *
     * @param string $event The event name
     * @param string $class The class to detatch
     */
    public static function detach($event, $class) {
        unset(self::$_events[$event][$class]);
    }

    /**
     * Removes all event handlers from the specified event
     *
     * @param string $event The event name
     */
    public static function detachAll($event) {
        unset(self::$_events[$event]);
    }

    /**
     * Clears all events
     *
     */
    public static function reset() {
        self::$_events = array();
    }
}
Event handlers are added with this:

Code: Select all

event_handler::attach('ADD_CAR_ORDER', 'add_car_parts_module', 'exec');
There could then be 15 event handlers attached to the add_car_parts model, each of those events could in turn trigger numerous events and so on and so forth.

The models trigger events with this which can take any number of arguments.

Code: Select all

event_handler::trigger('ADD_CAR_ORDER', $data);
Finally, here is what a module looks like:

Code: Select all

<?php
class add_car_parts_module {
    public function exec($data) {
        $eam = new add_car_parts_model();
        if (isset($data[0]['parts_list']) && is_array($data[0]['parts_list']) && count($data[0]['parts_list']) > 0) {
            foreach ($data[0]['parts_list'] as $row) {
                $ins = array(
                    'order_id'  => $data[0]['id'],
                    'type'      => $row['type'],
                    'name'      => $row['name'],
                );

                $eam->add($ins);
            }
        }
    }
}
What this enables me to do is configure the code dynamically depending on what needs to be done. I think this will work great but the project isn't finished yet so I can't say for sure. Does anyone here have any experience with this methodology?

Re: Event-driven programming

Posted: Fri Jan 07, 2011 12:56 am
by josh
I use wrappers. I'll have a simple vehicle object with Make/Model/Year. There is tons of operations for different contexts, like in a "tire" context we care about aspect ratios, diameters, section widths. So if a programmer in my system wants to work with a vehicle in a "tire size" sort of way, he must first instantiate the Tire Vehicle Wrapper and pass the "normal" vehicle to that wrapper.

Code: Select all

<?php
class Elite_Vaftire_Model_Vehicle
{
    /** @var Elite_Vaf_Model_Vehicle */
    protected $wrappedVehicle;
    
    function __construct(Elite_Vaf_Model_Vehicle $vehicle )
    {
        $this->wrappedVehicle = $vehicle;
    }
    
    function vehicle()
    {
    	return $this->wrappedVehicle;
    }

    /** redacted tons of tire specific operations **/
    /** redacted tons of tire specific operations **/
    /** redacted tons of tire specific operations **/
    
    function __call($methodName,$arguments)
    {
        $method = array($this->wrappedVehicle,$methodName);
        return call_user_func_array( $method, $arguments );
    }
    
}
Each module wraps objects from its dependent modules, to extend their behavior in different contexts. Methods from the wrapped object are proxied thru with __call() It allows me to arrange my modules into "packages" for distribution. Some users have access to the tire features, other users may just have the basic stuff.

Re: Event-driven programming

Posted: Fri Jan 07, 2011 12:57 am
by matthijs
There are probably others with more experience who can give better answers, but for me when I see the code I wonder what it really adds. It seems like the event_handler is a big bucket in which you put functions/methods in a big list to be run sequentially. Wouldn't that be about the same as just running a few global procedural functions in a row?

Code: Select all

function doThis(){
  $x = doThat();
}
function doThat(){
  $y = doSomethingElse($var);
}
function doSomethingElse($var){}
My understanding of trying to code in objects is that you don't care about all inter-dependencies at once. You only deal with the dependencies of each smaller piece. For example, the car object would only need the parts injected and then calculates the car price:

Code: Select all

$somecar = new Car();
$somecar->setParts($allkindsofparts);
$price = $somecar->getPrice();
You don't care about the calculation of the parts prices. Only inside the Car object calls are made to the different objects to calculate the subprices

Code: Select all

class Car {
  public function getPrice(){
    foreach($this->parts as $part) {
      $carPart = new Part();
      $partprice = $carPart->getPrice();
       $this->addToTotal($partprice); 
    }
  }
}
And of course each part can also in itself calculate its own price depending on other subparts.

Maybe you already have gone this way, but is this not working?

Re: Event-driven programming

Posted: Fri Jan 07, 2011 1:15 am
by Benjamin
josh wrote:I use wrappers. I'll have a simple vehicle object with Make/Model/Year. There is tons of operations for different contexts, like in a "tire" context we care about aspect ratios, diameters, section widths. So if a programmer in my system wants to work with a vehicle in a "tire size" sort of way, he must first instantiate the Tire Vehicle Wrapper and pass the "normal" vehicle to that wrapper.
Ok, so lets say that:

1. Certain models come with chains (for the tires)
2. Vehicles in certain regions come with chains
3. Certain vehicles optionally offer chains
4. The chains come in different types/sizes and may or may not need to be installed on the assembly line.

Would this be handled by the (or various) wrapper? Do you think that's a pretty clean way to accomplish this?

Re: Event-driven programming

Posted: Fri Jan 07, 2011 1:20 am
by Benjamin
matthijs wrote:It seems like the event_handler is a big bucket in which you put functions/methods in a big list to be run sequentially.
I wouldn't say that. It simply tracks what should be done when specific events occur. Javascript for example allows you to trigger actions based on events. This is a way to accomplish this in PHP.
matthijs wrote:My understanding of trying to code in objects is that you don't care about all inter-dependencies at once. You only deal with the dependencies of each smaller piece.
Well that's true. But let's say your model adds a record. The model has information about the record it added including the insert id. After a record has been added you may or may not want to do a bunch of things which can in turn may or may not require a bunch of other things to be done. What's the best way to do that?
matthijs wrote:Maybe you already have gone this way, but is this not working?
Well it works, but it's a mess and becomes very hard to wrap your head around what is happening when you get over 3 or 4 levels deep. By injecting modules into the models I can simply view of a list (by reading the code) of what happens when x happens.

Re: Event-driven programming

Posted: Fri Jan 07, 2011 2:45 am
by Christopher
Benjamin wrote:Obviously, putting all this into models would be a nightmare and duplicate code would start to suddenly appear everywhere.
You make this statement, but is it really true? It sounds like you have a lot of data and different data points, but is the code really that complex?
Benjamin wrote:Ok, so lets say that:

1. Certain models come with chains (for the tires)
2. Vehicles in certain regions come with chains
3. Certain vehicles optionally offer chains
4. The chains come in different types/sizes and may or may not need to be installed on the assembly line.

Would this be handled by the (or various) wrapper? Do you think that's a pretty clean way to accomplish this?
I think Josh is heading you in the right direction, but I might start with data that describes a Vehicle and data that describes a Part. Then deal with with things like whether parts are original, regional, optional, etc. I assume this information is either currently in a database or needs to be in a database. Once you have that then you can create classes for all the types of things. Given that it is mostly a lot of boring details -- maybe code generation based on the data in the database. So you would derive your Entity classes from the data.

Re: Event-driven programming

Posted: Fri Jan 07, 2011 3:01 am
by Benjamin
Christopher wrote:
Benjamin wrote:Obviously, putting all this into models would be a nightmare and duplicate code would start to suddenly appear everywhere.
You make this statement, but is it really true?
I think what it boils down to is that when certain events happen other events should happen as well. This could all be hard coded into the models of course but I predict down the road there will be times where I may not want anything to happen. So I guess what I'm aiming for is flexibility. The capability of adding modules gives me the flexibility to add/remove all sorts of stuff from all the various models without having to modify their code. I guess you could call them plugins.
Christopher wrote:It sounds like you have a lot of data and different data points, but is the code really that complex?
Yes. It was to the point where if I wanted to figure out what was causing something to happen I would have to start at an entry point and trace it through all the models to where it was occurring. With the event handler I can look and see that x happens because y happens. I know this may smell bad, but the code is actually quite clean, especially since all the models have been reduced to just the task they are supposed to perform and then trigger the event handler.

Re: Event-driven programming

Posted: Fri Jan 07, 2011 7:27 am
by josh
It sounds like what you want is re-use through components. For example see Zend's decorator's. Its a combination of decorator & strategy pattern. For example fulfilling the order could be done with $order->fulfill()

ok, what would happen inside, under the hood is there'd be an array "order fulfillment strategies", each would get passed to an order:

[syntax]
$order->AddFullfillmentStrategy( new Part_Assembler(array('foo'=>'bar')) );
$order->AddFullfillmentStrategy( new Customer_Notifier );
$order->fullfill(); // various strategies get invoked in a chain
[/syntax]
Benjamin wrote:I think what it boils down to is that when certain events happen other events should happen as well.
If that's true, event driven programming is for you! However I prefer the above example to one big global event stack, although either is fine by me (and I'm usually picky). I just hate when *everything* is run thru the event system as a default choice. Straight up PHP code should be the first choice, implementing events, or seasoning with wrappers/decorators only when truly necessary. In order of my preference it goes 1. method call 2. wrapper 3. decorator/strategy pattern thing ala Zend_Form 4. events
This could all be hard coded into the models of course but I predict down the road there will be times where I may not want anything to happen. So I guess what I'm aiming for is flexibility. The capability of adding modules gives me the flexibility to add/remove all sorts of stuff from all the various models without having to modify their code. I guess you could call them plugins.
I prefer code to config files. If you abstract, both will be at the same level of abstraction. The code will save you time. Although going the config file route makes it easier for people to think about their abstractions (unless you do test driven development, then you'll prefer code too in my opinion). For example in Magento all sorts of behavior is happening that is controlled by undocumented XML configuration files. Its horrible and at least if it was PHP code, the average person would be able to reverse engineer it (going from a class to it's super class is as easy as point & click. Trying to reverse engineer some complex XML file is a pain in the donkey)
Yes. It was to the point where if I wanted to figure out what was causing something to happen I would have to start at an entry point and trace it through all the models to where it was occurring. With the event handler I can look and see that x happens because y happens. I know this may smell bad, but the code is actually quite clean, especially since all the models have been reduced to just the task they are supposed to perform and then trigger the event handler.
I find a regular PHP stack trace much easier to trace, than debugging in an event based system. In event based systems you're usually on your own for debugging tools, if you made your own event framework this is especially true.

Re: Event-driven programming

Posted: Fri Jan 07, 2011 7:50 am
by Jenk
If you want some reading material about Event Driven programming, use the keywords "Event Sourcing" when searching. Plenty of info on it. As the title may suggest, everything is sourced from Events.

Re: Event-driven programming

Posted: Fri Jan 07, 2011 3:11 pm
by Benjamin
josh wrote:I just hate when *everything* is run thru the event system as a default choice.
Right, there needs to be a clear distinction between what should and shouldn't be handled by the event handler modules.
josh wrote:For example in Magento all sorts of behavior is happening that is controlled by undocumented XML configuration files. Its horrible and at least if it was PHP code, the average person would be able to reverse engineer it (going from a class to it's super class is as easy as point & click. Trying to reverse engineer some complex XML file is a pain in the donkey)
Yes, I agree. One may think their code is great because it uses all sorts of brand new best practices and methodologies, but if something breaks and it takes someone unfamiliar with the code 12 hours to figure out what broke, why it broke, and how to fix it, it's simply bad code. I prefer to keep things simple in the sense that any decent programmer can figure out what the hell the code is doing.
josh wrote:I find a regular PHP stack trace much easier to trace, than debugging in an event based system. In event based systems you're usually on your own for debugging tools, if you made your own event framework this is especially true.
This may sound crazy but I never use debuggers. I look at the code and execute it in my head. In debugging any application what needs to happen is that you identify the bug and are able to somehow efficiently identify the offending code. In this event based system it should be very easy. If a part is added incorrectly, we can look at the event attachments and determine what model triggered the part to be added. We can pull up the module (which we now also know the name of) and check that it's receiving and sending the correct data. If it's being received correctly and sent correctly we know the problem is in the model that the module is calling. If it's being received incorrectly we know it's the model triggering the event. If it's being received correctly but sent incorrectly we know it's the module itself.

That said, I don't like this one bit. I don't like putting this crap in every model:

Code: Select all

class foo extends foo_base {
    public function __construct() {
        # do some stuff
        parent::__construct();
    }

    public function add($data) {
        try {
             event_handler::trigger('BEFORE_ADD_CAR', $data);
             $data = filter_handler::trigger('FILTER_ADD_CAR', $data);

            if (false == $insert_id = parent::add($data)) {
                return false;
            }

            event_handler::trigger('AFTER_ADD_CAR', $data, $insert_id);

            return $insert_id;
        } catch (Exception $e) {
            $this->setError($e->getMessage());
            return false;
        }
    }
}
I don't like the wrapper approach either. I believe it would be a complex mess to build complex applications without the capability to intercept method calls internally. Think AI for example. I propose a modification to the PHP core adding the following features. (Not all inclusive, I'll try to include everything I think needs to be added).

I guess this called be interception. What it would enable you to do is intercept method calls immediately before and after they are executed. It would also enable you to filter the arguments sent to the methods.

We would need:
A way to enable this feature.
A way to add interceptions
A way to list interceptions
A way to remove interceptions
The ability to view interceptions in standard debug backtraces.

Here's an ugly version of it with only support for classes:

Code: Select all

interception::attachBefore('className', 'methodName', 'classToExecute', 'methodToExecute');
interception::attachFilter('className', 'methodName', 'classToExecute', 'methodToExecute');
interception::attachAfter('className', 'methodName', 'classToExecute', 'methodToExecute');

interception::detachBefore('className', 'methodName', 'classToExecute', 'methodToExecute');
interception::detachFilter('className', 'methodName', 'classToExecute', 'methodToExecute');
interception::detachAfter('className', 'methodName', 'classToExecute', 'methodToExecute');

$list = interception::getAll();
$list = interception::getForClass('className', [methodName]);

interception::clearAll();
When attaching an intercept before a method call, the methodToExecute will receive the exact same arguments as the intercepted methodName. Immediately after methodToExecute has completed, methodName will then be executed.

When attaching an intercept filter, the methodToExecute method will receive the exact same arguments as the intercepted methodName passed by reference (maybe). It can then modify the arguments if required.

When attaching an intercept after a method call, the methodToExecute will receive the exact same arguments as the intercepted methodName in addition to the return value of methodName.

If you guys can see how this would be beneficial, perhaps we should hammer out exactly how it should work and send it in as a proposed addition to the php core. I guess it's fairly similar to javascript in the sense that you can attach event handlers to objects in the dom.

Re: Event-driven programming

Posted: Fri Jan 07, 2011 9:03 pm
by josh
Benjamin wrote:This may sound crazy but I never use debuggers. I look at the code and execute it in my head.
False. You read error messages & stack traces, which are generated by debugging tools ;-) And you probably manually debug, as in print_r() here (at least occasionally). I do agree that over reliance on GUI debuggers leads to complex code, but the point is its available for those programmers who feel they need it (maybe your code wasn't as good as you thought, or maybe they aren't adept at "executing in their head")
Benjamin wrote:I guess this called be interception. What it would enable you to do is intercept method calls immediately before and after they are executed. It would also enable you to filter the arguments sent to the methods.

We would need:
A way to enable this feature.
A way to add interceptions
A way to list interceptions
A way to remove interceptions
The ability to view interceptions in standard debug backtraces.
Aspect Oriented Programming has been attempted in PHP and still looked down upon from real AOP programmers, so I am told. I think they call it "join points" not interception. Frankly if you prefer simple easy to follow code I don't see why you'd desire an AOP like approach. Almost by definition it means calling a method will have other behavior than the code you physically see on your screen. This is true of event & AOP systems, which is why decorator strategies & wrappers made it higher on my list. Nor is aspect oriented programming needed to organize your cross cutting concerns. For example on wikipedia the example shows they weave logging, database transactions & security checks all into that method. In my opinion that stuff should be in another layer, another class, another method. I am not very open minded to the weaving ;-)

Re: Event-driven programming

Posted: Sat Jan 08, 2011 2:54 am
by Benjamin
josh wrote:
Benjamin wrote:This may sound crazy but I never use debuggers. I look at the code and execute it in my head.
False. You read error messages & stack traces, which are generated by debugging tools ;-)
If I see error messages when running code, of course I fix them. Those are easy, considering it spits out the line number and all. What I'm referring to is identifying a bug, finding and reading the relevant code, understanding and comprehending exactly what it's doing and then identifying and solving the problem.
josh wrote:And you probably manually debug, as in print_r() here (at least occasionally).
Indeed, the following line of code is my friend:

Code: Select all

echo '<pre>' . print_r($foo, true) . '</pre>';
josh wrote:I do agree that over reliance on GUI debuggers leads to complex code, but the point is its available for those programmers who feel they need it (maybe your code wasn't as good as you thought, or maybe they aren't adept at "executing in their head")
On the contrary. I disagree because more than likely the code "feels" too complex to comprehend, so rather than try to understand what it's doing they fall back to something they feel will give them the answer to the problem. I'm sure college professors could go on and on about the psychology behind that.
josh wrote:Aspect Oriented Programming
I didn't realize this existed, but you have hit the nail on the head.
josh wrote:has been attempted in PHP and still looked down upon from real AOP programmers, so I am told.
No, that can't be right because AOP support does not exist in PHP and therefore cannot be attempted or implemented. This is for the most part exactly what I was trying to do.
josh wrote:I think they call it "join points" not interception.
Yeah, I read the wikipedia page. They may call it a "join point" but the fact of the matter is that they are intercepting code execution, and so I think that interception is the proper name.
josh wrote:Frankly if you prefer simple easy to follow code I don't see why you'd desire an AOP like approach.
Well, the description of the purpose of AOP attempts to solve exactly the problems I am trying to solve. Which are "cross-cutting" concerns I believe. Per the wikipedia page, these do not fit into the OO or MVC paradigms and therefore require their own class of methodology.
josh wrote:Almost by definition it means calling a method will have other behavior than the code you physically see on your screen.
Well I guess this would depend on how one implemented it. If you added a data filter to the arguments, yeah the arguments might be different than what was actually sent and you may have no clue why unless you are aware of the filter. In other cases, I feel like the functionality of the method should remain intact and that the before & after events should do their own, unrelated thing.
josh wrote:This is true of event & AOP systems, which is why decorator strategies & wrappers made it higher on my list.
But where does it stop? I mean you can't wrap every class with a wrapper and then wrap the wrappers with wrappers. Granted I'm not familiar with using them in the same way you do but I'm not sure they even apply here. I'll follow up with this at the end.
josh wrote:Nor is aspect oriented programming needed to organize your cross cutting concerns. For example on wikipedia the example shows they weave logging, database transactions & security checks all into that method. In my opinion that stuff should be in another layer, another class, another method. I am not very open minded to the weaving ;-)
The wikipedia page was describing how you would need to add all of that to each and every model in order to achieve the same functionality. It described being able to then modify, add, pull features without having to make a major overhaul of the entire codebase. This in essence is one of the issues I'm facing, but I also want to be able to do this based on other criteria as well.

Following up, I thought of something earlier today which may help clarify the problem. This is an extension of the whole "ordering a car" issue.
An order for a car is received by a system that receives thousands of orders per day. Every time an order is received the system must ensure that it is filled quickly and cost efficiently. So when the car order is received a component list is generated. The components may include such things as components, parts, or third party services such as OnStar and Satellite radio. The system is given the task to generate purchase orders routinely based on all orders for all vehicles received on a cycling basis, say every week. There can be multiple vendors for each part each charging different prices based upon the quantity ordered. Every order must be broken down into the components, every component must be broken down into individual parts, the system must determine who can supply the parts for the best price and deliver them on time. Once all of the parts for every vehicle is ordered they are stored and organized by the program. The program will determine the most efficient method to build all the cars, the order they should be built in etc.
The system I'm building isn't that complex, but it's certainly more complex than any web application I've ever built for anyone else.

I feel like the AOP methodology not only needs to be added to PHP, but PHP is fundamentally flawed by not having it. This may be a rare problem to run into, but it's almost a show stopper when you do.

In the Car examples described I've been trying to abstract the actual problem. It's not really a 100% fit with the actual problem but I think it's close enough.

I bet Vladsun could write a PHP module that accomplishes this.

Re: Event-driven programming

Posted: Sat Jan 08, 2011 2:31 pm
by josh
Benjamin wrote:
josh wrote:I do agree that over reliance on GUI debuggers leads to complex code, but the point is its available for those programmers who feel they need it (maybe your code wasn't as good as you thought, or maybe they aren't adept at "executing in their head")
On the contrary. I disagree because more than likely the code "feels" too complex to comprehend, so rather than try to understand what it's doing they fall back to something they feel will give them the answer to the problem. I'm sure college professors could go on and on about the psychology behind that.
Not sure if we both said the same thing? My argument was to use code code over configuration files, so people can fall back to debuggers. I get the sense you're agreeing with me (but get the sense that YOU think we disagree)
josh wrote:Aspect Oriented Programming has been attempted in PHP and still looked down upon from real AOP programmers, so I am told.
No, that can't be right because AOP support does not exist in PHP and therefore cannot be attempted or implemented. This is for the most part exactly what I was trying to do.
http://www.google.com/search?rlz=1C1CHO ... &q=php+aop
Several people are working on emulations. I for one don't see a difference between the real thing & emulating it. (maybe except for speed of execution)
josh wrote:I think they call it "join points" not interception.
Yeah, I read the wikipedia page. They may call it a "join point" but the fact of the matter is that they are intercepting code execution, and so I think that interception is the proper name.
josh wrote:Frankly if you prefer simple easy to follow code I don't see why you'd desire an AOP like approach.
Well, the description of the purpose of AOP attempts to solve exactly the problems I am trying to solve. Which are "cross-cutting" concerns I believe. Per the wikipedia page, these do not fit into the OO or MVC paradigms and therefore require their own class of methodology.
Right, per the wiki page. I would disagree though. Many design patterns & OOP techniques address cross cutting concerns. The biggest I would say is component based re-use instead of inheritance based re-use.
josh wrote:This is true of event & AOP systems, which is why decorator strategies & wrappers made it higher on my list.
But where does it stop? I mean you can't wrap every class with a wrapper and then wrap the wrappers with wrappers. Granted I'm not familiar with using them in the same way you do but I'm not sure they even apply here. I'll follow up with this at the end.
Sure you can, to quote wikipedia again
The decorator pattern can be used to make it possible to extend (decorate) the functionality of a certain object at runtime, independently of other instances of the same class, provided some groundwork is done at design time. This is achieved by designing a new decorator class that wraps the original class. This pattern is designed so that multiple decorators can be stacked on top of each other, each time adding a new functionality to the overridden method(s).
The decorator pattern is an alternative to subclassing. Subclassing adds behavior at compile time, and the change affects all instances of the original class; decorating can provide new behavior at runtime for individual objects.
josh wrote:Nor is aspect oriented programming needed to organize your cross cutting concerns. For example on wikipedia the example shows they weave logging, database transactions & security checks all into that method. In my opinion that stuff should be in another layer, another class, another method. I am not very open minded to the weaving ;-)
The wikipedia page was describing how you would need to add all of that to each and every model in order to achieve the same functionality. It described being able to then modify, add, pull features without having to make a major overhaul of the entire codebase. This in essence is one of the issues I'm facing, but I also want to be able to do this based on other criteria as well.
I disagree that simply using AOP prevents overhauls. Its a tool but it doesn't write software for you. The other day I explained to a client that MVC doesn't "force" you to write good code (which his other programmers told him was true)
I feel like the AOP methodology not only needs to be added to PHP, but PHP is fundamentally flawed by not having it.
One thing you may want to look into is "prototyping". Create your cross cutting concerns as lambda objects. Other objects can have these lambdas added on by making use of __call() In javascript which has this you can basically say "copy the function from class A to object B". AOP extends that by letting you modify existing methods. I'd rather have the physical template method

Aspect Oriented Programming
[syntax]
function foo() {
// stuff could be happening here in AOP for all we know.
echo 'test';
}
[/syntax]

Template Methods
[syntax]
function bar() {
// if stuff is happening here we are 100% sure of it.
$this->stuffIsHappening();
echo 'test';
}
[/syntax]

Re: Event-driven programming

Posted: Sat Jan 08, 2011 4:05 pm
by Christopher
I have not really heard anything here other than this is a difficult implementation because of the complexity of the Domain. I don't really think there is a magic bullet for complex Domains. Certain AOP has not proven to be a solution that has stood the test of time. It hangs around but ultimately it solves problems with as much code and maintenance -- just in a different way. That not an improvement ... its a style, and a non-standard one.

Ultimately I think you need to knuckle down and do the hard work of conquering your complex Domain Model. There's no way around that. And it does sound like you are going to need to step up your debugging techniques to deal with this. This seems like a project that need tooling, code generation, etc.

Re: Event-driven programming

Posted: Sun Jan 09, 2011 2:01 am
by josh
Christopher wrote:it solves problems with as much code and maintenance -- just in a different way.
For the most part I'd agree, regular design patterns can be used to break or reverse dependencies, and move cross cutting concerns into their own module. The only thing AOP is really doing for you is eliminating the call to a template method, like in my example above. I prefer to have a template method that I can override, as opposed to having to debug to see what code is running at that point cut (I've never used AOP first hand). I do agree there would probably be a little bit less duplication with AOP... For example I'll duplicate a function to get the database adapter, because I can't use a base class, because I have to extend other people's code. I could address it with design patterns and remove some of that duplicated code, and that might be easier with an AOP language, but other than that I don't really find the need to duplicate anything.

The proverbial "logging" example with aspect oriented programming is a bit contrived too. I can't say I've ever had the desire to start logging every thing in my system. If there's going to be so much logging code that it could tangle with business logic, I'd just use a wrapper.. I'd have Model and Logging_Model, and maybe Security_Model. They would be in a decorator chain, so in Model each method is implemented with business logic. In Logging_Model, each method is re-implemented but first call's the super class method, etc.

I should say, even the decorator chain is very heavy handed use of design patterns & indirection. I would prefer to separate it at the sub-method level. If following the single responsibility principle, you shouldn't even really need to use that many wrappers/decorators.

Code: Select all

class Model
{
 function businessLogic()
 {
   if( $this->securityCheck() )
   {
     return false;
   }
   $this->log();
   $this->doBusinessStuff();
 }
}
I'd prefer something like this, simple template methods, easy to follow. Only when that really starts breaking down are more advanced design patterns called for in my opinion. Basically first I'd have a method in a class call other methods in that same class as template methods. When I out-scale that I'd move each template method to it's own class (sprout class), the old class would be just a facade or would be deleted depending on my needs. If you outgrow the couple of classes, each class can become many classes (each class becomes it's own module). You could have your cross cutting concerns each in their own module, with another module acting as glue.