Kamafeather
Kamafeather

Reputation: 9845

How to manage classes configuration and propagate it to deeper levels

I have some classes that need to interact. Every class has many options that could be tuned to get one behaviour or another.

(note: I didn't test the code so there could be some syntax error)

(note 2: I write in PHP, but I guess the question is valid for every OOP language as well)

<?php

class Process
{
    protected $element;
    protected $config;
    protected $default_config = array(
        'process_path' => '/bin/command',
        'default_output_path' => '/tmp/',
        'element_conf' => array()
    );

    public function __construct(array $config)
    {
        $this->config = $config;
        $element_config = array_key_exists('element_conf', $this->config) 
            ? $this->config['element_conf'] 
            : array();
        $this->element = new Element($element_config);
    }

    public function run()
    {
        $this->element->behave();

        // ...
    }

    // ...
}

class Element
{
    protected $type;
    protected $config;
    protected $default_config = array(
        'behaviour_type' => 'evil',
        'can_kill_people' => true
    );

    public function __construct(array $config)
    {
        $this->config = array_replace($this->default_config, $config);
        $this->type = $this->config['behaviour'];
    }

    public function behave()
    {
        // ...
    }

    // ....
}

class Config
{
    // get data from config file and makes it available with a static getter/setter
}

-------------------------------

$element_conf = array(
    'can_kill_people' => false
);
$process_conf = array(
    'default_output_path' => Config::get('default_output_path'),
    'element_conf' => $element_conf
);

$pr = new Process($process_conf);
$pr->run();

I would like to keep the encapsulation as high as possible, so I avoided to have a direct reference into the classes to the Config class (the config could arrive from some other place as well).

I would like to receive your thoughts about:

Upvotes: 0

Views: 133

Answers (2)

Loenix
Loenix

Reputation: 1090

There is no best way to do something globally, there is a best way to do what you want (generic or not) (apply first assertion :D).

The config defaults

A configuration item may be mandatory or not but should always have a default value (so it could be not mandatory). In my mind, there is 2 way to provide a default value:

  1. The declarative way
    When you instanciate the configuration object, you provide all default values, but no dynamic values are possible if the source is not dynamic.
  2. The "inline" way
    You provide the default value when you request the current value.

But, in fact, you could use both using your own rules.

Using these rules, you could provide a default value to the configuration of your Element. But its current configuration could be provide as a sub-config or another source... that Process can use.

The declarative configuration

In your source code, the configuration is declarative, this means when you develop it, you know that configuration exists and must/could/should be provided. So it doesn't matter if you use an object with private element to store your config values/format or a simple constant per config key.

In my framework, all configurations are stored as arrays in a protected member of a Config class. This is due to the fact i need genericity but i don't allow missing values in config files.

OOP allows only OOP

When you use OOP, use it as much as you can, an object is an instance of a class, the class is declared, you know what its contents means. You can not do generic sources if you don't use objects.

Upvotes: 1

hynner
hynner

Reputation: 1352

If your process class is to be parametrized by Element object than pass an Element object as a parameter and not its parameters as a parameter. This approach also emphasize that Process class depends on Element class which is not immediately visible from Process constructor right now.

As for the array configuration - in my opinion using arrays is easy for writing but makes it hard to actually use the class, how will you know easily what settings your class support? I'd rather use either individual parameters in constructor or separate configuration class with defined properties.

How you do things inside the class itself doesn't really matter that much as long as you wrap it up in some reasonable public interface. What you want to do is keep as much stuff private as possible to keep the ability to change the inner workings of the class if necessary while maintaining backwards compatible public interface. Also keep in mind that protected is pretty much the same as public when it comes to your ability to change it without breaking backwards compatibility as protected properties and methods can be used by child classes.

One last thing - if your class really have many parameters that change its behavior then maybe you should consider whether it wouldn't be better to split these different behaviors to new classes that would extend the base class or implement its interface (whatever suits your situation better).

Upvotes: 1

Related Questions