-2

I receive an object during some process and this object needs to figure out its coloring scheme. For example, I have a coloring scheme that is stored like this:

class FirstScheme {
    public static $COLORS = array('1' => 'green', '2' => 'red', ...);
}
class SecondScheme {
    public static $COLORS = array('1' => 'red', '2' => 'green', ...);
}

I know all the coloring schemes names in advance; they can only change when the code changes. But the coloring scheme to be used for each object needs to be determined at run-time by matching the attribute of this object.

And here I don't know what to do. In python I would define a dict holding the mappings of color schemes to names like this:

d = {'attr_value1': FirstScheme, 'attr_value2': SecondScheme, 'attr_value3': FirstScheme, ...}

And then just access the "COLORS" variable, because every class should have it. But in PHP there is not way to reference a class in a such way, so what is the right way to do it? Note that more than one attribute can map to the same coloring scheme.

Danack
  • 24,939
  • 16
  • 90
  • 122
dragoon
  • 5,601
  • 5
  • 37
  • 55
  • 2
    What if you pass objects, not classes? Static methods/properties are usually considered as a bad practice. In the case of objects - you would have a single class `ColorScheme` and instantiate it with different schemes. – zerkms May 25 '13 at 11:49
  • Why? I don't understand why I need an object when my coloring schemes are pre-determined. I don't need to instantiate them, I know them in advance. – dragoon May 25 '13 at 11:51
  • how the fact that they are predetermined changes the fact that solution with statics sucks? Create a builder that creates various color schemes by request and tie your classes by interfaces, not by hardcoded class and property names – zerkms May 25 '13 at 11:54
  • @dragoon `Coloring scheme is need to be determined at run-time by matching the attribute of this object` Can you explain further? – hek2mgl May 25 '13 at 11:56
  • @hek2mgl Sure, I know all possible coloring schemes, but I might add more in the future. Each incoming object has some attribute, which allows to unambiguously map in to one of the schemes. – dragoon May 25 '13 at 12:02
  • Still don't get it. Don't make it too complicated. Btw, is my answer already ok for you? (it got 2 upvotes). Is something missing? – hek2mgl May 25 '13 at 12:04
  • @hek2mgl I added a comment to your answer on the part I don't understand. – dragoon May 25 '13 at 12:07
  • What do you mean by `mapping` in `run-time` ? What do you expect your output to be ? – Baba May 25 '13 at 12:12

3 Answers3

2

If every class should have the colors, define the interface that allows to get them:

interface ColorsProvider {
    function getColors();
}
class FirstScheme implements ColorsProvider {
    public static COLORS = array('1' => 'green', '2' => 'red', ...);

    public function getColors() {
        return self::COLORS;
    }
}
class SecondScheme implements ColorsProvider {
    public static COLORS = array('1' => 'red', '2' => 'green', ...);

    public function getColors() {
        return self::COLORS;
    }
}

Then, where you have stack of yout params:

$a = array(
  'attr_value1' => new FirstScheme(),
  'attr_value2' => new SecondScheme(),
);

You can call:

$param = 'attr_value1';

if(!isset($a[$param]))
    throw new Exception("undefined param");

if(!($a[$param] instanceof ColorsProvider))
    throw new Exception("Param should point to ColorsProvider");

$a[$param]->getColors();

Please note that it is full-objective. In PHP there are simplier ways to get this effects, but my solution is just elegant.


The another point is the interface completely separates the source of colors. There would be from file, database, xml, hardcoded etc.


Default implementation might be:

abstract class DefaultColorsProviderImpl implements ColorsProvider             {
    protected static COLORS = array();

    public function getColors() {
        return self::COLORS;
    }
}

class FirstScheme extends DefaultColorsProviderImpl {
    protected static COLORS = array( ... );
}

But still allows to make generic implementation that returns colors from e.x. from file.

Athlan
  • 6,389
  • 4
  • 38
  • 56
  • `getColors()` implementation might (must?) be moved to a parent abstract class. And color property doesn't need to be either public or static – zerkms May 25 '13 at 11:57
  • might be, but this solution (using interfaces) allows you to get colors from any source, ot only hardcoded array (as i mentioned). The another way is make default implementation (`ColorsProviderArrayImpl`) that returns colors from array (and it is popular solution, what you have mentioned, but still based on interfaces that allows you to create completely different implementation). – Athlan May 25 '13 at 11:59
  • I still don't see any reason for code duplication (they still implement the same interface) or for leaving `$COLORS` property public and static (as long as you've decided to retrieve it using method, not directly) – zerkms May 25 '13 at 12:00
  • Consider: `abstract class DefaultColorsProviderImpl implements ColorsProvider { proteted static COLORS = array(); public function getColors() { return self::COLORS; } }` `class FirstScheme extends DefaultColorsProviderImpl { proteted static COLORS = array( ... ); }` Here is no redundant code. – Athlan May 25 '13 at 12:02
  • @Athlan The reason why I wanted it to be static is that more than one attribute can (and will) map to the same scheme. Using your example I would have to instantiate same schemes many times for different attribute values, which I consider not good. – dragoon May 25 '13 at 12:05
  • The solution is use singleton or static method that will return static field. – Athlan May 25 '13 at 12:06
1

An alternative to hard-coding the colors in their own classes would be the following approach:

class ColorScheme {

   protected $colors;

   public function __construct(array $colors) {
       $this->colors = $colors;
   }

   public function getColors() {
       return $this->colors;
   }

}

$scheme1 = new ColorScheme(array('red', 'blue', 'green'));
$scheme2 = new ColorScheme(array('yellow', 'pink', 'cyan'));

The equivalent for a python dictionary in PHP is an associative array. So you could do this:

$d = array (
    'values_1' => $scheme1->getColors(),
    'values_2' => $scheme2->getColors()
);
hek2mgl
  • 152,036
  • 28
  • 249
  • 266
  • I still don't understand the part how I would get the mappings in run-time. – dragoon May 25 '13 at 12:07
  • `$mapping = $scheme1->getColors();` Or what do you mean with `mapping` ? – hek2mgl May 25 '13 at 12:07
  • Ok, I will try to explain differently. I have coloring schemes, like a few of them. They are known in advance. And they in run-time I have incoming objects, which possesses an attribute, that allows to find out the scheme I need to apply to it. That's why I put {'attr_value1': FirstScheme, ...} in my question. – dragoon May 25 '13 at 12:12
  • It doesn't explain anything actually. – zerkms May 25 '13 at 12:13
  • Sorry, I don't get it. Why not just posting a code example that does not work for you? – hek2mgl May 25 '13 at 12:13
  • 1
    @dragoon: are you sure we're not solving the [XY-problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) at the moment? – zerkms May 25 '13 at 12:13
  • Ok, I give up, will accept Athlan's solution because he got the problem right. – dragoon May 25 '13 at 12:20
  • @dragoon: actually there is no principal difference in solutions. – zerkms May 25 '13 at 12:21
  • @zerkms He showed how to get the scheme based on the $param and told about singleton solution. Again, in my opinion, schemes should not be instantiated for each object or for each attribute, because there are only a few different schemes which I need to define manually somehow. – dragoon May 25 '13 at 12:27
  • 1
    @dragoon: right, but it doesn't explain why they should be **static**. Any good reason for that? – zerkms May 25 '13 at 12:29
1

Of course you can:

$schemes = [
  'attr_value1' => FirstScheme::$COLORS,
  'attr_value2' => SecondScheme::$COLORS,
  ...
];

Or even at runtime:

$schemes = [
  'attr_value1' => 'FirstScheme',
  'attr_value2' => 'SecondScheme',
  ...
];

And then:

$reflector = new ReflectionClass($schemes['attr_value1']);
$schema = $reflector->getStaticPropertyValue('COLORS');

But this seems not any maintainable at all, and you would like to store such informations in a proper data layer, without hardcoding them as static fields of a class [which is not their purpose].

moonwave99
  • 21,957
  • 3
  • 43
  • 64