Answer the question
In order to leave comments, you need to log in
What SOLID principles are violated here?
I decided to delve into SOLID, and in particular to make a composition of interfaces, to solve the following problem.
There is a Box in which shapes are placed, some of these shapes implement the IShape interface , that is, they can be filled with color, the second part implements only IArea and cannot be filled with color (line, transparent, etc.). It is necessary to calculate the area of all figures filled with a certain color in the box.
class Box {
/**@var IShape[] $shapes */
private array $shapes = [];
public function addShape(IArea $shape): self {
$this->shapes[] = $shape;
return $this;
}
public function getTotalAreaWithColor(int $color): float {
$total = 0;
foreach ($this->shapes as $shape) {
if ($shape instanceof IFillColor && $shape->getFillColor() === $color) {
$total += $shape->getArea();
}
}
return $total;
}
public function getTotalArea(): float {
$total = 0;
foreach ($this->shapes as $shape) {
$total += $shape->getArea();
}
return $total;
}
}
interface IArea {
public function getArea(): float;
}
interface IFillColor {
public function setFillColor(string $rgb): self;
public function getFillColor(): int;
}
interface IShape extends IArea, IFillColor {
}
trait TFillColor {
protected int $fillColor = 0;
public function setFillColor(string $rgb): self {
$this->fillColor = (int)hexdec($rgb);
return $this;
}
public function getFillColor(): int {
return $this->fillColor;
}
}
Answer the question
In order to leave comments, you need to log in
O + I immediately catches the eye, I would say
imagine that after that you will need to calculate the area of \u200b\u200bonly small figures (area < 3) or an interface with a texture will be added and only fluffy ones will need to be calculated.
no, formally the O principle has not yet been violated, and the Box class can be extended by adding getTotalAreaSmall and getTotalAreaFluffy in the descendants. but Box already now looks not just like Box (which just stores shapes), but like BoxThatCountsColored, and its descendants will be called BoxThatCountsColoredAndSmallAndFluffy for example :)
From my point of view, the S (Single Responsibility Principle) is violated here in the first place. Box
in general, should not be able to do anything other than store shapes. As vgbege correctly noted , it does not follow from the task that in the future there will be no more options for calculating some metrics for figures with Box, so first of all I would take out a separate calculator. Also, you don't have a generic interface for IArea
and IShape
because of which there is confusion in addShape
.
In general, I got something like this:
/**
* Основной интерфейс для всех фигур которые можно хранить в Box'е
*/
interface IFigure
{
}
/**
* Интерфейс для фигур которые могут обладать площадью
*/
interface IArea extends IFigure
{
public function getArea(): float;
}
/**
* Интерфейс для фигур которые могут обладать цветом заливки
*/
interface IFillColor
{
public function setFillColor(string $color): self;
public function getFillColor(): string;
}
/**
* Обобщённый интерфейс для фигур, обладающих и цветом и площадью
* По факту, думаю, он не имеет смысла, но нужен по условию задачи
*/
interface IShape extends IArea, IFillColor
{
}
/**
* Коробка для хранения фигур
*/
class Box
{
/**
* @var IFigure[]
*/
private $figures = [];
/**
* @param IFigure[] $figures
*/
public function __construct(array $figures = [])
{
// Добавляем полученные через конструктор фигуры в объект
// Используем addFigure() чтобы проконтролировать корректность типов
array_walk($figures, [$this, 'addFigure']);
}
/**
* @param IFigure $figure
* @return $this
*/
public function addFigure(IFigure $figure): self
{
$this->figures[] = $figure;
return $this;
}
/**
* @param IFigure $figure
* @return bool
*/
public function hasFigure(IFigure $figure): bool
{
return in_array($figure, $this->figures, true);
}
/**
* @param IFigure $figure
* @return $this
*/
public function removeFigure(IFigure $figure): self
{
$this->figures = array_filter($this->figures, static function (IFigure $f) use ($figure) {
return $f !== $figure;
});
return $this;
}
/**
* @return IFigure[]
*/
public function getFigures(): array
{
return $this->figures;
}
}
/**
* Интерфейс для калькуляторов фигур в Box'ах
*/
interface IFigureCalculator
{
/**
* @param Box $box
* @param mixed ...$args
* @return mixed
*/
public function calculate(Box $box, ...$args);
}
/**
* Пример калькулятора
*/
class AreaCalculator implements IFigureCalculator
{
public function calculate(Box $box, ...$args): float
{
// Получаем список фигур которые обладают площадью
$figures = array_filter($box->getFigures(), static function (IFigure $figure) {
return $figure instanceof IArea;
});
// Получаем цвет из дополнительных аргументов калькулятора
$color = array_shift($args);
if ($color) {
// У нас задан цвет, фильтруем фигуры по цвету
$figures = array_filter($figures, static function (IFigure $figure) use ($color) {
return $figure instanceof IFillColor && $figure->getFillColor() === $color;
});
}
// Подсчитываем суммарную площадь
return array_reduce($figures, static function (float $area, IArea $figure) {
return $area + $figure->getArea();
}, 0);
}
}
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question