Q&A: Answering Some Questions About Object-Oriented Programming

« »

Last week I wrote about five tips to improve object-oriented code. This generated a number of important questions, which I will attempt to answer for those who asked them.

“Often times when a developer gives each object only one responsibility, they tightly couple objects together.” Can you explain?
There are two major pitfalls in object-oriented programming: trying to do too much with an object, and trying to couple a number of objects too closely together. For this example, we’ll use the engine metaphor.

<?php

class Engine {
protected $crankshaft;
protected $pistons;
protected $radiator;

public function __construct()
{
$this->crankshaft = new Crankshaft($this);
$this->pistons[] = new Piston($this);
$this->pistons[] = new Piston($this);
$this->pistons[] = new Piston($this);
$this->pistons[] = new Piston($this);
$this->radiator = new Radiator($this);
}
}

In the above example, the Engine has one job: to direct the function of the crankshaft, pistons and radiator in order to move an automobile along. But we have a couple of fatal flaws. The first fatal flaw is that we pass the Crankshaft, Pistons and Radiator a copy of the Engine; while this might make development easy at some point (because the Radiator can send messages to the Engine) it is a poor design. It’s a poor design because the Engine controls the components, not the other way around. Additionally, we are instantiating the Crankshaft, Pistons and Radiator inside our constructor method, which means that the same Crankshaft, Piston and Radiator classes be available wherever we go. Let’s decouple this and fix it.

<?php

class Engine {
protected $crankshaft;
protected $pistons;
protected $radiator;

public function __construct(CrankshaftI $crankshaft, array $pistons, RadiatorI $radiator)
{
$this->crankshaft = $crankshaft;
foreach($pistons as $piston) {
if(!($piston instanceof PistonI) {
throw new EngineException('Improper piston type used');
}
$this->pistons[] = $piston;
}
$this->radiator = $radiator
}
}

How is this an improvement? First, we make use of dependency injection in our constructor: rather than automatically creating new objects, we expect that the objects have already been created and are being “installed” (injected) into our Engine. Second, we require that the objects utilize a certain interface. Now, some might argue that this is still tight coupling but I disagree: while you must have the interface available to you, interfaces do not define function. They simply define the methods that are available and must have been defined.

In this case, you can pull these interfaces and the Engine class out and build the library elsewhere, so long as you include the various methods of CrankshaftI, PistonI and RadiatorI. This is acceptable because every Piston will have certain methods (fire, upSwing, downSwing, injectFuel) but the functionality can be different depending on the type of piston you want.

This also gives every object its own job. The Piston is only responsible for doing Piston-related tasks; the Engine is responsible for controlling the Piston, but not executing its specific job functions. The Piston doesn’t have to be smart about the Engine or know about the Radiator; it only needs to know about its job. And so, each object has only one clearly defined role.

I do not know about dependency injection – do you have any links that do not require subscription?
I’ll talk about Dependency Injection here. It’s actually a really simple topic.

Since PHP 5, when you pass an object into a function, class, or by value, what you’re actually doing is passing it in a reference-like state. I say “reference-like” because it behaves somewhat like a reference but somewhat not (to learn more read this). When you act on the object, you change the object globally – that is, all instances of the object are changed. This is because when you pass an object by value you’re not copying the object; you’re simply passing the internal PHP value that finds the object in a lookup table and acts upon it. This is a change from PHP 4, where objects were basically arrays that had functions.

This allows us to do some cool things, one of which is dependency injection. Dependency injection is the process of adding an object to another object by passing it as an argument in the constructor or other method, to make that object available to the dependent object. Here is a code sample:


class AppleSeed extends FruitSeed
{
// obviously some definition here...
}

class Apple extends Fruit
{
public function __construct(AppleSeed $seed)
{
$this->seed = $seed;
}
}

$apple = new Apple(new AppleSeed);

Everyone knows that apple seeds grow in apples, so it might seem silly at first to inject one. But consider the benefits: imagine that you want to do a unit test on the Apple’s methods. If the AppleSeed contains methods that could potentially change the Apple’s values in an unpredictable way, you need to use a mock object to make sure that the only thing that potentially changes is the methods you’re testing. If you instantiate the AppleSeed in the Apple class, however, you can’t do that.

Can you please explain ‘one object – one job’ concept?
The asker of this question was concerned that if you were to create, say, a BumbleBee, that you would have to create a bunch of other objects that composed the BumbleBee, because the BumbleBee has multiple tasks. But this is not what the “one object – one job” concept is all about.

Objects should be complete in that they should not be responsible for ancillary tasks that do not relate directly to the object. For example, a BumbleBee has a number of roles: to pollinate flowers, to bring pollen back to the nest for honey production, to fly, etc. The BumbleBee class should define all of these (and more) methods for what a BumbleBee does, because these are the actions a BumbleBee takes.

What the BumbleBee class should not do is define the BumbleBee’s heart structure or brain structure. It should not define the structure and operation of the wings. The BumbleBee should be composed of objects that allow for a heartbeat, brain function and flying, but when you invoke the BumbleBee::fly() method, that method should in turn invoke the API on the WingStructure object to actually perform the flying.

The BumbleBee thus has only “one job”: to be a BumbleBee. All the ancillary functions are handled by other objects that are tangential but not specifically associated with the BumbleBee and his job.

Summary
Object-oriented programming is difficult. It’s not difficult because of the syntax, but because of the skills and aptitudes required to do it well. However, these tips and skills should make it simple enough to write effective, well-designed code that follows the rules of abstraction, encapsulation and decoupling.

Brandon Savage is the author of Mastering Object Oriented PHP and Practical Design Patterns in PHP

Posted on 10/30/2009 at 1:00 am
Categories: Object-Oriented Development, Technology, Best Practices
Tags: , , ,

Rob... (@akrabat) wrote at 10/30/2009 3:01 am:

Whilst you have shown benefits to Engine of injecting the Pistons, Crankshaft and Radator into it, you haven’t shown how to avoid having a consumer of an Engine having to know too much detail about how an engine is made up.

e.g. if I have class Car that uses an Engine, the car now needs to know about Pistons etc. At the car level of abstraction, you may want to swap in different types of engine (e.g. remove a petrol one and replace with an electric one) without having to worry about the type of pistons the new engine uses.

I think it would be beneficial to provide some discussion on the practical ways to instantiate an object like this. Obviously a DI system like Pico is one way, or a factory function could also be used.

Regards,

Rob…

Ivo (@Ijansch) wrote at 10/30/2009 4:42 am:

Yes, Rob’s point is valid. You’re not taking dependency out of the system, you’re moving it up. If the engine is optimized to no longer need a radiator, consumers are still passing you a useless radiator. Rob and Brandon, What do you think of these alternative way to inject the dependency?

class Engine
{
protected $radiator;

public function setRadiator(RadiatorI $r) {
$this->radiator = $r;
}

public function getRadiator() {
if (!$this->radiator) $this->radiator = new
DefaultRadiator();
return $this->radiator;
}
}

This variant allows use of the engine without knowing it even has a radiator; however, consumers that are savvy about such things can still inject their own radiator.

Variant 2:

class Engine
{
protected $parts;

public function __construct(Parts $p) {
$this->parts = $p;
}

… // code that needs a radiator.
$radiator = $this->parts->getRadiator();
}

// In this case, Parts can act like a broker that injects those objects that are necessary, and apps can tell the broker they want a specific radiator to be used whenever someone requests it.

(e.g. Government calls $parts->setRadiator(new EcoFriendlyRadiator());

Ivo (@Ijansch) wrote at 10/30/2009 4:47 am:

The article you link to actually explains very well why setter based injection is better than constructor based injection, and it has a nice alternative way using factories:

http://www.potstuck.com/2009/01/08/php-dependency-injection/

Brandon Savage (@brandonsavage) wrote at 10/30/2009 5:33 am:

The assumption was that an engine will always need a radiator (though not always true in real life, as Volkswagen and Porsche showed). If we were to abstract this as a programming technique rather than a car part, we might see that the Controller needs a Request, a ResponseFormatter and an Action to complete.

With regards to where the engine is constructed, I would make the assumption (and the practical choice) of building a Factory where the Engine is constructed and then placed into the Car along with the Car’s other components (Wheels, Brakes, Transmission, etc.). While Rob is right in that I did not show that a consumer of an Engine would not need to know how an Engine works, my example was far from feature complete and a fully feature complete example would have been nearly impossible.

I do appreciate the comments, and I merit in each approach.

Stephan Hochdoerfer (@shochdoerfer) wrote at 10/30/2009 9:57 am:

@Ivo: I`d prefer constructor based injection since it automatically enforces the completeness of the instantiated object. With setter based injection it is not clear in which order things need to be instantiated and wired together.

And since I hope you use some kind of DI container that automatically does the wiring for you (e.g. based on some kind of configuration file), it should basically not matter how the wiring is done.

Herman Radtke (@hermanradtke) wrote at 10/30/2009 4:55 pm:

@Ivo Here are my guidelines for constructor vs setter DI:

Constructor injection is generally used when the injected dependency will last the lifetime of the object or the dependency requires injection in a certain order.
Setter injection is generally used if the injected dependency has a lifetime shorter than the object it is passed into.

Rory wrote at 10/30/2009 7:04 pm:

@Rob – Just to clarify – a Car doesn’t need to know about Pistons at all because you inject the (already created) Engine into the Car

class Car {

public function __construct(Engine $engine) {

You don’t need your car worrying about how to create it’s own engine.

Les wrote at 10/30/2009 7:23 pm:

Whilst I understand the hows and whys of Dependency Injections it’s not something I’ve been fully supportive of implementing – I’m not convinced it’s an ideal solution.

Seams to me it’s top heavy even for larger application development; my opinion is that you can achieve much the same effect solely with Factories – in which case you simply swap out factories.

I am of the feeling we need to streamline an applications foot print and most DI solutions are additional bloat – and do remember whatever solution you do use that in it’s self is a dependency.

To close, I am also left with the impression that many developers want to or would wish to implement DI as a way around the after affects of poor design thinking that they are safe from it’s [DI] use.

The use of any DI solution does not solve nor excuse issues brought upon by poor design.

Rob... (@akrabat) wrote at 10/30/2009 9:07 pm:

Rory,

Are you really suggesting that the Car’s constructor takes an instance of: Engine, Wheels, Seats, Drivetrain, ExhaustSystem, Electrics, etc. ?

What does the object that instantiates the Car look like?

Whilst pushing the decision up the chain can help, at the end of the day we need to write perfomant apps that can be maintained long term by people that didn’t write the original system. 90% of the time it’s gonna be fine to let the engine decide what type of pistons it needs. We just need to be able to override the default choice (usually for testing parts in isolation).

Regards,

Rob….

Richard Lynch (@LynchRichard) wrote at 10/31/2009 1:04 am:

I would assume that a car might take a few parameters:
engine
body
chassis

The body would take in various subsystems, as would the engine and chassis.

Anything with more than a handful of parameters is just a pain to use.

It all depends, pun intended, on what your application has to actually DO with a “car”.

Rory wrote at 10/31/2009 2:30 pm:

I would agree with Richard. If you’re object has more than (for example) 5 dependencies it’s probably doing too much. DI lets you recognize this so you can narrow the foccus of your objects.

In terms of instantiation, you are just moving the creation logic somewhere else but that is the whole point. The business of creating objects is separated from the normal work of the objects themselves. That is the specific goal. If you can’t find any benefit in that then DI is not for you.

« »

Copyright © 2023 by Brandon Savage. All rights reserved.