Приглашаем посетить
Лермонтов (lermontov-lit.ru)

Hack 75. Break Up Big Classes with Composites

Previous
Table of Contents
Next

Hack 75. Break Up Big Classes with Composites

Hack 75. Break Up Big Classes with Composites Hack 75. Break Up Big Classes with Composites

Use the Composite pattern to break megaclasses into small, manageable classes.

I have a curious reaction when I hear news about enormous databases storing everything anyone would ever want to know about me. While most people are immediately concerned about the privacy implications, I think about how poorly designed that system almost certainly is. I'm almost positive there's one awful mega-object in there called Person, probably with 4,000 fields and 8,000 methods.

How do I know that? Because I've seen and maintained objects like that before! What that kind of class really needs is the Composite pattern. The Composite pattern would retain the Person class, but would have groups of those 4,000 fields lumped into contained child objects; the Person object would really comprise 100 or so objects, each containing other small objects (which might contain even smaller objects, and so on).

Now, I'm not saying I've seen classes this bad in PHP; but I have seen classes with upward of 100 fields, simply because the objects represent a set of tables that have that many fields related to a single entry. This hack demonstrates how to take a Customer class (with far too many fields) and break it up into several smaller classes, but at the end of the process, still have just a single composite Customer class.

Hack 75. Break Up Big Classes with Composites

Well, in the sample, I'm breaking up only eight or so fields; I'll leave it to you to extrapolate from this to the several hundred fields I've seen on objects before.


The Customer construction is shown in Figure 7-9. The Customer class contains one of each of the CustomerName and CustomerAddress objects.

Figure 7-9. The Customer composite class with its child classes
Hack 75. Break Up Big Classes with Composites


7.10.1. The Code

Save the code in Example 7-11 as composite.php.

Example 7-11. A Customer composite object made up of smaller objects
	<?php
	class CustomerName
	{
	  public $first = "";
	  public $middle = "";
	  public $last = "";
	}

	class CustomerAddress
	{
	  public $line1 = "";
	  public $line2 = "";
	  public $city = "";
	  public $state = "";
	  public $zip = "";
	}

	class Customer
	{
	  public $id = null;
	  public $name = null;
	  public $address = null;

	  public function Customer()
	  {
	    $this->name = new CustomerName();
		$this->address = new CustomerAddress();
	  }

	  public function Load( $id )
	  {
	    $this->id = $id;
		$this->name->first = "George";
		$this->name->middle = "W";
		$this->name->last = "Bush";
		$this->address->line1 = "1600 Pennsylvania Ave.";
		$this->address->line2 = "";
		$this->address->city = "Washington";
		$this->address->state = "DC";
		$this->address->zip = "20006";
	  }	

	  public function Update()
	  {
	    // Update the record in the database
		// or insert the record if there is no id
	  }

	  public function __toString()
	  {
	    return $this->name->first." ".$this->name->last;
	  }
	}	

	$cust = new Customer();
	$cust->Load( 1 );
	print( $cust );
	print( "\n" );
	?>

7.10.2. Running the Hack

You run this script from the command line using the PHP interpreter:

	% php composite.php
	George Bush

The code creates a new customer and loads in record #1. I've hardcoded that to be George W. Bush. The code then prints the Customer object.

There isn't much to this; the idea is as effective as it is simple. You shouldn't have megaclasses with 100 fields. You should have small grouped classes, such as CustomerName and CustomerAddress, that you can composite into larger featuresin this case, the Customer class. Even better, it's possible to reuse a class like CustomerAddress in other classes that require postal addresses.

A good clue for when a Composite pattern is required is when the data for an object is spread across multiple database tables. Each related table should be its own object or data structure.

The Composite pattern also allows for database read optimization. Because loading each subobject, such as the address, will require a different query, it's possible to do that lazily. In other words, you can delay the loading of a particular subobject until the data for that object is needed. This avoids your code needing to grab hundreds of fields in multiple tables when all you really need is a person's first and last name.


Previous
Table of Contents
Next