Basics of Object Design - Part One
Tags: objectorienteddesign, beginners, softwaredesign
Published: 2019-01-29 18:17:03

---
title:  Basics of Object Design - Part One
published: true
description: 
tags: ObjectOrientedDesign, Beginners, softwareDesign
cover_image: https://www.colourbox.com/preview/7724015-simple-seamless-pattern-with-damask-motifs-in-black-and-white-drawn-with-short-strokes-texture-for-web-print-holiday-decor-textile-design-fabric-wrapping-paper-invitation-background-wallpaper.jpg
series: Basics of Object Design
---


In my previous post, [Basics of Object Design Series](https://dev.to/omensah/basics-of-object-design-series-28ol), I shared about the book I have been reading on object design, [Style Guide for Object Design](https://leanpub.com/object-design) by [Matthias Noback](https://twitter.com/matthiasnoback) and also sharing my learnings from it through series of blog posts. So far, I have read the first chapter, creating service objects. In this article, we will discuss certain principles that can guide software professionals while creating service objects. 

As software professionals, our main goal is to solve problems. Modeling has been one of the approaches to problem-solving. Through object-oriented modeling which involves the practice of representing problem domains as objects in your software.  Thinking in terms of objects and representing them in code keeps everything organized, flexible, and reusable because objects are all around us. In an application, there will be two types of objects that are likely to be designed and created.  And they are;

  i) objects for performing a task or giving out information
  ii) objects as data storage and their manipulation 



Objects that perform a task are referred to as services. They are publicly known to be doing one thing. For instance, if we take an object like `FileLogger` - logging files is the only functionality that will come to mind. This makes intuitive sense right. And usually, we can get what a service does from even its name. 

## Common things we should know about services.

**They are created immutably**: *Thus they are created once and can be used multiple times without causing changes to their functionality and properties. That means when service objects are created, they should not behave unpredictably *

**They are doers**: *Any object that is using service object can tell what that service does specifically. For instance, having a service with a name `Router` tells that this service is meant for routing functionality in an application* 

## Key Principles of Creating Service Objects.

### 1) Everything needed to create the object must be injected as a constructor argument.

Imagine creating a Router object. We need to provide what will be needed by a Router class to create an object. These dependencies should be injected through the constructor. This makes it well-prepared for use and there is no reconfiguration after its creation.  Let's try this in code. 

```php 
<?php 

class Router
{
    public function __construct(array $routes)
    {
        $this->routes = $routes;
    }

    public function route($route): string
    {
        if (array_key_exists($route, $this->routes))
        {
            return $this->routes[$route];
        }
        
    }
}

$routes = array(
    "/" => "https://omensah.github.io",
    "/resume" => "https://omensah.github.io/resume", 
    "/contact" => "https://omensah.github.io/contact"
);
$routers = new Router($routes);
echo $routers->route("/");
```

From the code, we can tell that the constructor needs only the `routes` dependency to prepare it for use. 


### 2) Whenever an object needs configuration settings, they must be injected as constructor arguments. 

Sometimes creating an object requires part of the object to be configured in order for its client to know about certain decisions. For instance,  when you take a service object like a `FileLogger`, its main purpose is to log message(s) to a file. Well, we can log the message(s) to a file at any directory without the client of the object knowing about that specific location. To prevent that, all we need to do is to pass all the configurations as constructor arguments. Hence the client has explicit knowledge about the log files location. Let's try an example.   

```php

<?php 
final class FileLogger {

    public function __construct(string $logFilePath){

        $this->logFilePath = $logFilePath;
    }

    public function log(string $message): void {
        file_put_contents(
            $this->logFilePath,
            $message,
            FILE_APPEND
        );
    }

}

$logger = new FileLogger("/home/olivermensah/Desktop/practices/OD/logs.txt");
$logger->log('A message');

```


### 3) Data Relevant for a task must be passed as method arguments, not a constructor argument.

You might have noticed in the first two examples, we have constructor and method arguments. Basically, constructor arguments are used to set what the entire object needs while method arguments are injected in a specific method to accomplish the task at hand.  For example, in the  `FileLogger` class, it really makes sense to pass the `message` parameter to the `log` method. Thus, it is the  `log` method only that will need such a dependency to accomplish its task. 

**NB:** If there are other operations that must work on the `message` parameter before using it in the logged method then the method argument won't be necessary.
 

### 4) All constructor arguments should be required

When an object can work without setting up dependencies through a constructor, we should not pass such dependencies at all. There is no such thing as optional dependency where you pass dependency value as null. 
This introduces unnecessary code complications by always checking for nullity of that dependency before its usage. Let's try this in code.

```php 

<?php

// null object

class Car {
    private $model = '';
    public function __construct($model = null){
        if($model){
            $this -> model = $model;
        }
    }
    public function getCarModel(){
        return ' The car model is: ' . $this -> model;
    }
}


$car = new Car();
echo $car->getCarModel();
$car1 = new Car("BMW");
echo $car->getCarModel();

```

These code complications or workaround can be solved through either providing default value,  making all dependencies required or a null object.

```php 

<?php

// default value solution

class Car {
    private $model = '';
    public function __construct($model = "Mercedes"){
        $this -> model = $model;
    }
    public function getCarModel(){
        return ' The car model is: ' . $this -> model;
    }
}

$car = new Car();
echo $car->getCarModel();

$car1 = new Car("BMW");
echo $car->getCarModel();

//required dependency solution 

class Car {
    private $model = '';
    public function __construct(string $model){
        $this -> model = $model;
    }
    public function getCarModel(){
        return ' The car model is: ' . $this -> model;
    }
}


$car = new Car();
echo $car->getCarModel();
$car1 = new Car("BMW");
echo $car->getCarModel();


// null object

interface CarModel{
    public function getCarModel(): string;
}

class NullCarModel implements CarModel{
    public function getCarModel(): string{
        return ' The car model is: ';
    }
}

class MercedesCarModel implements CarModel{
    public function getCarModel(): string{
        return ' The car model is: is Mercedes';
    }
}

class Car {
    private $model = '';
    public function __construct(CarModel $carModel){
        $this->carModel = $carModel;
    }
    public function getCarModel(){
        return  $this->carModel->getCarModel();
    }
}


$car = new Car(new NullCarModel);
echo $car->getCarModel();

$car1 = new Car(new MercedesCarModel);
echo $car1->getCarModel();

```

The above code introduces the default value, required dependency and null object to workaround optional dependency. This is because we don't want to optionally pass dependency, we want to pass dependency when needed for use. I think the null object provides a nice workaround and makes dependency more explicit to be injected.  With the null object, what you have to do is to create a type for all the possible dependency using `interface`. These dependencies will be created using this type and injected later for use.
For instance, the car required a `model` as its dependency, I created a type called `CarModel` and all car models are created with this type and later injected via the constructor. So we passed `NullCarModel` when don't have any `CarModel` and `MercedesCarModel` for Mercedes car models. You can create as many models you want.


### 5). Use on constructor injection, not setter injection.

In creating a service object, it must be defined immutably. That's means methods that mutate the properties of a service object must not be allowed.  Let's try in code;

```php
final class Printer {
    
    public function ignoreErrors(bool $ignoreErrors): void{
        $this->ignoreErrors = $ignoreErrors;
    }
    public function print(){
        if($this->ignoreErrors) {
            echo "Error ignored";
        }
        else{
            echo "Errors not ignored";
        }
    }
// ...
}
$printer = new Printer();
$printer->ignoreErrors(false);
$printer->print();

```

Looking at the code above, we have a method that can set the property of service object to a boolean state. This means the object is not going to be predictable.  
Therefore, it is advisable to only use a constructor to set the values needed once and those values should not be changed as refactored below;

```php

final class Printer {
    
    public function __construct(bool $ignoreErrors){
        $this->ignoreErrors = $ignoreErrors;
    }
    public function print(){
        if($this->ignoreErrors) {
            echo "Error ignored";
        }
        else{
            echo "Errors not ignored";
        }
    }
// ...
}
$ignoreErrors = true;
$printer = new Printer($ignoreErrors);
$printer->print();

```


### 6) Inject what you an object needs, not where it can get it from.

When working on a large application with a large codebase, there is always a special service that holds other services for their easier usage.  This special service might be called `service locator` or `service registry` or `service manager`. 

The main goal of a `service locator` is to make other services available to be use easily by taking care of their instantiations.  Let's imagine having `Router` and `FileLogger` service. They will be loaded into `service locator`. And any part of the application that needs any of the registered services, they can reach the service locator using a specific identifier and if the identifier is not found, an exception is thrown. Let's try this in code.

```php

<?php 
final class FileLogger {

    public function __construct(string $logFilePath){

        $this->logFilePath = $logFilePath;
    }

    public function log(string $message): void {
        file_put_contents(
            $this->logFilePath,
            $message,
            FILE_APPEND
        );
    }

}

class Router
{
    public function __construct(array $routes)
    {
        $this->routes = $routes;
    }

    public function route($route): string
    {
        if (array_key_exists($route, $this->routes))
        {
            return $this->routes[$route];
        }
        
    }
}


final class ServiceLocator {

    public function __construct(){
        $this->services = [
            'FileLogger' => new  FileLogger("/home/olivermensah/Desktop/daily-practices/OD/log.txt"),
            "Router" => new Router(
                array(
                    "/" => "https://omensah.github.io",
                    "/resume" => "https://omensah.github.io/resume", 
                    "/contact" => "https://omensah.github.io/contact"
                )
            )
            // We can have any mumber of services here...
        ];
    }
    public function get(string $identifier): object{
        if (!isset($this->services[$identifier])) {
            throw new LogicException( 'Unknown service: ' . $identifier);
        }
        return $this->services[$identifier];
    }
}

//Injecting what you need to get them from service. 
final class FileLoggerController {

    public function __construct(FileLogger $fileLogger){
        $this->fileLogger = $fileLogger;
    }

    public function saveLog(){
        return $this->fileLogger->log("Oliver");
    }
}

$locator = new ServiceLocator();
$logger = new FileLoggerController($locator->get(FileLogger::class));
echo $logger->saveLog();

```

In the above code, we only passed the `FileLogger` which is needed by the `FileLoggerController` to work. We could have passed the service and then later retrieve the necessary service.  That goes against the style guide. Only pass what is needed not where you can get it from.



### 7)  Make sure all dependencies are explicit.

Sometimes, we embed codes deeply in a class without letting the client of the object know whether that object has some dependencies. Let's look at this in code;

```php

<?php

class Cache{
    private static $data = array(
        'post' => "Data is the new Gold", 
        'recent_post' => "Together we are building the tech scene in Ghana."
    );


    public static function has($key){
        return isset(self::$data[$key]);
    }

    public static function get($key){
        return self::$data[$key];
    }
}

final class DashboardController {
    public function getPost(){
        
        if (Cache::has('recent_post')) {
            echo Cache::get('recent_post');
        }else{
            echo "No Recent posts";
        }
    }
}


$ctrl = new DashboardController();
$ctrl->getPost();

```

We can see that the `DashboardController` has `Cache` as a dependency but we did not explicitly let the client of this object know that it has a dependency. This must be avoided.  Make sure that all dependencies are made explicitly known by clients. Let's refactor the previous code. 

```php
class Cache{
    private static $data = array(
        'post' => "Data is the new Gold", 
        'recent_post' => "Together we are building the tech scene in Ghana, one 
                        meetup at a time"
    );


    public static function has($key){
        return isset(self::$data[$key]);
    }

    public static function get($key){
        return self::$data[$key];
    }
}

final class DashboardController{

    public function __construct(Cache $cache){
        $this->cache = $cache;
    }
    public function getPost(){
        // $recentPosts = [];
        if ($this->cache->has('recent_post')) {
            // $recentPosts = Cache::get('recent_posts');
            echo $this->cache->get('recent_post');
        }else{
            echo "No Recent posts";
        }
    }
    
}

$ctrl = new DashboardController(new Cache());
$ctrl->getPost();

```
Now any client using this `DashboardController` will know that it uses `Cache` as a dependency. This means never hide your dependencies, make them know through constructor or method injection.


### 8). Do nothing in the constructor function, Only assign properties.

Anytime that you are creating a service object, it means you are injecting constructor argument(s) to prepare it for use. Anything outside assigning the argument(s) to object property must be done in another method of the object, not in the constructor method.  Let's review this code. 

```php 
final class FileLogger{
    private  $logFilePath;

    public function __construct(string $logFilePath){
        $logFileDirectory = dirname($logFilePath);
        if (!is_dir($logFileDirectory)) {
            // create the directory if it doesn't exist yet
            mkdir($logFileDirectory, 0777, true);
        }
        touch($logFilePath);
        $this->logFilePath = $logFilePath;
    }
}

$logger = new FileLogger("/home/olivermensah/Desktop/oliver/d.txt");

```

In the constructor, we are retrieving the directory of the giving path, checking to see if the directory does not exist, then creating that directory if possible and finally adding the file in that directory.  That's a lot of work to be done by the constructor.  We can do better by extracting those operations from the constructor into a separate method. 

```php

final class FileLogger{
    private $logFilePath;
    public function __construct(string $logFilePath){
        $this->logFilePath = $logFilePath;
    }
    public function log(): void{
        $this->ensureLogFileExists();
    }
    private function ensureLogFileExists(): void{
        if (is_file($this->logFilePath)) {
            return;
        }
        $logFileDirectory = dirname($this->logFilePath);
        if (!is_dir($logFileDirectory)) {
            // create the directory if it doesn't exist yet
            mkdir($logFileDirectory, 0777, true);
        }
        touch($this->logFilePath);
    }
}

$logger = new FileLogger("~/Desktop/oliver/a.txt");
$logger->log();
```
Now, we have been able to extract the work done by the constructor into a different method. 

Another means to avoid the constructor method to do a lot of work, is by setting up the necessary configurations before instantiating the class and the result is passed as a constructor argument. This is usually called bootstrapping. 

```php

final class FileLogger{
    public function __construct(string $logFilePath){
        /*
        * We expect that the log file path has already been
        * properly set up for us, so all we do here is a "sanity
        * check":
        */
        if (!is_writable($logFilePath)) {
            throw new InvalidArgumentException(sprintf(
                'Log file path "%s" should be writable',
                $logFilePath
            ));
        }
        $this->logFilePath = $logFilePath;
    }   
    public function log(string $message): void{
        // No need for a call to `ensureLogFileExists()` or anything
        // ...
    }
}
/*
* The task of creating the log directory and file should be moved to
* the bootstrap phase of the application itself:
*/
$logFilePath = "/home/olivermensah/Desktop/oliver/ca.txt";

if(!is_file($logFilePath)) {
    $logFileDirectory = dirname($logFilePath);
    if (!is_dir($logFileDirectory)) {
        // create the directory if it doesn't exist yet
        mkdir($logFileDirectory, 0777, true);
    }
    touch($logFilePath);
}
$logger = new FileLogger($logFilePath);


```

### 9). Throw an exception when an argument is not valid

When the client of the class provides an argument that is not valid, the type checker will show warning(s). However, not in all cases that the client will pass the wrong type. Passing the right type but a value that does not make sense for the operation of the object methods. In that case, you will need to throw an exception to ensure sanity checks. 

```php

<?php 

class Score  {
    private $minimumLevel;
    public function __construct(int $minimumLevel) {
        if ($minimumLevel <= 0) {
            throw new InvalidArgumentException('Minimum alerting level should be greater than 0');
        }
        $this->minimumLevel = $minimumLevel;
    }
}

$score = new Score(-100);

```


## Conclusion 

Having gone through the various style guides on how we can create good service objects within a software application, I think the following takeaways will solidify our fundamentals on how we can design service objects; 
  
 1) Everything needed to create the object must be injected as a constructor argument.

 2) Whenever an object needs configuration settings, they must be injected as constructor arguments. 

 3) Data Relevant for a task must be passed as method arguments, not a constructor argument.

 4) All constructor arguments should be required

 5) Use on constructor injection, not setter injection.

 6) Inject what you an object needs, not where it can get it from.

 7) Make sure all dependencies are explicit.
 
 8) Do nothing in the constructor function, Only assign properties.
 
 9) Throw an exception when an argument is not valid

I hope you find them very useful. 
**NB**: Let's discuss in the comment section your thoughts about these guides. 


## Further Reading

[Matthias Noback's](https://twitter.com/matthiasnoback) description is one of the best in-depth discussion of  [Style Guide for Object Design](https://leanpub.com/object-design) from a practical design principle perspective. 

## Keep In Touch
 
Let's keep in touch as we continue to learn and explore more about software engineering. Don't forget to connect with me on [Twitter](https://twitter.com/Oliver_Mensah) or [LinkedIn](https://www.linkedin.com/in/olivermensah/)