Skip to content

Commit

Permalink
[php][step_shotgun_surgery-01_base] Add Shotgun Surgery example
Browse files Browse the repository at this point in the history
  • Loading branch information
santakadev committed Apr 8, 2021
1 parent d5f2b62 commit 241ea85
Show file tree
Hide file tree
Showing 14 changed files with 321 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Application;

use CodelyTv\StepShotgunSurgery\Domain\StepDurationCalculatorFactory;
use CodelyTv\StepShotgunSurgery\Domain\StepRepository;

class GetStepDuration
{
private StepRepository $steps;

public function __construct(StepRepository $steps)
{
$this->steps = $steps;
}

public function __invoke(string $stepId): float
{
$step = $this->steps->find($stepId);
return StepDurationCalculatorFactory::build()->calculate($step);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

class DurationMultiplier
{
public static function multiplierFor(Step $step): float
{
if ($step->type() === StepEnums::STEP_TYPE_VIDEO) {
return StepEnums::STEP_DURATION_MULTIPLIER_VIDEO;
}

if ($step->type() === StepEnums::STEP_TYPE_QUIZ) {
return StepEnums::STEP_DURATION_MULTIPLIER_QUIZ;
}

return 1.0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

class Question
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

final class QuizStep extends Step
{
private array $questions;

public function __construct(string $id, Question ...$questions)
{
parent::__construct($id);
$this->questions = $questions;
}

public function type(): string
{
return StepEnums::STEP_TYPE_QUIZ;
}

public function questionCount(): int
{
return count($this->questions);
}
}
22 changes: 22 additions & 0 deletions examples/php/php-step_shotgun_surgery-01_base/src/Domain/Step.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

abstract class Step
{
private string $id;

public function __construct(string $id)
{
$this->id = $id;
}

public function id(): string
{
return $this->id;
}

abstract public function type(): string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

interface StepDurationCalculator
{
public function supports(Step $step): bool;

public function calculate(Step $step): float;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

use RuntimeException;

class StepDurationCalculatorChain implements StepDurationCalculator
{
/** @var StepDurationCalculator[] */
private array $calculators;

public function __construct(StepDurationCalculator ...$calculators)
{
$this->calculators = $calculators;
}

public function supports(Step $step): bool
{
return in_array($step->type(), StepEnums::STEP_TYPES);
}

public function calculate(Step $step): float
{
if (!$this->supports($step)) {
throw new RuntimeException("Missing calculator for step type {$step->type()}");
}

foreach ($this->calculators as $calculator) {
if ($calculator->supports($step)) {
return $calculator->calculate($step);
}
}

throw new RuntimeException("Missing calculator for step type {$step->type()}");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

class StepDurationCalculatorFactory
{
public static function build(): StepDurationCalculator
{
// Remember to add the calculator!!
return new StepDurationCalculatorChain(
new StepDurationCalculatorVideo(),
new StepDurationCalculatorQuiz(),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

class StepDurationCalculatorQuiz implements StepDurationCalculator
{
public function supports(Step $step): bool
{
return $step instanceof QuizStep;
}

public function calculate(Step $step): float
{
return $step->questionCount() * StepEnums::QUIZ_QUESTION_DURATION * DurationMultiplier::multiplierFor($step);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

class StepDurationCalculatorVideo implements StepDurationCalculator
{
public function supports(Step $step): bool
{
return $step instanceof VideoStep;
}

public function calculate(Step $step): float
{
return $step->videoDuration() * DurationMultiplier::multiplierFor($step);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

final class StepEnums
{
public const STEP_TYPE_VIDEO = 'video';
public const STEP_TYPE_QUIZ = 'quiz';

public const STEP_DURATION_MULTIPLIER_VIDEO = 1.1;
public const STEP_DURATION_MULTIPLIER_QUIZ = 1.5;

public const QUIZ_QUESTION_DURATION = 5;

# Important: don't forget to add here the type!!
public const STEP_TYPES = [
self::STEP_TYPE_VIDEO,
self::STEP_TYPE_QUIZ
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

interface StepRepository
{
public function find(string $id): Step;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace CodelyTv\StepShotgunSurgery\Domain;

final class VideoStep extends Step
{
private int $videoDuration;

public function __construct(string $id, int $videoDuration)
{
parent::__construct($id);
$this->videoDuration = $videoDuration;
}

public function type(): string
{
return StepEnums::STEP_TYPE_VIDEO;
}

public function videoDuration(): int
{
return $this->videoDuration;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types = 1);

namespace CodelyTv\StepShotgunSurgery\Tests\Application;

use CodelyTv\StepShotgunSurgery\Application\GetStepDuration;
use CodelyTv\StepShotgunSurgery\Domain\Question;
use CodelyTv\StepShotgunSurgery\Domain\QuizStep;
use CodelyTv\StepShotgunSurgery\Domain\Step;
use CodelyTv\StepShotgunSurgery\Domain\StepRepository;
use CodelyTv\StepShotgunSurgery\Domain\VideoStep;
use PHPUnit\Framework\TestCase;

final class GetStepDurationTest extends TestCase
{
private StepRepository $stepRepository;
private GetStepDuration $getStepDuration;

protected function setUp(): void
{
$this->stepRepository = $this->createMock(StepRepository::class);
$this->getStepDuration = new GetStepDuration($this->stepRepository);
}

/** @test */
public function shouldReturnVideoStepDuration(): void
{
$videoStep = new VideoStep('videoId', 10);
$this->givenStepRepositoryHas($videoStep);

$duration = ($this->getStepDuration)('videoId');

self::assertSame(11.0, $duration);
}

/** @test */
public function shouldReturnQuizStepDuration(): void
{
$questions = [
new Question(),
new Question(),
];
$quizStep = new QuizStep('videoId', ...$questions);
$this->givenStepRepositoryHas($quizStep);

$duration = ($this->getStepDuration)('videoId');

self::assertSame(15.0, $duration);
}

public function givenStepRepositoryHas(Step $step): void
{
$this->stepRepository
->method('find')
->willReturn($step);
}
}

0 comments on commit 241ea85

Please sign in to comment.