System Structure

lemon-tree

x10 Minion
Community Support
Messages
1,420
Reaction score
46
Points
48
As some of you may be aware, I already have a functioning site (see signature) and I am currently looking to rebuild the backend and move to a better structure that is both faster and easier to extend. At the moment it is a mash-up of all sorts of unused code and poor standards. However, I'm looking for some quick input on how best to structure the backend, with a few specific issues.

I'll start by outlining a few of the base classes as I see they may be necessary:

PHP:
class FPD // This is the main class that contains shared functions 
class Account // Manages account specific methods and data
class Plan // The unit object that is created on a load from database, contains the info for one single plan

// There may also be a Database class, for instantiating connections and handling requests.
However, once I have these classes set out, there are a number of ways in which the methods and the objects could be structured:


Method 1:

In this method, the Account instance is within the FPD instance. The plan object also has methods within it that can be called:
PHP:
$fpd = new FPD;
$fpd->account = new Account;
//load a plan object
$plan = $fpd->load(index); // Returns an instance of Plan class
//Get the length of the plan
$distance = $plan->getDistance(); // This may make more sense to be a static method though
The fpd and account instances have access to one another, although that may not be important.
However, this method means I may end up with calls such as $fpd->account->username; whilst this is no problem in practice, it might just be a little convoluted.


Method 2:

In this method, the Account instance is separate from the FPD instance, but would then have to be brought in somehow. The plan object also no methods within it and all functions for them are held within the FPD parent:
PHP:
$fpd = new FPD;
$account = new Account;
//load a plan object
$plan = $fpd->load(index); // Returns an instance of Plan class
//Get the length of the plan
$distance = $fpd->getPlanDistance($plan);


Method n:

Neither of the above structures, I've got it all wrong and should start again.


Queries:

So essentially I have two main questions:

1. How should the Account and FPD instances be nested; should account be held within the FPD instance or should it be external? Or is it just a matter of preference?

2. Should the Plan class have it's own methods to call, or should the methods be within the FPD class and have the Plan instance as an argument.


Once this is sorted it'll be on to the frontend, which is in even more dire need to some work; it's the consequence of nearly two years of poor structure.
 

misson

Community Paragon
Community Support
Messages
2,572
Reaction score
72
Points
48
The purpose of the three classes isn't quite clear. Is FPD a utility class? Could the methods be made static (as is common for utility classes), so that you don't need to construct an FPD? Does Account manage a user account, does it represent one, or something else? What's a plan? By "unit object", do you mean something like "singleton" or "multiton"? Does "unit" refer to "unit testing"?

Since globals are evil, $fpd, $account and $plan should be done away with, if global. If an object needs to refer to another outside the method where the second is introduced, the first needs to keep a reference to it.

An object should only have access to other objects it creates or is introduced to (or if the objects and connection between them exist before the system starts, though that doesn't apply here); this principle comes from capabilities. In pure OO languages, the former used to be the way of things: objects creating the objects they use, giving them a "has-a" relationship (aka "composition"). This made unit testing difficult, as you had to alter a class to unit test it. Composition also implies the creating object owns the created, and when the owning object is destroyed, so is the owned object. This doesn't work so well when you sometimes want instances of one class to outlast instances of the other, and other times you want the latter to outlast the former. To fix both these issues, control was inverted, defining a class whose sole responsibility is setting up object relationships (creating objects and introducing them to each other). This is called dependency injection. Injected objects are connected via aggregation rather than composition. The distinction is less important in garbage collected languages, as ownership isn't so important for determining object lifetime. The first option you list approaches Dependency Injection, as $fpd->account = new Account; isn't performed within a method of FPD. Where it differs is that it's done at the global level, rather than being the responsibility of a third class.

An alternative to having objects store references to others is the Registry pattern. A registry is a unique, globally accessible indexed collection of objects. Usually, a registry is exposed via methods rather than as, say, a static property holding a dictionary. The Registry pattern allows an object associated with a given key to vary with time. We could further distinguish registries that allow time variance from those that don't, though it wouldn't be useful here. Some consider it an anti-pattern, as it requires creating global state (though with a much lower potential for bugs than global variables). Read Frank Klein's "Registry Design Pattern - useful or harmful?" and Brandon Savage's "The Registry Pattern Reexamined" before going this route.

Static properties or methods (when the method returns the same (not just equal) object for given arguments; the method can create the object returned and complete tasks related to this, but should have no other non-trivial side effects) can be considered applications of the registry pattern (static properties are registries with nullary keys). I can't think of any examples of the Registry pattern from the standard PHP library or extensions, but Cocoa has a few: NSFileManager's +defaultManager, NSNotificationCenter's +defaultCenter and CGColorGetConstantColor (where the first two are nullary). In .Net, System.Drawing.Brushes is a registry, where the member names are the keys, while System.Windows.Dataformats is a registry that provides getDataFormat methods for access. There are standard PHP classes and functions that use registration (such as streams), but these don't follow the Registry pattern as they don't provide direct access to the registered objects, whereas the Registry pattern is primarily about making objects globally accessible through the collection.
 
Top