简体   繁体   中英

Polymorphism and dependency injection - too many dependencies

So I have read that if we see a switch statement, its a sign that it needs polymorphism.

I saw such polymorphism example:

include 'vendor/autoload.php';

$calc = new Calculator();

$calc->setOperands(5, 6);
$calc->setOperation(new Addition);
echo $result = $calc->calculate();

And of course there can be various classes as Subtract, Multiply, Root, etc.

Now lets say I want to use this code in a Laravel framework, but this I think should apply to any php framework.

Example of addition class ( same way could work any other calculator function)

Class Addition implements Operation {
    public function run($num, $current){
        return $current + $num;
    }

}

Calculator:

Class Calculator {

    protected $result = 0;
    protected $operands = array();
    protected $operation;

    public function getResult()
    {
        return $this->result;
    }

    public function setOperands()
    {
        $this->operands = func_get_args();
    }

    public function setOperation(Operation $operation)
    {
        $this->operation = $operation;
    }

    public function calculate()
    {
        foreach ($this->operands as $num) {
            echo $num;
            if ( ! is_numeric($num)) {
                throw new InvalidArgumentException;
            }
            $this->result = $this->operation->run($num, $this->result);
        }

        return $this->result;
    }




}

I write a class like this:

class CalcUser
{
  private $calc; 

  public function __construct(Calculator $calc)
  {
    $this->calc = $calc;
  }

  public function index()
  {
    $this->calc->setOperands(5, 6);
    $this->calc->setOperation(new Addition);
    $result = $calc->calculate();

    // imagine we have long formula to calculate so we add here many functions
    // (5+6) * 12 + 21  / 6 + sqrt(4) ...
    $this->calc->setOperands($result, 6);
    $this->calc->setOperation(new AnyOtherFunction);
    echo $result = $calc->calculate();

  }

}

And this should work, did not test my CalcUser class, just wrote directly here.

But I see one problem - there is used keyword new

And also what I have read is this:

Signs of Untestable Code:

  1. New Operators

The only time when it's acceptable to instantiate a class inside of another class is when that object is what we refer to as a value-object, or a simple container with getters and setters that doesn't do any real work.

Ok, now I could add the Addition class, and other classes to constructor as parameter like I did with calculator class and new operator will be avoided.

But then there is another thing:

Too Many Dependencies

If you find that a particular class requires four or more dependencies, this is, more often than not, a tell-tale sign that your class is asking for too much

And so its easily to get more than 3 dependencies that way with calculator alone having many different functions.

So how should I reformat code to avoid having too many dependencies?

There are a bunch of different views on this matter (when does a class depend on something), but class dependencies are typically seen as what's passed into the constructor, ie what's needed for the class to be instantiated as an object. Therefore, when you have an instance of your calculator, calling a method and passing another object - in this case an implementation of Operation - into a method is not a direct dependency.

So basically your Calculator class has no dependencies, your CalcUser has one dependency ( Calculator ).

Also, I think the thing about the new -keyword/construct is that a class should not call something on itself, passing a method dependency through there. That could be a sign that the newly instantiated object is redundant if it's only used in that class' ecosystem. Say you never use Operation anywhere else beside in the class that depends on it, ie in this case:

class Calculator
{

    ...

    public function newSumOperation()
    {
        $this->setOperands(func_get_args());
        $this->setOperation(new Addition);
    }

   ...
}

class CalcUser
{
    ... 
    public function index()
    {
        $this->calc->newSumOperation(1,2,3);
        $result = $this->calc->calculate();

        // imagine we have long formula to calculate so we add here many functions
        // (5+6) * 12 + 21  / 6 + sqrt(4) ...
        $this->newXXXXXOperation(4,3,2);
        echo $result = $calc->calculate();

    }

}

So, as you see, in the above example you never use Addition outside the Calculator -class. If you think of a real calculator, you put numbers in and get the result out; and in between that, the calculator doesn't push the logic of adding numbers together over to something else, because it's the calculators job to do an addition.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM