SlideShare a Scribd company logo
1 of 136
Download to read offline
Symfony Components


and Design Patterns
Design patterns?
Why should you care?
Communication
Communication


Reduced amount of code in one chunk
Communication


Reduced amount of code in one chunk


Increased cohesion and reduced coupling
Communication


Reduced amount of code in one chunk


Increased cohesion and reduced coupling


Extendibility
Communication


Reduced amount of code in one chunk


Increased cohesion and reduced coupling


Extendibility


SOLID
Increase code quality
Can we measure it?
Avg. Logic Lines of Code
Avg. Logic Lines of Code LLoC
Avg. Logic Lines of Code LLoC
Amount of classes
Avg. Logic Lines of Code LLoC
Amount of classes AoC
Avg. Logic Lines of Code LLoC
Amount of classes AoC
Avg. Cyclomatic Complexity
Avg. Logic Lines of Code LLoC
Amount of classes AoC
Avg. Cyclomatic Complexity ACC
Let’s see the code
Sample Project
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct(
$options[‘product'],
$options[‘type'],
$options[‘quantity'],
$options[‘price']
);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
Avg. Logic Lines of Code 40
Amount of classes 1
Avg. Cyclomatic Complexity 8
LLoC AoC ACC
Sample project 40 1 8
Single responsibility principle


Open close principle


Liskov substitution principle


Interface segregation principle


Dependency inversion principle
Single responsibility principle


Open close principle


Liskov substitution principle


Interface segregation principle


Dependency inversion principle
Composition
Preparation
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
private function applyTaxation(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
private function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
private function applyTaxation(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
/**
* @param Cart $cart
*/
private function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
Extraction
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
private function applyTaxation(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
}
LLoC AoC ACC
Sample project 40 1 8
Extracted processor 61 2 4.5
final class CompositeOrderProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
private function applyTaxation(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
private function applyTaxation(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
}
final class OrderTaxationProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
}
final class OrderPromotionProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private OrderPromotionProcessor $orderPromotionProcessor;
private OrderTaxationProcessor $orderTaxationProcessor;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderPromotionProcessor = $orderPromotionProcessor;
$this->orderTaxationProcessor = $orderTaxationProcessor;
}
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->orderPromotionProcessor->processOrder($cart);
// Apply taxation
$this->orderTaxationProcessor->processOrder($cart);
}
}
LLoC AoC ACC
Sample project 40 1 8
Extracted processor 61 2 4.5


Separated processors 76 4 2.75
final class CompositeOrderProcessor implements OrderProcessor
{
private OrderPromotionProcessor $orderPromotionProcessor;
private OrderTaxationProcessor $orderTaxationProcessor;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderPromotionProcessor = $orderPromotionProcessor;
$this->orderTaxationProcessor = $orderTaxationProcessor;
}
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->orderPromotionProcessor->processOrder($cart);
// Apply taxation
$this->orderTaxationProcessor->processOrder($cart);
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderProcessors = [
$orderPromotionProcessor,
$orderTaxationProcessor
];
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderProcessors = [
$orderPromotionProcessor,
$orderTaxationProcessor
];
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderProcessors = [
$orderPromotionProcessor,
$orderTaxationProcessor
];
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderProcessors = [
$orderPromotionProcessor,
$orderTaxationProcessor
];
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(iterable $orderProcessors)
{
$this->orderProcessors = $orderProcessors;
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(iterable $orderProcessors)
{
$this->orderProcessors = $orderProcessors;
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
SymfonyDesignPatternsProcessorOrderProcessor:
class: 'SymfonyDesignPatternsProcessorCompositeOrderProcessor'
arguments:
- [
'@SymfonyDesignPatternsProcessorOrderPromotionProcessor',
'@SymfonyDesignPatternsProcessorOrderTaxationProcessor'
]
Can Symfony help us with it?
SymfonyDesignPatternsProcessorOrderProcessor:
class: 'SymfonyDesignPatternsProcessorCompositeOrderProcessor'
arguments:
- !tagged_iterator app.order_processor
SymfonyDesignPatternsProcessorOrderProcessor:
class: 'SymfonyDesignPatternsProcessorCompositeOrderProcessor'
arguments:
- !tagged_iterator app.order_processor
SymfonyDesignPatternsProcessorOrderPromotionProcessor:
tags:
- app.order_processor
SymfonyDesignPatternsProcessorOrderTaxationProcessor:
tags:
- app.order_processor
LLoC AoC ACC
Sample project 40 1 8
Extracted processor 61 2 4.5


Separated processors 76 4 2.75
Composite iteration 73 4 3
Single responsibility principle


Open close principle


Liskov substitution principle


Interface segregation principle


Dependency inversion principle
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy
final class OrderTaxationProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
}
final class OrderTaxationProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
}
final class OrderTaxationProcessor implements OrderProcessor
{
private TaxationStrategyDelegator $taxationStrategy;
public function __construct(TaxationStrategyDelegator $taxationStrategy)
{
$this->taxationStrategy = $taxationStrategy;
}
public function processOrder(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
$this->taxationStrategy->processTaxation($item);
}
}
}
final class OrderTaxationProcessor implements OrderProcessor
{
private TaxationStrategyDelegator $taxationStrategy;
public function __construct(TaxationStrategyDelegator $taxationStrategy)
{
$this->taxationStrategy = $taxationStrategy;
}
public function processOrder(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
$this->taxationStrategy->processTaxation($item);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ShirtTaxationStrategy $shirtTaxationStrategy;
private BookTaxationStrategy $bookTaxationStrategy;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->shirtTaxationStrategy = $shirtTaxationStrategy;
$this->bookTaxationStrategy = $bookTaxationStrategy;
}
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$this->shirtTaxationStrategy->processTaxation($item);
}
if ('book' === $item->getType()) {
$this->bookTaxationStrategy->processTaxation($item);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ShirtTaxationStrategy $shirtTaxationStrategy;
private BookTaxationStrategy $bookTaxationStrategy;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->shirtTaxationStrategy = $shirtTaxationStrategy;
$this->bookTaxationStrategy = $bookTaxationStrategy;
}
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$this->shirtTaxationStrategy->processTaxation($item);
}
if ('book' === $item->getType()) {
$this->bookTaxationStrategy->processTaxation($item);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ShirtTaxationStrategy $shirtTaxationStrategy;
private BookTaxationStrategy $bookTaxationStrategy;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->shirtTaxationStrategy = $shirtTaxationStrategy;
$this->bookTaxationStrategy = $bookTaxationStrategy;
}
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$this->shirtTaxationStrategy->processTaxation($item);
}
if ('book' === $item->getType()) {
$this->bookTaxationStrategy->processTaxation($item);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ShirtTaxationStrategy $shirtTaxationStrategy;
private BookTaxationStrategy $bookTaxationStrategy;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->shirtTaxationStrategy = $shirtTaxationStrategy;
$this->bookTaxationStrategy = $bookTaxationStrategy;
}
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$this->shirtTaxationStrategy->processTaxation($item);
}
if ('book' === $item->getType()) {
$this->bookTaxationStrategy->processTaxation($item);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private array $indexedTaxationStrategies;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->indexedTaxationStrategies = [
'shirt' => $shirtTaxationStrategy,
'book' => $bookTaxationStrategy,
];
}
public function processTaxation(CartItem $item): void
{
$this->indexedTaxationStrategies[$item->getType()]->processTaxation($item);
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private array $indexedTaxationStrategies;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->indexedTaxationStrategies = [
'shirt' => $shirtTaxationStrategy,
'book' => $bookTaxationStrategy,
];
}
public function processTaxation(CartItem $item): void
{
$this->indexedTaxationStrategies[$item->getType()]->processTaxation($item);
}
}
Can Symfony help us with it?
final class TaxationStrategyDelegator implements TaxationStrategy
{
private array $indexedTaxationStrategies;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->indexedTaxationStrategies = [
'shirt' => $shirtTaxationStrategy,
'book' => $bookTaxationStrategy,
];
}
public function processTaxation(CartItem $item): void
{
$this->indexedTaxationStrategies[$item->getType()]->processTaxation($item);
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private array $indexedTaxationStrategies;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->indexedTaxationStrategies = [
'shirt' => $shirtTaxationStrategy,
'book' => $bookTaxationStrategy,
];
}
public function processTaxation(CartItem $item): void
{
$this->indexedTaxationStrategies[$item->getType()]->processTaxation($item);
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ServiceLocator $taxationStrategies;
public function __construct(ServiceLocator $taxationStrategies)
{
$this->taxationStrategies = $taxationStrategies;
}
public function processTaxation(CartItem $item): void
{
$this->taxationStrategies->get($item->getType())->processTaxation($item);
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ServiceLocator $taxationStrategies;
public function __construct(ServiceLocator $taxationStrategies)
{
$this->taxationStrategies = $taxationStrategies;
}
public function processTaxation(CartItem $item): void
{
$this->taxationStrategies->get($item->getType())->processTaxation($item);
}
}
SymfonyDesignPatternsTaxationStrategiesTaxationStrategy:
class: 'SymfonyDesignPatternsTaxationStrategiesTaxationStrategyDelegator'
arguments:
- !tagged_locator { tag: 'app.taxation_strategies', index_by: 'type' }
SymfonyDesignPatternsTaxationStrategiesTaxationStrategy:
class: 'SymfonyDesignPatternsTaxationStrategiesTaxationStrategyDelegator'
arguments:
- !tagged_locator { tag: 'app.taxation_strategies', index_by: 'type' }
SymfonyDesignPatternsTaxationStrategiesBookTaxationStrategy:
tags:
- { name: 'app.taxation_strategies', type: 'book' }
SymfonyDesignPatternsTaxationStrategiesShirtTaxationStrategy:
tags:
- { name: 'app.taxation_strategies', type: 'shirt' }
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Single responsibility principle


Open close principle


Liskov substitution principle


Interface segregation principle


Dependency inversion principle
Time for small refactoring
final class CheckoutController
{
private array $repository = [];
private OrderProcessor $orderProcessor;
public function __construct(OrderProcessor $orderProcessor)
{
$this->orderProcessor = $orderProcessor;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
private OrderProcessor $orderProcessor;
public function __construct(OrderProcessor $orderProcessor)
{
$this->orderProcessor = $orderProcessor;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private OrderProcessor $orderProcessor;
private CartRepository $cartRepository;
public function __construct(OrderProcessor $orderProcessor, CartRepository $cartRepository)
{
$this->orderProcessor = $orderProcessor;
$this->cartRepository = $cartRepository;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
}
final class CheckoutController
{
private OrderProcessor $orderProcessor;
private CartRepository $cartRepository;
public function __construct(OrderProcessor $orderProcessor, CartRepository $cartRepository)
{
$this->orderProcessor = $orderProcessor;
$this->cartRepository = $cartRepository;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
}
final class InMemoryCartRepository implements CartRepository
{
private array $repository = [];
public function getCart(string $cart_id): Cart
{
if (isset($this->repository[$cart_id])) {
/** @var Cart $cart */
$cart = $this->repository[$cart_id];
} else {
$cart = new Cart($cart_id);
}
return $cart;
}
}
public function getCart(string $cart_id): Cart
{
if (!isset($this->repository[$cart_id])) {
$this->repository[$cart_id] = new Cart($cart_id);
}
/** @var Cart $cart */
return $this->repository[$cart_id];
}
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command
final class CheckoutController
{
private array $repository = [];
private OrderProcessor $orderProcessor;
public function __construct(OrderProcessor $orderProcessor)
{
$this->orderProcessor = $orderProcessor;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options[‘cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
private OrderProcessor $orderProcessor;
public function __construct(OrderProcessor $orderProcessor)
{
$this->orderProcessor = $orderProcessor;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options[‘cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$this->messageBus->dispatch(new AddToCart(
$options['cart_id'],
$options['product'],
$options['type'],
$options['quantity'],
$options['price']
));
} elseif ('remove_from_cart' === $operation) {
$this->messageBus->dispatch(new RemoveFromCart(
$options['cart_id'],
$options['product'],
));
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$this->messageBus->dispatch(new AddToCart(
$options['cart_id'],
$options['product'],
$options['type'],
$options['quantity'],
$options['price']
));
} elseif ('remove_from_cart' === $operation) {
$this->messageBus->dispatch(new RemoveFromCart(
$options['cart_id'],
$options['product'],
));
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
final class AddToCart
{
private string $cartId;
private string $product;
private string $type;
private int $quantity;
private int $price;
public function __construct(...)
{
$this->cartId = $cartId;
$this->product = $product;
$this->type = $type;
$this->quantity = $quantity;
$this->price = $price;
}
}
final class AddToCartHandler implements MessageHandlerInterface
{
private CartRepository $cartRepository;
public function __construct(CartRepository $cartRepository)
{
$this->cartRepository = $cartRepository;
}
public function __invoke(AddToCart $addToCart)
{
$cart = $this->cartRepository->getCart($addToCart->getCartId());
$cart->addProduct(
$addToCart->getProduct(),
$addToCart->getType(),
$addToCart->getQuantity(),
$addToCart->getPrice()
);
$this->cartRepository->save($cart);
}
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$this->messageBus->dispatch(new AddToCart(
$options['cart_id'],
$options['product'],
$options['type'],
$options['quantity'],
$options['price']
));
} elseif ('remove_from_cart' === $operation) {
$this->messageBus->dispatch(new RemoveFromCart(
$options['cart_id'],
$options['product'],
));
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$this->messageBus->dispatch(new AddToCart(
$options['cart_id'],
$options['product'],
$options['type'],
$options['quantity'],
$options['price']
));
} elseif ('remove_from_cart' === $operation) {
$this->messageBus->dispatch(new RemoveFromCart(
$options['cart_id'],
$options['product'],
));
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
public function cartAction(CartIdAwareCommand $operation): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($operation->getCartId());
// Perform operation
$this->messageBus->dispatch($operation);
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
public function cartAction(CartIdAwareCommand $operation): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($operation->getCartId());
// Perform operation
$this->messageBus->dispatch($operation);
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator
final class AddToCartHandler implements MessageHandlerInterface
{
private CartRepository $cartRepository;
public function __construct(CartRepository $cartRepository)
{
$this->cartRepository = $cartRepository;
}
public function __invoke(AddToCart $addToCart)
{
$cart = $this->cartRepository->getCart($addToCart->getCartId());
$cart->addProduct(
$addToCart->getProduct(),
$addToCart->getType(),
$addToCart->getQuantity(),
$addToCart->getPrice()
);
$this->cartRepository->save($cart);
}
}
final class AddToCartHandler implements MessageHandlerInterface
{
private CartRepository $cartRepository;
public function __construct(CartRepository $cartRepository)
{
$this->cartRepository = $cartRepository;
}
public function __invoke(AddToCart $addToCart)
{
$cart = $this->cartRepository->getCart($addToCart->getCartId());
$cart->addProduct(
$addToCart->getProduct(),
$addToCart->getType(),
$addToCart->getQuantity(),
$addToCart->getPrice()
);
$this->cartRepository->save($cart);
}
}
final class AddToCartHandler implements MessageHandlerInterface
{
private CartRepository $cartRepository;
public function __construct(CartRepository $cartRepository)
{
$this->cartRepository = $cartRepository;
}
public function __invoke(AddToCart $addToCart): Cart
{
$cart = $this->cartRepository->getCart($addToCart->getCartId());
$cart->addProduct(
$addToCart->getProduct(),
$addToCart->getType(),
$addToCart->getQuantity(),
$addToCart->getPrice()
);
return $cart;
}
}
final class AddToCartHandler implements MessageHandlerInterface
{
private CartRepository $cartRepository;
public function __construct(CartRepository $cartRepository)
{
$this->cartRepository = $cartRepository;
}
public function __invoke(AddToCart $addToCart): Cart
{
$cart = $this->cartRepository->getCart($addToCart->getCartId());
$cart->addProduct(
$addToCart->getProduct(),
$addToCart->getType(),
$addToCart->getQuantity(),
$addToCart->getPrice()
);
return $cart;
}
}
final class TransactionDecorator
{
private MessageHandlerInterface $messageHandler;
private CartRepository $cartRepository;
public function __construct(
MessageHandlerInterface $messageHandler,
CartRepository $cartRepository
) {
$this->messageHandler = $messageHandler;
$this->cartRepository = $cartRepository;
}
public function __invoke(CartIdAwareCommand $cartIdAwareCommand)
{
// start transaction
$cart = ($this->messageHandler)($cartIdAwareCommand);
$this->cartRepository->save($cart);
}
}
Can Symfony help us with it?
SymfonyDesignPatternsDecoratorTransactionDecorator:
decorates: SymfonyDesignPatternsCommandHandlerAddToCartHandler
arguments:
- '@.inner'
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator iteration 216 13 1.31
Take offs?
Trade offs!
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator iteration 216 13 1.31
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator iteration 216 13 1.31
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator iteration 216 13 1.31
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator iteration 216 13 1.31
Increased complexity
Increased complexity


Increased testability
Increased complexity


Increased testability


Possibility for maintenance cost reduction
Symfony may help you a lot
@Sylius
Thank you!
@lukaszchrusciel

More Related Content

What's hot

Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and EffectsMartin Odersky
 
Learn flask in 90mins
Learn flask in 90minsLearn flask in 90mins
Learn flask in 90minsLarry Cai
 
Materi 3 Finite State Automata
Materi 3   Finite State AutomataMateri 3   Finite State Automata
Materi 3 Finite State Automataahmad haidaroh
 
Flask Introduction - Python Meetup
Flask Introduction - Python MeetupFlask Introduction - Python Meetup
Flask Introduction - Python MeetupAreski Belaid
 
Running Free with the Monads
Running Free with the MonadsRunning Free with the Monads
Running Free with the Monadskenbot
 
Introduction to Data Modeling with Apache Cassandra
Introduction to Data Modeling with Apache CassandraIntroduction to Data Modeling with Apache Cassandra
Introduction to Data Modeling with Apache CassandraDataStax Academy
 
2.7 normal forms cnf & problems
2.7 normal forms  cnf & problems2.7 normal forms  cnf & problems
2.7 normal forms cnf & problemsSampath Kumar S
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricksJavier Eguiluz
 
Edge Side Include Injection: Abusing Caching Servers into SSRF and Transparen...
Edge Side Include Injection: Abusing Caching Servers into SSRF and Transparen...Edge Side Include Injection: Abusing Caching Servers into SSRF and Transparen...
Edge Side Include Injection: Abusing Caching Servers into SSRF and Transparen...Priyanka Aash
 
Context free grammars
Context free grammarsContext free grammars
Context free grammarsShiraz316
 
Regular expression
Regular expressionRegular expression
Regular expressionLarry Nung
 
Collections Framework
Collections FrameworkCollections Framework
Collections FrameworkSunil OS
 
Notasi Bahasa - P 5,6,7
Notasi Bahasa - P 5,6,7 Notasi Bahasa - P 5,6,7
Notasi Bahasa - P 5,6,7 ahmad haidaroh
 
Teori bahasa dan automata2
Teori bahasa dan automata2Teori bahasa dan automata2
Teori bahasa dan automata2Nurdin Al-Azies
 

What's hot (20)

Preparing for Scala 3
Preparing for Scala 3Preparing for Scala 3
Preparing for Scala 3
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and Effects
 
Learn flask in 90mins
Learn flask in 90minsLearn flask in 90mins
Learn flask in 90mins
 
F# for C# Programmers
F# for C# ProgrammersF# for C# Programmers
F# for C# Programmers
 
Materi 3 Finite State Automata
Materi 3   Finite State AutomataMateri 3   Finite State Automata
Materi 3 Finite State Automata
 
Data struture lab
Data struture labData struture lab
Data struture lab
 
Flask Introduction - Python Meetup
Flask Introduction - Python MeetupFlask Introduction - Python Meetup
Flask Introduction - Python Meetup
 
Running Free with the Monads
Running Free with the MonadsRunning Free with the Monads
Running Free with the Monads
 
L3 cfg
L3 cfgL3 cfg
L3 cfg
 
Introduction to Data Modeling with Apache Cassandra
Introduction to Data Modeling with Apache CassandraIntroduction to Data Modeling with Apache Cassandra
Introduction to Data Modeling with Apache Cassandra
 
2.7 normal forms cnf & problems
2.7 normal forms  cnf & problems2.7 normal forms  cnf & problems
2.7 normal forms cnf & problems
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Edge Side Include Injection: Abusing Caching Servers into SSRF and Transparen...
Edge Side Include Injection: Abusing Caching Servers into SSRF and Transparen...Edge Side Include Injection: Abusing Caching Servers into SSRF and Transparen...
Edge Side Include Injection: Abusing Caching Servers into SSRF and Transparen...
 
Context free grammars
Context free grammarsContext free grammars
Context free grammars
 
Regular expression
Regular expressionRegular expression
Regular expression
 
Collections Framework
Collections FrameworkCollections Framework
Collections Framework
 
Notasi Bahasa - P 5,6,7
Notasi Bahasa - P 5,6,7 Notasi Bahasa - P 5,6,7
Notasi Bahasa - P 5,6,7
 
Teori bahasa dan automata2
Teori bahasa dan automata2Teori bahasa dan automata2
Teori bahasa dan automata2
 
Converting R to PMML
Converting R to PMMLConverting R to PMML
Converting R to PMML
 
Graph-Teori-Algoritma.pdf
Graph-Teori-Algoritma.pdfGraph-Teori-Algoritma.pdf
Graph-Teori-Algoritma.pdf
 

Similar to Symfony World - Symfony components and design patterns

Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)GOG.com dev team
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介Jace Ju
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 
Chaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscoreChaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscoreNicolas Carlo
 
12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdf12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdffasttracksunglass
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsBastian Feder
 
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...Mateusz Zalewski
 
Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2Aldo Chiecchia
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgetsvelveeta_512
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutesBarang CK
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 MinutesAzim Kurt
 
Framework Project
Framework  ProjectFramework  Project
Framework ProjectMauro_Sist
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsSam Hennessy
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Fabien Potencier
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
Chaining et composition de fonctions avec lodash / underscore
Chaining et composition de fonctions avec lodash / underscoreChaining et composition de fonctions avec lodash / underscore
Chaining et composition de fonctions avec lodash / underscoreNicolas Carlo
 

Similar to Symfony World - Symfony components and design patterns (20)

Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Chaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscoreChaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscore
 
12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdf12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdf
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
 
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
 
Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgets
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
 
Framework Project
Framework  ProjectFramework  Project
Framework Project
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
Zero to SOLID
Zero to SOLIDZero to SOLID
Zero to SOLID
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
Chaining et composition de fonctions avec lodash / underscore
Chaining et composition de fonctions avec lodash / underscoreChaining et composition de fonctions avec lodash / underscore
Chaining et composition de fonctions avec lodash / underscore
 

More from Łukasz Chruściel

Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
ConFoo 2024 - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024  - Need for Speed: Removing speed bumps in API ProjectsConFoo 2024  - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024 - Need for Speed: Removing speed bumps in API ProjectsŁukasz Chruściel
 
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solutionConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solutionŁukasz Chruściel
 
SyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdfSyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdfŁukasz Chruściel
 
Need for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API ProjectsNeed for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API ProjectsŁukasz Chruściel
 
SymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdfSymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdfŁukasz Chruściel
 
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...Łukasz Chruściel
 
4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdfŁukasz Chruściel
 
4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdfŁukasz Chruściel
 
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API SyliusaBoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API SyliusaŁukasz Chruściel
 
What we've learned designing new Sylius API
What we've learned designing new Sylius APIWhat we've learned designing new Sylius API
What we've learned designing new Sylius APIŁukasz Chruściel
 
How to optimize background processes.pdf
How to optimize background processes.pdfHow to optimize background processes.pdf
How to optimize background processes.pdfŁukasz Chruściel
 
Dutch php a short tale about state machine
Dutch php   a short tale about state machineDutch php   a short tale about state machine
Dutch php a short tale about state machineŁukasz Chruściel
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machineŁukasz Chruściel
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machineŁukasz Chruściel
 
BDD in practice based on an open source project
BDD in practice based on an open source projectBDD in practice based on an open source project
BDD in practice based on an open source projectŁukasz Chruściel
 
Diversified application testing based on a Sylius project
Diversified application testing based on a Sylius projectDiversified application testing based on a Sylius project
Diversified application testing based on a Sylius projectŁukasz Chruściel
 

More from Łukasz Chruściel (18)

Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
ConFoo 2024 - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024  - Need for Speed: Removing speed bumps in API ProjectsConFoo 2024  - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024 - Need for Speed: Removing speed bumps in API Projects
 
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solutionConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
 
SyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdfSyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdf
 
Need for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API ProjectsNeed for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API Projects
 
SymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdfSymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdf
 
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
 
4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf
 
4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf
 
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API SyliusaBoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
 
What we've learned designing new Sylius API
What we've learned designing new Sylius APIWhat we've learned designing new Sylius API
What we've learned designing new Sylius API
 
How to optimize background processes.pdf
How to optimize background processes.pdfHow to optimize background processes.pdf
How to optimize background processes.pdf
 
Dutch php a short tale about state machine
Dutch php   a short tale about state machineDutch php   a short tale about state machine
Dutch php a short tale about state machine
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machine
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machine
 
BDD in practice based on an open source project
BDD in practice based on an open source projectBDD in practice based on an open source project
BDD in practice based on an open source project
 
Diversified application testing based on a Sylius project
Diversified application testing based on a Sylius projectDiversified application testing based on a Sylius project
Diversified application testing based on a Sylius project
 
Why do I love and hate php?
Why do I love and hate php?Why do I love and hate php?
Why do I love and hate php?
 

Recently uploaded

WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2
 
WSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyviewmasabamasaba
 
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension AidPhilip Schwarz
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburgmasabamasaba
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...chiefasafspells
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareJim McKeeth
 
tonesoftg
tonesoftgtonesoftg
tonesoftglanshi9
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxAnnaArtyushina1
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrandmasabamasaba
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrainmasabamasaba
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...masabamasaba
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...Jittipong Loespradit
 

Recently uploaded (20)

Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
WSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security Program
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 

Symfony World - Symfony components and design patterns

  • 2.
  • 4.
  • 5.
  • 9. Communication Reduced amount of code in one chunk Increased cohesion and reduced coupling
  • 10. Communication Reduced amount of code in one chunk Increased cohesion and reduced coupling Extendibility
  • 11. Communication Reduced amount of code in one chunk Increased cohesion and reduced coupling Extendibility SOLID
  • 14. Avg. Logic Lines of Code
  • 15. Avg. Logic Lines of Code LLoC
  • 16. Avg. Logic Lines of Code LLoC Amount of classes
  • 17. Avg. Logic Lines of Code LLoC Amount of classes AoC
  • 18. Avg. Logic Lines of Code LLoC Amount of classes AoC Avg. Cyclomatic Complexity
  • 19. Avg. Logic Lines of Code LLoC Amount of classes AoC Avg. Cyclomatic Complexity ACC
  • 22. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 23. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; }
  • 24. { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct( $options[‘product'], $options[‘type'], $options[‘quantity'], $options[‘price'] ); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository
  • 25. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 26. private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart;
  • 27. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 28. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 29. Avg. Logic Lines of Code 40 Amount of classes 1 Avg. Cyclomatic Complexity 8
  • 30. LLoC AoC ACC Sample project 40 1 8
  • 31. Single responsibility principle Open close principle Liskov substitution principle Interface segregation principle Dependency inversion principle
  • 32. Single responsibility principle Open close principle Liskov substitution principle Interface segregation principle Dependency inversion principle
  • 35. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 36. $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 37. private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 38. private function applyTaxation(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } }
  • 39. private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 40. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 41. private function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); }
  • 42. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } private function applyTaxation(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } /** * @param Cart $cart */ private function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); } }
  • 43. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 44. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 46. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 47. final class CompositeOrderProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); } private function applyTaxation(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } }
  • 48. LLoC AoC ACC Sample project 40 1 8 Extracted processor 61 2 4.5
  • 49.
  • 50. final class CompositeOrderProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); } private function applyTaxation(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } }
  • 51. final class CompositeOrderProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); } private function applyTaxation(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } }
  • 52. final class OrderTaxationProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } }
  • 53. final class CompositeOrderProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } }
  • 54. final class OrderPromotionProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } }
  • 55. final class CompositeOrderProcessor implements OrderProcessor { private OrderPromotionProcessor $orderPromotionProcessor; private OrderTaxationProcessor $orderTaxationProcessor; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderPromotionProcessor = $orderPromotionProcessor; $this->orderTaxationProcessor = $orderTaxationProcessor; } public function processOrder(Cart $cart): void { // Apply promotion $this->orderPromotionProcessor->processOrder($cart); // Apply taxation $this->orderTaxationProcessor->processOrder($cart); } }
  • 56. LLoC AoC ACC Sample project 40 1 8 Extracted processor 61 2 4.5 Separated processors 76 4 2.75
  • 57.
  • 58. final class CompositeOrderProcessor implements OrderProcessor { private OrderPromotionProcessor $orderPromotionProcessor; private OrderTaxationProcessor $orderTaxationProcessor; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderPromotionProcessor = $orderPromotionProcessor; $this->orderTaxationProcessor = $orderTaxationProcessor; } public function processOrder(Cart $cart): void { // Apply promotion $this->orderPromotionProcessor->processOrder($cart); // Apply taxation $this->orderTaxationProcessor->processOrder($cart); } }
  • 59. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderProcessors = [ $orderPromotionProcessor, $orderTaxationProcessor ]; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 60. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderProcessors = [ $orderPromotionProcessor, $orderTaxationProcessor ]; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 61. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderProcessors = [ $orderPromotionProcessor, $orderTaxationProcessor ]; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 62. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderProcessors = [ $orderPromotionProcessor, $orderTaxationProcessor ]; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 63. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct(iterable $orderProcessors) { $this->orderProcessors = $orderProcessors; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 64. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct(iterable $orderProcessors) { $this->orderProcessors = $orderProcessors; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 66. Can Symfony help us with it?
  • 70. LLoC AoC ACC Sample project 40 1 8 Extracted processor 61 2 4.5 Separated processors 76 4 2.75 Composite iteration 73 4 3
  • 71. Single responsibility principle Open close principle Liskov substitution principle Interface segregation principle Dependency inversion principle
  • 72. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3
  • 74. final class OrderTaxationProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } }
  • 75. final class OrderTaxationProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } }
  • 76. final class OrderTaxationProcessor implements OrderProcessor { private TaxationStrategyDelegator $taxationStrategy; public function __construct(TaxationStrategyDelegator $taxationStrategy) { $this->taxationStrategy = $taxationStrategy; } public function processOrder(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { $this->taxationStrategy->processTaxation($item); } } }
  • 77. final class OrderTaxationProcessor implements OrderProcessor { private TaxationStrategyDelegator $taxationStrategy; public function __construct(TaxationStrategyDelegator $taxationStrategy) { $this->taxationStrategy = $taxationStrategy; } public function processOrder(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { $this->taxationStrategy->processTaxation($item); } } }
  • 78. final class TaxationStrategyDelegator implements TaxationStrategy { public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } }
  • 79. final class TaxationStrategyDelegator implements TaxationStrategy { public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } }
  • 80. final class TaxationStrategyDelegator implements TaxationStrategy { private ShirtTaxationStrategy $shirtTaxationStrategy; private BookTaxationStrategy $bookTaxationStrategy; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->shirtTaxationStrategy = $shirtTaxationStrategy; $this->bookTaxationStrategy = $bookTaxationStrategy; } public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $this->shirtTaxationStrategy->processTaxation($item); } if ('book' === $item->getType()) { $this->bookTaxationStrategy->processTaxation($item); } } }
  • 81. final class TaxationStrategyDelegator implements TaxationStrategy { private ShirtTaxationStrategy $shirtTaxationStrategy; private BookTaxationStrategy $bookTaxationStrategy; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->shirtTaxationStrategy = $shirtTaxationStrategy; $this->bookTaxationStrategy = $bookTaxationStrategy; } public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $this->shirtTaxationStrategy->processTaxation($item); } if ('book' === $item->getType()) { $this->bookTaxationStrategy->processTaxation($item); } } }
  • 82.
  • 83. final class TaxationStrategyDelegator implements TaxationStrategy { private ShirtTaxationStrategy $shirtTaxationStrategy; private BookTaxationStrategy $bookTaxationStrategy; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->shirtTaxationStrategy = $shirtTaxationStrategy; $this->bookTaxationStrategy = $bookTaxationStrategy; } public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $this->shirtTaxationStrategy->processTaxation($item); } if ('book' === $item->getType()) { $this->bookTaxationStrategy->processTaxation($item); } } }
  • 84. final class TaxationStrategyDelegator implements TaxationStrategy { private ShirtTaxationStrategy $shirtTaxationStrategy; private BookTaxationStrategy $bookTaxationStrategy; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->shirtTaxationStrategy = $shirtTaxationStrategy; $this->bookTaxationStrategy = $bookTaxationStrategy; } public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $this->shirtTaxationStrategy->processTaxation($item); } if ('book' === $item->getType()) { $this->bookTaxationStrategy->processTaxation($item); } } }
  • 85. final class TaxationStrategyDelegator implements TaxationStrategy { private array $indexedTaxationStrategies; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->indexedTaxationStrategies = [ 'shirt' => $shirtTaxationStrategy, 'book' => $bookTaxationStrategy, ]; } public function processTaxation(CartItem $item): void { $this->indexedTaxationStrategies[$item->getType()]->processTaxation($item); } }
  • 86. final class TaxationStrategyDelegator implements TaxationStrategy { private array $indexedTaxationStrategies; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->indexedTaxationStrategies = [ 'shirt' => $shirtTaxationStrategy, 'book' => $bookTaxationStrategy, ]; } public function processTaxation(CartItem $item): void { $this->indexedTaxationStrategies[$item->getType()]->processTaxation($item); } }
  • 87. Can Symfony help us with it?
  • 88. final class TaxationStrategyDelegator implements TaxationStrategy { private array $indexedTaxationStrategies; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->indexedTaxationStrategies = [ 'shirt' => $shirtTaxationStrategy, 'book' => $bookTaxationStrategy, ]; } public function processTaxation(CartItem $item): void { $this->indexedTaxationStrategies[$item->getType()]->processTaxation($item); } }
  • 89. final class TaxationStrategyDelegator implements TaxationStrategy { private array $indexedTaxationStrategies; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->indexedTaxationStrategies = [ 'shirt' => $shirtTaxationStrategy, 'book' => $bookTaxationStrategy, ]; } public function processTaxation(CartItem $item): void { $this->indexedTaxationStrategies[$item->getType()]->processTaxation($item); } }
  • 90. final class TaxationStrategyDelegator implements TaxationStrategy { private ServiceLocator $taxationStrategies; public function __construct(ServiceLocator $taxationStrategies) { $this->taxationStrategies = $taxationStrategies; } public function processTaxation(CartItem $item): void { $this->taxationStrategies->get($item->getType())->processTaxation($item); } }
  • 91. final class TaxationStrategyDelegator implements TaxationStrategy { private ServiceLocator $taxationStrategies; public function __construct(ServiceLocator $taxationStrategies) { $this->taxationStrategies = $taxationStrategies; } public function processTaxation(CartItem $item): void { $this->taxationStrategies->get($item->getType())->processTaxation($item); } }
  • 94. SymfonyDesignPatternsTaxationStrategiesBookTaxationStrategy: tags: - { name: 'app.taxation_strategies', type: 'book' } SymfonyDesignPatternsTaxationStrategiesShirtTaxationStrategy: tags: - { name: 'app.taxation_strategies', type: 'shirt' }
  • 95. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86
  • 96. Single responsibility principle Open close principle Liskov substitution principle Interface segregation principle Dependency inversion principle
  • 97. Time for small refactoring
  • 98. final class CheckoutController { private array $repository = []; private OrderProcessor $orderProcessor; public function __construct(OrderProcessor $orderProcessor) { $this->orderProcessor = $orderProcessor; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 99. final class CheckoutController { private array $repository = []; private OrderProcessor $orderProcessor; public function __construct(OrderProcessor $orderProcessor) { $this->orderProcessor = $orderProcessor; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 100. final class CheckoutController { private OrderProcessor $orderProcessor; private CartRepository $cartRepository; public function __construct(OrderProcessor $orderProcessor, CartRepository $cartRepository) { $this->orderProcessor = $orderProcessor; $this->cartRepository = $cartRepository; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; } }
  • 101. final class CheckoutController { private OrderProcessor $orderProcessor; private CartRepository $cartRepository; public function __construct(OrderProcessor $orderProcessor, CartRepository $cartRepository) { $this->orderProcessor = $orderProcessor; $this->cartRepository = $cartRepository; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; } }
  • 102. final class InMemoryCartRepository implements CartRepository { private array $repository = []; public function getCart(string $cart_id): Cart { if (isset($this->repository[$cart_id])) { /** @var Cart $cart */ $cart = $this->repository[$cart_id]; } else { $cart = new Cart($cart_id); } return $cart; } }
  • 103. public function getCart(string $cart_id): Cart { if (!isset($this->repository[$cart_id])) { $this->repository[$cart_id] = new Cart($cart_id); } /** @var Cart $cart */ return $this->repository[$cart_id]; }
  • 104. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75
  • 106. final class CheckoutController { private array $repository = []; private OrderProcessor $orderProcessor; public function __construct(OrderProcessor $orderProcessor) { $this->orderProcessor = $orderProcessor; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options[‘cart_id']); // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 107. final class CheckoutController { private array $repository = []; private OrderProcessor $orderProcessor; public function __construct(OrderProcessor $orderProcessor) { $this->orderProcessor = $orderProcessor; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options[‘cart_id']); // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 108. public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $this->messageBus->dispatch(new AddToCart( $options['cart_id'], $options['product'], $options['type'], $options['quantity'], $options['price'] )); } elseif ('remove_from_cart' === $operation) { $this->messageBus->dispatch(new RemoveFromCart( $options['cart_id'], $options['product'], )); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 109. public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $this->messageBus->dispatch(new AddToCart( $options['cart_id'], $options['product'], $options['type'], $options['quantity'], $options['price'] )); } elseif ('remove_from_cart' === $operation) { $this->messageBus->dispatch(new RemoveFromCart( $options['cart_id'], $options['product'], )); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 110. final class AddToCart { private string $cartId; private string $product; private string $type; private int $quantity; private int $price; public function __construct(...) { $this->cartId = $cartId; $this->product = $product; $this->type = $type; $this->quantity = $quantity; $this->price = $price; } }
  • 111. final class AddToCartHandler implements MessageHandlerInterface { private CartRepository $cartRepository; public function __construct(CartRepository $cartRepository) { $this->cartRepository = $cartRepository; } public function __invoke(AddToCart $addToCart) { $cart = $this->cartRepository->getCart($addToCart->getCartId()); $cart->addProduct( $addToCart->getProduct(), $addToCart->getType(), $addToCart->getQuantity(), $addToCart->getPrice() ); $this->cartRepository->save($cart); } }
  • 112. public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $this->messageBus->dispatch(new AddToCart( $options['cart_id'], $options['product'], $options['type'], $options['quantity'], $options['price'] )); } elseif ('remove_from_cart' === $operation) { $this->messageBus->dispatch(new RemoveFromCart( $options['cart_id'], $options['product'], )); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 113. public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $this->messageBus->dispatch(new AddToCart( $options['cart_id'], $options['product'], $options['type'], $options['quantity'], $options['price'] )); } elseif ('remove_from_cart' === $operation) { $this->messageBus->dispatch(new RemoveFromCart( $options['cart_id'], $options['product'], )); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 114. public function cartAction(CartIdAwareCommand $operation): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($operation->getCartId()); // Perform operation $this->messageBus->dispatch($operation); $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 115. public function cartAction(CartIdAwareCommand $operation): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($operation->getCartId()); // Perform operation $this->messageBus->dispatch($operation); $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 116. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33
  • 118. final class AddToCartHandler implements MessageHandlerInterface { private CartRepository $cartRepository; public function __construct(CartRepository $cartRepository) { $this->cartRepository = $cartRepository; } public function __invoke(AddToCart $addToCart) { $cart = $this->cartRepository->getCart($addToCart->getCartId()); $cart->addProduct( $addToCart->getProduct(), $addToCart->getType(), $addToCart->getQuantity(), $addToCart->getPrice() ); $this->cartRepository->save($cart); } }
  • 119. final class AddToCartHandler implements MessageHandlerInterface { private CartRepository $cartRepository; public function __construct(CartRepository $cartRepository) { $this->cartRepository = $cartRepository; } public function __invoke(AddToCart $addToCart) { $cart = $this->cartRepository->getCart($addToCart->getCartId()); $cart->addProduct( $addToCart->getProduct(), $addToCart->getType(), $addToCart->getQuantity(), $addToCart->getPrice() ); $this->cartRepository->save($cart); } }
  • 120. final class AddToCartHandler implements MessageHandlerInterface { private CartRepository $cartRepository; public function __construct(CartRepository $cartRepository) { $this->cartRepository = $cartRepository; } public function __invoke(AddToCart $addToCart): Cart { $cart = $this->cartRepository->getCart($addToCart->getCartId()); $cart->addProduct( $addToCart->getProduct(), $addToCart->getType(), $addToCart->getQuantity(), $addToCart->getPrice() ); return $cart; } }
  • 121. final class AddToCartHandler implements MessageHandlerInterface { private CartRepository $cartRepository; public function __construct(CartRepository $cartRepository) { $this->cartRepository = $cartRepository; } public function __invoke(AddToCart $addToCart): Cart { $cart = $this->cartRepository->getCart($addToCart->getCartId()); $cart->addProduct( $addToCart->getProduct(), $addToCart->getType(), $addToCart->getQuantity(), $addToCart->getPrice() ); return $cart; } }
  • 122. final class TransactionDecorator { private MessageHandlerInterface $messageHandler; private CartRepository $cartRepository; public function __construct( MessageHandlerInterface $messageHandler, CartRepository $cartRepository ) { $this->messageHandler = $messageHandler; $this->cartRepository = $cartRepository; } public function __invoke(CartIdAwareCommand $cartIdAwareCommand) { // start transaction $cart = ($this->messageHandler)($cartIdAwareCommand); $this->cartRepository->save($cart); } }
  • 123. Can Symfony help us with it?
  • 125. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33 Decorator iteration 216 13 1.31
  • 128. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33 Decorator iteration 216 13 1.31
  • 129. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33 Decorator iteration 216 13 1.31
  • 130. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33 Decorator iteration 216 13 1.31
  • 131. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33 Decorator iteration 216 13 1.31
  • 135. Symfony may help you a lot