Don't be a bad programmer

I will, in fact, claim that the difference between a bad programmer and a good one is whether he considers his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships.


Linus Torvalds


Creator of the Linux Kernel

Show me your code and conceal your data structures, and I shall continue to be mystified. Show me your data structures, and I won't usually need your code; it'll be obvious.

Eric Raymond

Book: The Cathedral and the Bazaar

Shop Specifications

Minimal approach, nothing fancy, just these objects
  • Item (id, price)
  • Customer (id)
  • Order (id, customer, creation date)
  • Orderitem (id, item, price, order)

Ignore generation or setting of id's for this.

How would you do it?

  • Just sketching the classes
  • Including properties and functions
  • Make it as right as you know how to
  • These are objects and classes, not a database

How most would do it

class Customer
{
    protected $id;
    
    public function getId()
    {
        return $this->id;
    }
}
class Item
{
    protected $id;
    protected $price;
    
    public function getId()
    {
        return $this->id;
    }
    
    public function setPrice($price)
    {
        $this->price = $price;
        return $this;
    }
    
    public function getPrice()
    {
        return $this->price;
    }
}
class Order
{
    protected $id;
    protected $customer;
    protected $created;
    
    public function getId()
    {
        return $this->id;
    }
    public function setCustomer(Customer $customer)
    {
        $this->customer = $customer;
        return $this;
    }
    public function getCustomer()
    {
        return $this->customer;
    }
    public function setCreated(\DateTime $created)
    {
        $this->created = $created;
        return $this;
    }
    public function getCreated()
    {
        return $this->created;
    }
}
class Orderitem
{
    protected $id;
    protected $item;
    protected $price;
    protected $order;
    public function getId() {
        return $this->id;
    }
    public function setItem(Item $item) {
        $this->item = $item;
        return $this;
    }
    public function getItem() {
        return $this->item;
    }
    public function setPrice($price) {
        $this->price = $price;
        return $this;
    }
    public function getPrice() {
        return $this->price;
    }
    public function setOrder(Order $order) {
        $this->order = $order;
        return $this;
    }
    public function getOrder() {
        return $this->order;
    }
}

So?

Most people would do it that way
Some frameworks and IDEs auto generate these for you
Let's have a closer look

Objects in use

$customer = new Customer();
$item = new Item();

$order = new Order();
$order
    ->setCustomer($customer)
    ->setCreated(new \DateTime());

$orderitem = new Orderitem();
$orderitem
    ->setItem($item)
    ->setPrice($item->getPrice())
    ->setOrder($order);

And so?

Still looks good, right?

The good

  • Customer and Item are good this way
  • Fluid interfaces on setters
  • Parameter hints on objects

The bad


Order

  • Customer can be changed, should be impossible
  • Created also can be changed and has to be initialized by hand


Orderitem

  • Item, price and order can be changed, all should be impossible
  • Price needs no setting, it can be taken from the item

The solution

  • Remove setters for properties that are not allowed to change

  • Add properties to the constructor signature, that are absolutely needed for the object

  • Initialize properties in constructor that need no setting or need good default values

The solution in code

class Order
{
    protected $id;
    protected $customer;
    protected $created;
    
    public function __construct(Customer $customer)
    {
        $this->customer = $customer;
        $this->created = new \DateTime();
    }

    public function getId()
    {
        return $this->id;
    }

    public function getCustomer()
    {
        return $this->customer;
    }

    public function getCreated()
    {
        return $this->created;
    }
}
class Orderitem
{
    protected $id;
    protected $item;
    protected $price;
    protected $order;

    public function __construct(Item $item, Order $order)
    {
        $this->item = $item;
        $this->price = $item->getPrice();
        $this->order = $order;
    }
    
    public function getId()
    {
        return $this->id;
    }
    public function getItem()
    {
        return $this->item;
    }
    public function getPrice()
    {
        return $this->price;
    }
    public function getOrder()
    {
        return $this->order;
    }
}

Using the new code

$customer = new Customer();
$item = new Item();

$order = new Order($customer);

$orderitem = new Orderitem($item, $order);


This was the old

$customer = new Customer();
$item = new Item();

$order = new Order();
$order
    ->setCustomer($customer)
    ->setCreated(new \DateTime());

$orderitem = new Orderitem();
$orderitem
    ->setItem($item)
    ->setPrice($item->getPrice())
    ->setOrder($order);

It's adding up

  • Smaller code
  • Easier to read
  • No impossible objects (i. e. order without customer)
  • No forgotten setters
  • No overwritten properties that shouldn't have been

Exceptions

  • Interfaces you can't control (framework, library), force you to implement a setter publicly (consider stubbing)
  • If you need a setter logic, set it with a protected or private method
  • In case of doubt, go with the smaller code
  • Anything else I forgot to mention here

Jumping to Doctrine

  • No jump needed
  • You can do it exactly like this
  • Just add mapping information
  • Think objects, not data rows
  • Then think smarter objects

What about the constructor?

  • Doctrine doesn't call the constructor on loading
  • Only you call it when creating a new object
  • If a field has a default value, put that in the constructor body
  • Think show, don't tell

Smart data structures and dumb code works a lot better than the other way around.

Eric Raymond





So don't be a bad programmer

Instead..

Be Legendary



Thank you for listening



Christian Riesen

Don't be a bad programmer

By Christian Riesen

Don't be a bad programmer

How to be a good programmer and make the smart decisions when it comes to data structures.

  • 1,483