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)