Page 2 of 3

Re: Event-driven programming

Posted: Sun Jan 09, 2011 5:04 pm
by Benjamin
josh wrote: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]
I know what you are saying. But with AOP the events don't get triggered when the method is called, they are triggered before or after the method is called. The method should only be concerned about what it is supposed to do and not be concerned with what other events may be "attached" or "triggered" by it. When you look at a method you have no clue what may be called before or after it anyway.

The problem is more complex than wrappers because it can go many layers deep.

If one was adding a car the system may need to add various components of the car including the engine, transmission, drivetrain. So it would make sense to create a wrapper that accomplishes this. But now when the engine is added, the system should get a parts list for the engine and add each part. When each part is added maybe the system will get a list of vendors for each part and do some other stuff. So there ends up being a large chain of events that occur that are triggered by a single main event.

I don't see how this could be accomplished with wrappers because the functionality also needs to be in place to just add a part, just add a vendor, just add an engine maybe, without triggering other stuff.

I think AOP is something that certainly needs to be added to PHP because this can really help organize code and it's very powerful. Of course I'm sure there will be a tab vs spaces debate regarding this but at the end of the day it's a hell of a lot better than the goto statement that was recently added. If crap like that makes it in, AOP should already be in it.

Re: Event-driven programming

Posted: Sun Jan 09, 2011 7:07 pm
by josh
Benjamin wrote: with AOP the events don't get triggered when the method is called, they are triggered before or after the method is called.
Incorrect technically. In AOP the compiler takes the code from the aspects, and copy pastes it *inside* the methods, before, after, or in the middle of your code. AOP technically speaking creates duplicate, spaghetti code at run/compile time. To quote a blog:
Aspect Oriented Programming is, in its basest form, adding extra behaviour to a unit of abstracted code (a function, a method, what-have-you) seperate from the definition of that behaviour.
The method should only be concerned about what it is supposed to do and not be concerned with what other events may be "attached" or "triggered" by it. When you look at a method you have no clue what may be called before or after it anyway.
Right, but in my opinion you're arguing against AOP. In AOP you have to be concerned about side effects that attach themselves to that method from other modules. In regular OOP any side effects are explicitly written there.

If one was adding a car the system may need to add various components of the car including the engine, transmission, drivetrain. So it would make sense to create a wrapper that accomplishes this. But now when the engine is added, the system should get a parts list for the engine and add each part. When each part is added maybe the system will get a list of vendors for each part and do some other stuff. So there ends up being a large chain of events that occur that are triggered by a single main event.
That's just regular objects, I don't see a need for wrappers. First of all you need to hit the whiteboard. Engine & transmission are components of the drivetrain. A car has a drivetrain object, and a chassis object, for example. A car need not hold a reference to each individual "thing", break it into a hierarchy of objects. The gears are a part of the transmission, so the car will hold a reference to a drivetrain object, the drivetrain object will hold a reference to a transmission object, a transmission object will hold references to gear objects.

The way I was suggesting to use wrappers was for different contexts. For example in one context you may have a car object that is designed to be used during order fulfillment. When the customer is shopping on the front-end, perhaps another wrapper is used that provides different types of methods than the ones needed to fulfill orders. These are contexts as per what I was thinking of.

I think AOP is something that certainly needs to be added to PHP because this can really help organize code and it's very powerful. Of course I'm sure there will be a tab vs spaces debate regarding this but at the end of the day it's a hell of a lot better than the goto statement that was recently added. If crap like that makes it in, AOP should already be in it.
You can technically do everything you can in AOP, without AOP: http://flow3.typo3.org/documentation/ma ... framework/
If PHP had "magic factories" more people would consider it maybe.

I don't think you've mentioned anything that really needs the use of AOP. To me the use of AOP comes down to this. I have a class "class A". An extension developer makes a class "class B extends class A". Another extension developer wants to make a class "class C extends class A".. uh oh, only one of the extensions will ultimately take affect. In Magento extensions work way, extensions work thru inheritance. In a prototypical/aspect oriented environment working in legacy systems would be easier. For greenfield development I don't really see any need for these features.

Also see this link http://blog.jonnay.net/archives/637-Asp ... ages..html

I guess the reason I'd have to ask myself before applying AOP to a project is "why can I not just insert a template method here myself, why is it I need PHP to insert code dynamically at run time"? About the only legit answer I can think of is that other developers can better write extensions on non extensible code.

Re: Event-driven programming

Posted: Sun Jan 09, 2011 8:40 pm
by Benjamin
josh wrote:
Benjamin wrote: with AOP the events don't get triggered when the method is called, they are triggered before or after the method is called.
Incorrect technically. In AOP the compiler takes the code from the aspects, and copy pastes it *inside* the methods, before, after, or in the middle of your code. AOP technically speaking creates duplicate, spaghetti code at run/compile time. To quote a blog:
Aspect Oriented Programming is, in its basest form, adding extra behaviour to a unit of abstracted code (a function, a method, what-have-you) seperate from the definition of that behaviour.
I doubt that's correct, and if it is, whoever created it that way implemented it wrong. The triggered methods should not be copied into other methods. Instead the called method should be intercepted and the triggered methods should be executed via pointers or however a normal method is called. I know there is an AOP implementation for PHP, and it's behaviour is as you describe. I consider that to be nothing more than AOP emulation.

josh wrote:That's just regular objects, I don't see a need for wrappers. First of all you need to hit the whiteboard. Engine & transmission are components of the drivetrain. A car has a drivetrain object, and a chassis object, for example. A car need not hold a reference to each individual "thing", break it into a hierarchy of objects.

I don't think you've mentioned anything that really needs the use of AOP. To me the use of AOP comes down to this. I have a class "class A". An extension developer makes a class "class B extends class A". Another extension developer wants to make a class "class C extends class A".. uh oh, only one of the extensions will ultimately take affect. In Magento extensions work way, extensions work thru inheritance. In a prototypical/aspect oriented environment working in legacy systems would be easier. For greenfield development I don't really see any need for these features.
I don't understand. I don't see how wrappers can be created for every combination of possible events, nor should they be when the logic flow can be controlled with a series of one liners, perhaps making up an "aspect" in the sense that each file loading the intercept points can be the configuration needed to create a specific type of vehicle or whatever object is being built.
josh wrote:I guess the reason I'd have to ask myself before applying AOP to a project is "why can I not just insert a template method here myself, why is it I need PHP to insert code dynamically at run time"? About the only legit answer I can think of is that other developers can better write extensions on non extensible code.
That's just it. You can, but why hardcode everything when you can simply create a one liner to add/remove/modify behaviour anywhere you choose? Complex code can be organized very easily and ALL existing code becomes easily extensible. I would think that's more than reason enough. I'll look into the FLOW3 framework more. It seems interesting.

Re: Event-driven programming

Posted: Sun Jan 09, 2011 11:04 pm
by josh
Benjamin wrote:
josh wrote:
Benjamin wrote: with AOP the events don't get triggered when the method is called, they are triggered before or after the method is called.
Incorrect technically. In AOP the compiler takes the code from the aspects, and copy pastes it *inside* the methods, before, after, or in the middle of your code. AOP technically speaking creates duplicate, spaghetti code at run/compile time. To quote a blog:
Aspect Oriented Programming is, in its basest form, adding extra behaviour to a unit of abstracted code (a function, a method, what-have-you) seperate from the definition of that behaviour.
I doubt that's correct, and if it is, whoever created it that way implemented it wrong. The triggered methods should not be copied into other methods. Instead the called method should be intercepted and the triggered methods should be executed via pointers or however a normal method is called. I know there is an AOP implementation for PHP, and it's behaviour is as you describe. I consider that to be nothing more than AOP emulation.
AspjectJ's documentation states that it intercepts in the middle of calling code (It states "picks out each join point that is a call to a method" - emphasis on "call to a method"). It doesn't intercept a method being called as much as it intercepts in the middle of the calling code. That is, you're setting point cuts in the middle of some procedural method, not "before"/"after" the method being called. That is from my understanding of their documentation: http://www.eclipse.org/aspectj/doc/rele ... #pointcuts

Either way, my point was that is basically the design pattern "template method" except we have added extra indirection. It has it's uses, but as a primary way of organizing your core business logic would not be one of those uses in my opinion. You can no longer determine a method's side effects from looking at that method, you must run a text-search to see if there's point cuts for that method's signature, for every method. Unless you ran the text search every time you read a method, you'd never know for sure in an AOP system. In a regular OOP system, if I have a method, the only code that is running is the code within the method, every time.

I don't see how wrappers can be created for every combination of possible events
I wasn't recommending wrappers or events. I was recommending component based re-use. I recommended wrappers for cross cutting concerns. The fact a car has an engine doesn't constitute a cross cutting concern. A cross cutting concern would be like if every component in the system had an engine, wrappers/decorators would save you from placing setEngine() getEngine() on each of those classes. Instead, in the module that deal's with engines it'd wrap other objects.

However if only one object (the car) has this engine, its far from a cross cutting concern. In that case, even wrappers are overkill, AOP/events are even more overkill.
That's just it. You can, but why hardcode everything when you can simply create a one liner to add/remove/modify behaviour anywhere you choose? Complex code can be organized very easily and ALL existing code becomes easily extensible. I would think that's more than reason enough. I'll look into the FLOW3 framework more. It seems interesting.
By hard code do you mean insert a template method instead of a point cut? If that's what you mean I think you'd find aspects would use more physical code/typing.

Why don't you give us an example in code or psuedo code so we can see what you mean? Can you write some regular code without events, and show me how its messy? I bet I could organize any code you post so that you find it readable, without using events (or very sparing use of them).... lets see it shall we?

Re: Event-driven programming

Posted: Sun Jan 09, 2011 11:52 pm
by Benjamin
Ok. I don't want to give away what I'm building so I've gone through one of the blocks of code and renamed everything. Originally, I started by writing wrappers to accomplish everything I need. This code is a part of the system that gathers data and populates database tables. A front end will also need to be built when which allows users to add/modify/remove different things which may require completely different behaviour. The behaviour will vary depending on what is being done, what vendors they select, what options are turned on/off etc.

So as I said here's the code from one of the wrappers. This is rather light because there are about 10 other data sources doing various things that haven't been added yet.

Code: Select all

<?php
class engine_parts_data_collector extends engine_parts_api {
    public function getEnginePartsInfo() {
        $this->engine_parts      = new engine_parts_model();
        $this->image_retrieval   = new image_retrieval_model();
        $this->vendor_part       = new engine_parts_vendors_model();
        $this->identical_parts   = new engine_parts_identical_parts_model();

        /**
         * Override standard image retrieval behavior
         */
        $this->image_retrieval->setOption('discard_gif_images', false);
        $this->image_retrieval->setOption('min_image_size', 0);
        $this->image_retrieval->setOption('min_image_proportion_ratio', 0);

        try {
            if (false == $partsList = $this->engine_parts->getAll()) {
                throw new Exception("No parts found.");
            }

            foreach ($partsList as $part) {
                /*
                 * Set part number
                 */
                $this->setPartNumber($part['number']);

                /*
                 * Pull extra information about this part from another source
                 */
                if (false != $partInfo = $this->partGetInfo($this->_options['allow_non_oem'])) {
                    /*
                     * Add new information to existing record, because this data is not available in the original parts list
                     */
                    $query = array(
                        'weight'    => $partInfo['weight'],
                        'lifespan'  => $partInfo['lifespan'],
                        'available' => date("Y-m-d H:i:s", strtotime(trim($partInfo['available']))),
                        'warnings'  => strip_tags($partInfo['warnings']),
                        'warantee'  => strip_tags($partInfo['warantee']),
                    );

                    $this->engine_parts->update($part['number'], $query);
                }

                unset($partInfo);

                /*
                 * Pull a list of vendors for this part
                 */
                if (false != $partVendors = $this->partGetVendors($this->_options['allow_non_oem'])) {
                    if (!isset($partVendors['vendor_list']) || !is_array($partVendors['vendor_list'])) {
                        continue;
                    }

                    foreach ($partVendors['vendor_list'] as $vendor) {
                        if (is_array($vendor['image'])) {
                            $image = array_pop($vendor['image']);

                            if (!empty($image['#path'])) {
                                $image = $this->image_retrieval->capture($image['#path']);
                            } else {
                                $image = null;
                            }
                        } else {
                            $image = null;
                        }

                        $ins = array(
                            'part_num'      => $part['number'],
                            'name'          => $vendor['name'],
                            'price'         => $vendor['price'],
                            'shipping'      => $vendor['shipping'],
                            'url'           => $vendor['url'],
                            'image'         => $image,
                        );

                        if (false != $vendor_id = $this->vendor_part->add($ins)) {
                            $data = $this->vendorGetInfo($part['number']);

                            if ($data == false || !is_array($data['info'])) {
                                continue;
                            }

                            $n = array(
                                'is_approved'   => $data['info']['is_approved'],
                                'approved_on'   => date("Y-m-d H:i:s", strtotime(trim($data['info']['approved_on']))),
                            );

                            $this->vendor_part->update($vendor_id, $n);

                            if (!is_array($data['info']['indentical_parts']) || count($data['info']['indentical_parts']) < 1) {
                                continue;
                            }

                            foreach ($data['info']['indentical_parts'] as $key => $identical) {
                                $n = array(
                                    'part_id'  => $identical['id'],
                                    'replaces' => $part['number'],
                                    'name'     => $identical['name'],
                                );

                                $this->identical_parts->add($n);
                            }
                        }
                    }
                }

                unset($partVendors);


            }
            return true;
        } catch (Exception $e) {
            logging::addEvent(__FILE__, __LINE__, __CLASS__ . '->' . __FUNCTION__ . '() - ' . $e->getMessage(), $data);
            return false;
        }
    }
}

Re: Event-driven programming

Posted: Mon Jan 10, 2011 12:06 am
by josh
I would say you just need to work on OOP more, and that aspects would not necessarily help you. The code within the "parts" loop can be extracted, so the new method process one part at a time. And then there are 3 distinct kludges of code within that kludge. ("setting the part #", "pulling additional info from 'another source'", "populating list of vendors"). The latter, populating the list of vendors could be a whole class of it's own. I left that as an exercise for you.

Code: Select all

<?php
class engine_parts_data_collector extends engine_parts_api {
	function __construct() {
		$this->engine_parts = new engine_parts_model ();
		$this->image_retrieval = new image_retrieval_model ();
		$this->vendor_part = new engine_parts_vendors_model ();
		$this->identical_parts = new engine_parts_identical_parts_model ();
		$this->overrideImageBehavior ();
	}
	
	function overrideImageBehavior() {
		$this->image_retrieval->setOption ( 'discard_gif_images', false );
		$this->image_retrieval->setOption ( 'min_image_size', 0 );
		$this->image_retrieval->setOption ( 'min_image_proportion_ratio', 0 );
	}
	
	function getEnginePartsInfo() {
		
		try {
			if (false == $partsList = $this->engine_parts->getAll ()) {
				throw new Exception ( "No parts found." );
			}
			
			foreach ( $partsList as $part ) {
			   $this->doPart($part);
			}
			return true;
		} catch ( Exception $e ) {
			logging::addEvent ( __FILE__, __LINE__, __CLASS__ . '->' . __FUNCTION__ . '() - ' . $e->getMessage (), $data );
			return false;
		}
	}
	
	function doPart($part) {
		$this->setPartNumber( $part['number'] );
		$this->pullInfo($part);
		$this->pullPartVendors();
	}
	
	function pullInfo($part)
	{
		if (false != $partInfo = $this->partGetInfo ( $this->_options ['allow_non_oem'] )) {
			$query = array ('weight' => $partInfo ['weight'], 'lifespan' => $partInfo ['lifespan'], 'available' => date ( "Y-m-d H:i:s", strtotime ( trim ( $partInfo ['available'] ) ) ), 'warnings' => strip_tags ( $partInfo ['warnings'] ), 'warantee' => strip_tags ( $partInfo ['warantee'] ) );
			$this->engine_parts->update ( $part ['number'], $query );
		}
	}
	
	function pullPartVendors()
	{
		// left this one up to your imagination
		throw Exception('i didn't move this one last method, because you get the idea.);
	}
}
Practice extract method & sprout class refactorings would be my prescription to your problem.

Re: Event-driven programming

Posted: Mon Jan 10, 2011 12:21 am
by Benjamin
I know what decomposition is, however that method is one of many in this particular class. Could I clean it up? Sure. But I also need to add many more data sources. Everything added, from top-level stuff such as a car to bottom-level stuff such as a part vendor needs to also trigger about 4 or 5 events. Here's an example of the methods that already exist in this class.

Code: Select all

    public function populateAll() {
        $this->processVehicleGetDealers();
        $this->processVehicleGetImages();
        $this->processVehicleGetInfo();
        $this->processVehicleGetCommonProblems();
        $this->processVehicleGetSimilar();
        $this->processVehicleGetSales();
        $this->processVehicleGetTopSalesStates();
        $this->processVehicleGetBuyerDemographics();
        $this->processBuyerDemographicsBreakdownByState();
        $this->processVehicleGetComponentList();
        $this->getVehicalPartsInfo();
        $this->getVehiclePartsInfoVendors();
        $this->getVehiclePartsInfoVendorsInfo();
    }
Edit: Here's a good Q/A About it: http://stackoverflow.com/questions/2328 ... rogramming
http://stackoverflow.com/questions/2066 ... n-software

Re: Event-driven programming

Posted: Mon Jan 10, 2011 1:14 am
by Benjamin
Well Rasmus said this isn't a realistic feature request because there is too much overhead involved in checking for triggers before, filter and after every method call. I must digress though, the overhead involved in emulating this behaviour in the PHP code itself is likely much greater. My argument is that:
I believe the overhead would be negligible or non-existent because:

1. The feature could be implemented in a way where it has to be explicitly enabled.
2. The lookups could use array keys which would be very fast and only add a few CPU cyles to each method call.
So for now, I'll go with a glorified plugin system for implementing this behaviour.

Re: Event-driven programming

Posted: Mon Jan 10, 2011 1:57 am
by josh
Benjamin wrote:Could I clean it up? Sure. But I also need to add many more data sources.
Hmm so why not clean it up? Because you need to add more? Seems like circular logic, that is all the more reason to clean it up. If you're adding more code, it makes sense to add new methods & classes, rather than to place the code into existing methods.

Right off the bat in that method list I see things that should be their own class, once you have 2-3 methods with "demographics" in their name, you should probably move them to an object called "demographics". If you already have that class, the methods should be moved there. As far as a car is concerned all it should need a method ->demographics() that returns an instance of the "demographics" class. The demographics class would hold the methods with "demographics" in their name, better yet each method name no longer needs to state "demographics" in it's title, as that will be the name of the class they live on, so it will be self evident that they are methods related to demographics.

I'm interested in how you think AOP would clean up your example code better than just grouping into methods & classes? What in your example code do you think AOP weaving would help clean up better than simply grouping code kludges into actual methods? As my rule of thumb if code is separated by whitespace or comments, its a kludge. Your class definitely violates the single responsibility principle. I write this not to bash your code of course but as constructive criticism. I think the only way AOP could help is maybe it would encourage you to break up your methods more, but that's more psychologically related than programming related. You can simply decide to organize your code better and get just as good results, I think.

As for AOP being in the PHP core, it didn't make it to the Java core, and AspectJ has existed for a long time. AspectJ itself is an extension to Java, just like some of the PHP extensions that are being made, granted AspectJ is more robust than the existing PHP extensions as of today.

I read the most highly rated answer here. http://stackoverflow.com/questions/2328 ... rogramming his example is contrived. In PHP you have __get & __set, so his example depends on not having access to overloading, and furthermore the programmer must have created 200 methods and duplicated code in the first place (in order for his example of having to modify 200 methods to make sense). Either way, if each setter delegated to a single function where all setting happens, the example would be moot. In PHP it's even more moot. It's a good way of illustrating the benefits, but even the guy who wrote that answer points out the huge downside... Really the only 2 valid examples I've heard so far is logging, and creating a more extensible program. Even the logging is arguably better addressed with a wrapper.

Anyways I'm curious as to specifically how you think AOP would help you better organize your code, as opposed to doing it in regular OOP. I definitely understand and appreciate the benefit of AOP, but I fail to see any cross cutting concerns in your example code(s).

Re: Event-driven programming

Posted: Mon Jan 10, 2011 2:14 am
by Benjamin
josh wrote:Right off the bat in that method list I see things that should be their own class, once you have 2-3 methods with "demographics" in their name, you should probably move them to an object called "demographics". If you already have that class, the methods should be moved there. As far as a car is concerned all it should need a method ->demographics() that returns an instance of the "demographics" class. The demographics class would hold the methods with "demographics" in their name, better yet each method name no longer needs to state "demographics" in it's title, as that will be the name of the class they live on, so it will be self evident that they are methods related to demographics.
There are indeed models for everything. You are looking at the wrapper. Could I organize it more, sure. But there's still going to be duplicate code in many places because there will need to be lots of wrappers.
josh wrote:I'm interested in how you think AOP would clean up your example code better than just grouping into methods & classes? What in your example code do you think AOP weaving would help clean up better than simply grouping code kludges into actual methods?
AOP would eliminate the wrappers all together. Instead I will utilize decomposition and move all of the pieces into modules. I will create driver files which are a collection of interception commands in order to configure the system at runtime to perform specific tasks. Using this methodology, there would be only one simple file for each unique task, rather than a series or collection of wrappers. This will organize the system much better and significantly improve development time, allow me to add/remove features easily and possibly improve performance.

Code: Select all

$car = new car();
$car->using_driver('default')->add('GCM Sierra', '2002');
josh wrote:I read the most highly rated answer here. http://stackoverflow.com/questions/2328 ... rogramming his example is contrived. In PHP you have __get & __set, so his example depends on not having access to overloading, and furthermore the programmer must have created 200 methods and duplicated code in the first place (in order for his example of having to modify 200 methods to make sense). Either way, if each setter delegated to a single function where all setting happens, the example would be moot. In PHP it's even more moot.
I like to automate as much as practically possible because it saves time and code. Less code = less bugs. Hell my models are getting to the point where they are *almost* 100% crud ready out of the box without having to run a script on a yml file.

Re: Event-driven programming

Posted: Mon Jan 10, 2011 2:24 am
by josh
Hmm well I would say you haven't shown any wrappers, you've actually shown a facade. A class that is used to interact with a # of sub-systems (other sub-classes if you will). In my opinion you have leaked a lot of business logic out of your models and into the facade. Which line(s) of code are you finding you need to duplicate? I didn't notice any duplicate code, as much as I noticed lots of code at the wrong level of abstraction, and a bad "responsibility count to method count ratio". Pulling a list of vendors is 50 lines of code*. That should be in a method, on the "part" class, possible called getVendorList(). It should only occupy 1 line in the facade class. If not the "part" class or the "getVendorList" method, maybe a method called getVendorListWithImages. Or better yet, getVendorList(), and then pass the array to the "image" object $image_retreival->retrieveFor($vendorsList) (2 lines of code max)

* well you're doing something after you get the vendor's list that takes 50 lines of code, its very verbose, and at the wrong level of abstraction in my opinion. Can you describe in 2-3 words what this big kludge of code does? For example you pull the array, and loop over it processing it, modifying the data (setting the image filename for example). Could that getVendorsList() just return the array with the images already set up? Then it would be at the right level of abstraction.

See 'feature envy': http://sourcemaking.com/refactoring/feature-envy

Re: Event-driven programming

Posted: Mon Jan 10, 2011 2:34 am
by Benjamin
josh wrote:Hmm well I would say you haven't shown any wrappers, you've actually shown a facade. A class that is used to interact with a # of sub-systems (other sub-classes if you will). In my opinion you have leaked a lot of business logic out of your models and into the facade. Which line(s) of code are you finding you need to duplicate? I didn't notice any duplicate code, as much as I noticed lots of code at the wrong level of abstraction, and a bad "responsibility count to method count ratio". Pulling a list of vendors is 50 lines of code. That should be in a method, on the "part" class, possible called getVendorList(). It should only occupy 1 line in the facade class
Yes, some of the data coming from other classes should have been preprocessed there. So that I agree with. What lines do I need to duplicate? I'll try to create an example, but I think I stopped production early enough that it hasn't really happened yet.

Code: Select all

                /*
                 * Pull extra information about this part from another source
                 */
                if (false != $partInfo = $this->partGetInfo($this->_options['allow_non_oem'])) {
                    /*
                     * Add new information to existing record, because this data is not available in the original parts list
                     */
                    $query = array(
                        'weight'    => $partInfo['weight'],
                        'lifespan'  => $partInfo['lifespan'],
                        'available' => date("Y-m-d H:i:s", strtotime(trim($partInfo['available']))),
                        'warnings'  => strip_tags($partInfo['warnings']),
                        'warantee'  => strip_tags($partInfo['warantee']),
                    );

                    $this->engine_parts->update($part['number'], $query);
                }
When adding a car, we *might* create a component list. One of the components could be an engine. When we add the engine, we *may* create a parts list. When we iterate through the parts list, we *may* pull additional information about the part from another source and update the existing record. This is 4 levels deep right here. If there are 5 events for each level that may or may not need to occur the complexity grows exponentially.

I am having a hard time understanding a better way to solve this problem then by using an AOP approach.

Anyway, with the code above, that could be abstracted out into another method call of some sort. The point is that a line of code would still need to be there, in every wrapper. If I need to add the functionality to enable/disable this feature, then that adds at least another 2 lines every place this needs to happen. If I want to add a feature, I need to add lines to every model that requires the feature. 3 lines * 6 features is 18 lines without spaces in *every* wrapper. So I guess that is what I foresee happening and am trying to avoid.

Re: Event-driven programming

Posted: Mon Jan 10, 2011 3:10 am
by josh
Benjamin wrote:When adding a car, we *might* create a component list.
Its not specified what you add a car to, so I'll assume an Order of some sort
One of the components could be an engine. When we add the engine, we *may* create a parts list. When we iterate through the parts list, we *may* pull additional information about the part from another source and update the existing record. This is 4 levels deep right here. If there are 5 events for each level that may or may not need to occur the complexity grows exponentially.
Amount or complexity of code itself doesn't grow, only the # of possible ways to "wire up" the object graphs in memory should really grow.
I am having a hard time understanding a better way to solve this problem then by using an AOP approach.
Well your main complaint was the "if" check to see if we should add the parts or not. With aspects, you'd still need to run a check inside each aspect.

Right now, your requirement is if we create an order, we add a car. Sometimes, the car will need components. Sometimes, a component will have a parts list, and so on....

Right now the way you're solving this is an if statement at each "level" of decision, and nesting the if statements. Now let's say you need a new feature like "component" but called a "widget", and like components, widgets sometimes have a parts list. With your style the code to get the parts list for components would be duplicated for widgets.

If however the code that generated parts list was in it's own class, you'd only be duplicating about 1 line of code (the code to instantiate the class, and pass execution off to it). Once execution is passed off to the "sub-module" it does some things and than may/may not pass execution to its "sub-sub-module". Its really hard to talk about this or write example code because very little information is given about your problem domain so I don't know if my answer is tailored to your individual problem. But basically each module should only need 1 check, for the level immediately below it, after all if it returns false, you're done checking.

This is like this refactoring: http://www.refactoring.com/catalog/repl ... auses.html
but in his example imagine each 1 line is a whole complex module. Instead of having kludges nested, you have each kludge in it's own class, and you simply end execution whenever necessary be not passing execution off to further modules.
Anyway, with the code above, that could be abstracted out into another method call of some sort. The point is that a line of code would still need to be there, in every wrapper. If I need to add the functionality to enable/disable this feature, then that adds at least another 2 lines every place this needs to happen. If I want to add a feature, I need to add lines to every model that requires the feature. 3 lines * 6 features is 18 lines without spaces in *every* wrapper. So I guess that is what I foresee happening and am trying to avoid.
Ok well this is a different problem. Martin Fowler once wrote there's no wrong or right object model, only object models that are more adept to certain kinds of changes, and less adept to other kinds of change. For example right now we have an "order" that may or may not have multiple "components". What are you adding more of, more things like components? (Eg. widgets) that an order can "have"?

A) So an order can have 0 - N components, and an order may also have 0 - N widgets (aka you're adding more "things" that an order "has"?)
or
B) An order can have 0-N components, a car can have 0-N components, a user can have 0-N components (aka you're adding more things that "have" components)

With example A I would write my facade like this:

Code: Select all

class OrderFacade
{
	function assembleOrder($order, $car, $options)
	{
		if($options['createComponentList'])
		{
			$componentsFacade = new ComponentsListFacade;
			$componentsFacade->assembleOrder($order, $car, $options);
		}
	}
}
In example A I do write an if statement, when I add widgets later, all my if statements will be in the same method, best suited for example A, not well suited for example B (with example A you have an extra 2 lines of code to feel guilty about duplicating)

With example B I would write my "order assembly" facade like this:

Code: Select all

class OrderFacade
{
	function assembleOrder($order, $car, $options)
	{
		$componentsFacade = new ComponentsListFacade;
		$componentsFacade->assembleOrder($order, $car, $options);
	}
}
It doesn't do any checks, because these particular 2 lines will need to exist in more than one class. The check is instead done inside ComponentsListFacade itself, simply returning false if the $options array didn't contain a flag signifying that we wanted components. Best suite for example B, not well suited for example A (with example A the checks would seem "scattered around" different classes)

Re: Event-driven programming

Posted: Mon Jan 10, 2011 4:49 pm
by Weirdan
Btw, there's a fresh article from Matthew O'Phinney about different implementations of signals/events: http://weierophinney.net/matthew/archives/251-.html - Benjamin, you may find it useful.

Re: Event-driven programming

Posted: Mon Jan 10, 2011 5:00 pm
by Benjamin
Thanks guys. I'll see if I'm able to do what I want with the wrappers or facads and post back next time I work on this. I'd would like to try an AOP approach also in order to see how it works out.

Edit: Here's a tech talk video on it: http://vigilance.co.in/aspect-oriented- ... alk-video/