My objective is to create class that can be used both static and non-static way. Both ways have to use the same methods, but in different way
Non-static way:
$color = new Color("#fff");
$darkenColor = $color->darken(0.1);
Static way:
$darkenColor = Color::darken("#fff", 0.1);
So in this example method darken
can be used both on existing object and as static method of Color
class. But depending on how it's used, it uses different parameters.
How should such class be designed? What is good pattern for creating such types of classes?
Class will have a lot of different methods so it should avoid massive checking code in beginning of each method.
PHP doesn't really support method overloading, so it's not all that simple to realize, but there are ways.
Why provide static and non-static?
What I would ask myself first though is if it is really needed to offer both static and non-static approaches. It seems overly complicated, possibly confusing to the users of your color class, and doesn't seem to add all that many benefits. I would just go with the non-static approach and be done with it.
Static Factory Class
What you basically want are static factory methods, so you could create an extra class that realizes this:
class Color {
private $color;
public function __construct($color)
{
$this->color = $color;
}
public function darken($by)
{
// $this->color = [darkened color];
return $this;
}
}
class ColorFactory {
public static function darken($color, $by)
{
$color = new Color($color);
return $color->darken($by);
}
}
An alternative would be to put the static method inside Color
and give it a different name, eg createDarken
(this should be the same each time, so all static factory methods would be called createX
for user convenience).
callStatic
Another possibility is to use the magic methods __call
and __callStatic
. The code should look something like this:
class Color {
private $color;
public function __construct($color)
{
$this->color = $color;
}
// note the private modifier, and the changed function name.
// If we want to use __call and __callStatic, we can not have a function of the name we are calling in the class.
private function darkenPrivate($by)
{
// $this->color = [darkened color];
return $this;
}
public function __call($name, $arguments)
{
$functionName = $name . 'Private';
// TODO check if $functionName exists, otherwise we will get a loop
return call_user_func_array(
array($this, $functionName),
$arguments
);
}
public static function __callStatic($name, $arguments)
{
$functionName = $name . 'Private';
$color = new Color($arguments[0]);
$arguments = array_shift($arguments);
// TODO check if $functionName exists, otherwise we will get a loop
call_user_func_array(
array($color, $functionName),
$arguments
);
return $color;
}
}
Note though that this is somewhat messy. Personally, I would not use this approach, because it's not that nice for the users of your class (you can't even have proper PHPDocs). It is the simplest for you as programmer though, because you do not need to add a lot of extra code when adding new functions.
According to your code examples and your comments, I understand that your method ->darken()
will modify a property in the $color
object, based on the current value of that property. On the other hand, the static ::darken()
method will return the value of the darkened color...
If I understood it correctly, you may do something like:
class Color {
protected $value;
public static function darkenColor($value, $coef) {
$darkened = //Darken $value using $coef somehow...
return $darkened;
}
public function darken($coef) {
$darkened = Color::darkenColor($this->value, $coef);
$this->value = $darkened;
return $darkened;
}
}
With this approach, you don't need to repeat the code of darkening the color, and you offer both the dynamic and static methods from the class.
The static method "does the job", and can be called independently, and the dynamic method just uses the static one to make the calculations and assign the result to the object property.
Note that you have to use different names for the methods as PHP does not support overloading methods...
That said, personally I'd move the static methods to a different class(es), e.g., ColorManager
or ColorCalculator
, or even better, if the code to darken a color is complex enough and you'll have more operations like that, I'd create a ColorDarkener
class with just that method...
class Color {
protected $value;
public function darken($coef) {
$darkened = ColorDarkener::darken($this->value, $coef);
$this->value = $darkened;
return $darkened;
}
}
class ColorDarkener {
public static function darken($value, $coef) {
$darkened = //Darken $value using $coef somehow...
return $darkened;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With