In OOP, I can populate (initialize) an object using constructor injection at object creation time, or setter injection, after creation time.
I like the idea of populating object at creation time via constructor, for object parameters that are essential to the object. And use setter methods for things that are non-essential or secondary.
And all was well.
Until, I discovered DataMapper
pattern. In the pattern I
- load up data from the database (data concern)
- map schema colums to object parameters (mapping concern)
- create/update/delete/return the object to its caller (object management concern)
I am quite content with DataMapper essentially being the manager for the object, thereby creating the object, loading data, and then populating that object. If all 3 of the above concerns share the same function, there is no issue as they all seem to seamlessly integrate together.
Problem
The issue arises when I start separating concerns. Namely, the mapping concern. Mapping concern should only concern itself with taking the DB schema column names and copying data from DB resource object over to the new object's data field parameter variables. For me it translates to this
- accept a created object as input parameter
- fill out the object from DB schema via the mapping (using setters for private vars, or directly if vars are public)
- return the filled out object back to its caller.
I am thinking that mapping concern should not be creating the object. This means, the mapping concern cannot use constructor initialization.
Question
What can I do to use separate the mapping concern and still use constructor initialization?
Possible Work-arounds
- Use setter initialization methods only [abandon constructor initialization even when it's desired]
- bundle two different concerns of managing the object (namely creating it [means ability to initialize it via constructor]) and schema-to-object mapping inside the same class. [abandon SRP]
Code Example
For clarity - see comments
class ProductDataMapper
{
public function dataConcern($id)
{
$sql = "SELECT model as name FROM product where id = {$id}";
$result = db_query($sql);
$data = db_fetch_array($result);
return $data;
}
public function mappingConcern($data)
{
//schema to property mapping (to separate concerns)
$parameters = array();
$parameters['name'] = $data['schema_column_name_in_db'];
//In order to use constructor initialization, mapping concern
//has to be the place to *create* the product (using new)
//my question deals with separating 'object creation' from 'object mapping'
//init via constructor - this is the *undesirable* concern for a mapper
$product = new Product($parameters);
//init via setters
foreach ($data as $key => $value) {
$setter = 'set' . ucfirst($key);
$product->$setter($value);
}
return $product;
}
public function getProduct($productId)
{
$data = $this->dataConcern($productId);
$product = $this->mappingConcern($data);
return $product;
}
}
Update
I am thinking that I can separate out object creation WITH mapping into a separate concern.
That concern will be Object-Creation-and-Schema-Mapping-To-Object-via-Constructor
.
Namely, still not perfect, because there will be two mapper concerns
- mapper that creates and initializes the object via constructor
- mapper that uses setters to populate the object