Skip to content

Commit

Permalink
Decoupled calls among parent and heir classes.
Browse files Browse the repository at this point in the history
Moved declaration of common interfaces to the acme/contracts package.
  • Loading branch information
Grigori Kochanov committed Dec 7, 2020
1 parent bd0630e commit 33908a4
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 27 deletions.
5 changes: 3 additions & 2 deletions composer.json
Expand Up @@ -5,7 +5,8 @@
"php": "^8.0", "php": "^8.0",
"symfony/dependency-injection": "^5.2", "symfony/dependency-injection": "^5.2",
"symfony/config": "^5.2", "symfony/config": "^5.2",
"acme/image": "^0.1" "acme/image": "^0.1",
"acme/contracts": "0.1"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
Expand All @@ -18,7 +19,7 @@
"repositories": [ "repositories": [
{ {
"type": "path", "type": "path",
"url": "./packages/acme-image" "url": "./packages/*"
} }
], "license": "MIT", ], "license": "MIT",
"authors": [ "authors": [
Expand Down
17 changes: 17 additions & 0 deletions packages/acme-contracts/composer.json
@@ -0,0 +1,17 @@
{
"name": "acme/contracts",
"description": "a package for contracts between application and acme/image",
"license": "MIT",
"version": "0.1",
"authors": [
{
"name": "Grigori Kochanov",
"email": "public@grik.net"
}
],
"autoload": {
"psr-4": {
"Acme\\Contracts\\": "src/"
}
}
}
11 changes: 11 additions & 0 deletions packages/acme-contracts/src/BaseModelInterface.php
@@ -0,0 +1,11 @@
<?php


namespace Acme\Contracts;


interface BaseModelInterface
{
public function foo();
public function callBar();
}
10 changes: 10 additions & 0 deletions packages/acme-contracts/src/FooImplementationInterface.php
@@ -0,0 +1,10 @@
<?php

namespace Acme\Contracts;

interface FooImplementationInterface
{
public function withBaseModel(BaseModelInterface $baseModel): void;

public function doFoo(): mixed;
}
@@ -1,13 +1,13 @@
<?php <?php




namespace Acme\Image\Contracts; namespace Acme\Contracts;


/** /**
* Interface ImageInterface * Interface ImageInterface
* @package Acme\Image * @package Acme\Image
*/ */
interface ImageInterface interface ImageInterface
{ {
public function load(string $tmp_name): mixed; public function load(string $id): mixed;
} }
32 changes: 23 additions & 9 deletions packages/acme-image/src/ImageService.php
Expand Up @@ -3,8 +3,13 @@


namespace Acme\Image; namespace Acme\Image;


use Acme\Image\Contracts\ImageInterface; use Acme\Contracts\{
FooImplementationInterface,
BaseModelInterface,
ImageInterface
};
use Acme\Image\lib\Image; use Acme\Image\lib\Image;
use Symfony\Contracts\Service\Attribute\Required;


/** /**
* Class ImageService provides an API for the application. * Class ImageService provides an API for the application.
Expand All @@ -14,24 +19,33 @@
* @package Acme\Image * @package Acme\Image
* @api * @api
*/ */
class ImageService implements ImageInterface final class ImageService implements ImageInterface, FooImplementationInterface
{ {


/** /**
* @var Image * @var Image
*/ */
private Image $imageLib; private Image $imageLib;


public function __construct() /**
* @param string $id
* @return mixed
*/
public function load(string $id): mixed
{ {
$this->imageLib = new Image(); return $this->imageLib->load($id);
} }


/** #[Required]
* @param string $tmp_name public function withBaseModel(BaseModelInterface $baseModel): void
*/
public function load(string $tmp_name): mixed
{ {
return $this->imageLib->load($tmp_name); $this->imageLib = new Image($baseModel);
$baseModel->init();
} }

public function doFoo(): mixed
{
return $this->imageLib->foo();
}

} }
18 changes: 11 additions & 7 deletions packages/acme-image/src/lib/Image.php
Expand Up @@ -2,25 +2,29 @@


namespace Acme\Image\lib; namespace Acme\Image\lib;


use Acme\Contracts\BaseModelInterface;

/** /**
* This class is tightly coupled with \BaseModel * This class is the code is not coupled with a \BaseModel class anymore
* *
* @internal * @internal
* @package Acme\Image * @package Acme\Image
*/ */
class Image extends \BaseModel class Image
{ {
public function __construct(private BaseModelInterface $baseModel)
{
}


/** /**
* called from the \BaseModel * called from the \BaseModel through adapters
*/ */
protected function foo() public function foo()
{ {
//an inherited final method $this->baseModel->callBar();
$this->bar();
} }


public function load($tmp_name) public function load($id)
{ {
} }
} }
18 changes: 15 additions & 3 deletions services.php
Expand Up @@ -2,16 +2,28 @@


namespace Symfony\Component\DependencyInjection\Loader\Configurator; namespace Symfony\Component\DependencyInjection\Loader\Configurator;


use Acme\Image\{Contracts\ImageInterface,ImageService}; use Acme\BaseModelAdapter;
use Acme\ImageResizeController;
use Acme\Contracts\{
FooImplementationInterface,
BaseModelInterface,
ImageInterface
};
use Acme\Image\ImageService;

use Symfony\Component\DependencyInjection\Reference;


return function(ContainerConfigurator $configurator) { return function(ContainerConfigurator $configurator) {
/** /**
* @see https://symfony.com/doc/current/service_container/autowiring.html * @see https://symfony.com/doc/current/service_container/autowiring.html
*/ */
$services = $configurator->services()->defaults()->autowire()->public(); $services = $configurator->services()->defaults()->autowire()->public();


$services->load('Acme\\', './src/*') $services->set(ImageResizeController::class);
->exclude('./src/BaseModel.php');


$services->set(ImageInterface::class, ImageService::class); $services->set(ImageInterface::class, ImageService::class);

$services->set(BaseModelInterface::class, BaseModelAdapter::class)
->bind(FooImplementationInterface::class, service(ImageInterface::class))
;
}; };
49 changes: 49 additions & 0 deletions src/BaseModelAdapter.php
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);

namespace Acme;

use Acme\Contracts\{BaseModelInterface,FooImplementationInterface};

/**
* An adapter service for BaseModel to decouple inheritance into composition
*
* @package Acme
*/
final class BaseModelAdapter extends \BaseModel implements BaseModelInterface
{
public function __construct(private FooImplementationInterface $fooInstance)
{
}

public function init():void
{
parent::__construct();
}

public function getX(): int
{
return $this->x;
}

/**
* Required by the interface of the BaseModel abstract class
*/
public function foo()
{
return $this->fooInstance->doFoo($this);
}

/**
* An adapter for a final protected method bar()
*/
public function callBar()
{
//print call trace after decoupling
foreach (debug_backtrace() as ['function'=>$f,'class'=>$c]) {
echo "$c::$f()\n";
}

return $this->bar();
}
}
7 changes: 3 additions & 4 deletions src/ImageResizeController.php
Expand Up @@ -4,13 +4,12 @@
namespace Acme; namespace Acme;




use Acme\Image\Contracts\ImageInterface; use Acme\Contracts\ImageInterface;


class ImageResizeController class ImageResizeController
{ {
public function __construct( public function __construct(public ImageInterface $image)
public ImageInterface $image {
){
} }


public function action(?string $id) public function action(?string $id)
Expand Down

0 comments on commit 33908a4

Please sign in to comment.