Types of Coupling
Coupling Types
| Type | Description | Example |
|---|---|---|
| Content | Shared internal data | Global variables |
| Common | Shared global data | Static methods |
| External | Via external data format | XML config, database |
| Control | Flag/control flow passed | Callback functions |
| Stamp | Shared composite data | Passing entire objects |
| Data | Via parameters only | Method arguments |
Tight Coupling Example
// BAD: Tight coupling
class OrderProcessor
{
public function process($orderId)
{
// Direct dependency on concrete class
$connection = new \Magento\Framework\DB\Adapter\Pdo\Mysql();
$order = $connection->fetchRow("SELECT * FROM sales_order WHERE entity_id = {$orderId}");
// Direct dependency on another concrete class
$emailSender = new \Vendor\Email\Model\Sender();
$emailSender->send($order['customer_email']);
}
}
Loose Coupling Example
// GOOD: Loose coupling via interfaces
class OrderProcessor
{
private OrderRepositoryInterface $orderRepository;
private EmailServiceInterface $emailService;
public function __construct(
OrderRepositoryInterface $orderRepository,
EmailServiceInterface $emailService
) {
$this->orderRepository = $orderRepository;
$this->emailService = $emailService;
}
public function process(OrderId $orderId): void
{
$order = $this->orderRepository->get($orderId);
$this->emailService->sendOrderConfirmation($order);
}
}
Coupling in Magento
// BAD: Tight coupling in Magento
class ProductController
{
public function execute()
{
// Direct model dependency
$product = Mage::getModel('catalog/product')->load($id);
// Direct helper dependency
$helper = Mage::helper('catalog');
}
}
// GOOD: Loose coupling via DI
class ProductController
{
private ProductRepositoryInterface $productRepository;
private CatalogHelper $catalogHelper;
public function __construct(
ProductRepositoryInterface $productRepository,
CatalogHelper $catalogHelper
) {
$this->productRepository = $productRepository;
$this->catalogHelper = $catalogHelper;
}
}
Key Takeaway
Reduce coupling by depending on interfaces, not concrete classes. Use dependency injection instead of direct instantiation. Avoid global state and static methods.
Types of Cohesion
Cohesion Levels (Best to Worst)
| Level | Description | Example |
|---|---|---|
| Functional | Single purpose | Math functions |
| Sequential | Output of one feeds input of next | Pipeline |
| Communicational | Operate on same data | Same entity operations |
| Procedural | Execute in sequence | Workflow steps |
| Temporal | Execute at same time | Initialization |
| Logical | Logically related | Utilities |
| Coincidental | No meaningful relationship | Random functions |
High Cohesion Example
// GOOD: High cohesion - all related functionality
class OrderService
{
public function placeOrder(OrderRequest $request): Order
{
$order = $this->createOrder($request);
$this->processPayment($order);
$this->sendConfirmation($order);
return $order;
}
private function createOrder(OrderRequest $request): Order { /* ... */ }
private function processPayment(Order $order): void { /* ... */ }
private function sendConfirmation(Order $order): void { /* ... */ }
}
Low Cohesion Example
// BAD: Low cohesion - unrelated functionality
class Manager
{
public function processOrder($orderId) { /* ... */ }
public function generateReport() { /* ... */ }
public function sendEmail($to, $subject, $body) { /* ... */ }
public function updateInventory($productId) { /* ... */ }
public function calculateTax($amount) { /* ... */ }
}
Improving Cohesion
// Before: Low cohesion
class OrderHelper
{
public function createOrder() { /* ... */ }
public function validateAddress() { /* ... */ }
public function calculateShipping() { /* ... */ }
public function sendNotification() { /* ... */ }
}
// After: High cohesion - split by responsibility
class OrderCreator
{
public function create(OrderRequest $request): Order { /* ... */ }
}
class AddressValidator
{
public function validate(Address $address): bool { /* ... */ }
}
class ShippingCalculator
{
public function calculate(Order $order): Money { /* ... */ }
}
class OrderNotifier
{
public function sendConfirmation(Order $order): void { /* ... */ }
}
Key Takeaway
High cohesion means related functionality is grouped together. Split classes with unrelated responsibilities. Each class should have a single, well-defined purpose.
Measuring Coupling and Cohesion
Coupling Metrics
// Coupling Between Objects (CBO)
// Count of classes this class depends on
class OrderProcessor
{
private OrderRepository $orderRepo; // +1
private PaymentGateway $payment; // +1
private EmailService $email; // +1
private InventoryService $inventory; // +1
private TaxCalculator $tax; // +1
// CBO = 5 (high coupling)
}
Cohesion Metrics
// Lack of Coherence (LCOM)
// Measures how methods share instance variables
class OrderService
{
private $orderRepo;
private $paymentGateway;
// Methods using $orderRepo
public function getOrder() { /* uses $orderRepo */ }
public function saveOrder() { /* uses $orderRepo */ }
// Methods using $paymentGateway
public function processPayment() { /* uses $paymentGateway */ }
public function refundPayment() { /* uses $paymentGateway */ }
// LCOM is low (good cohesion)
// Two groups, each using shared state
}
Static Analysis Tools
# PHPStan coupling analysis
vendor/bin/phpstan analyse --level=6 app/code/Vendor/
# PHPMD cohesion metrics
vendor/bin/phpmd app/code/Vendor/ text codesize,cleancode
# Exakat coupling analysis
vendor/bin/exakat report --project app/code/Vendor/ --format coupling
Refactoring for Better Metrics
// Before: High coupling, low cohesion
class OrderManager
{
private $db;
private $cache;
private $email;
private $payment;
public function process($data) { /* uses all 4 */ }
public function save($data) { /* uses $db */ }
public function cache($key, $val) { /* uses $cache */ }
}
// After: Lower coupling, higher cohesion
class OrderProcessor
{
private OrderRepository $repo;
private PaymentService $payment;
public function process(Order $order) { /* uses $repo, $payment */ }
}
class CacheManager
{
private CacheInterface $cache;
public function save($key, $val) { /* uses $cache */ }
}
Key Takeaway
Measure coupling (CBO) and cohesion (LCOM) with static analysis tools. Refactor to reduce dependencies and group related functionality.
Reducing Coupling in Magento
Dependency Injection
<!-- Use interfaces in DI -->
<type name="Vendor\Module\Model\OrderProcessor">
<arguments>
<argument name="orderRepository" xsi:type="object">
Magento\Sales\Api\OrderRepositoryInterface
</argument>
<argument name="paymentService" xsi:type="object">
Vendor\Payment\Api\PaymentServiceInterface
</argument>
</arguments>
</type>
Event System
// Decouple via events
class OrderService
{
private EventDispatcher $eventDispatcher;
public function placeOrder(Order $order): void
{
// Process order
$this->orderRepository->save($order);
// Dispatch event instead of direct dependency
$this->eventDispatcher->dispatch('order_placed', [
'order' => $order
]);
}
}
// Observer handles side effects
class SendOrderConfirmation
{
public function execute(EventObserver $observer)
{
$order = $observer->getEvent()->getOrder();
$this->emailService->sendConfirmation($order);
}
}
Plugin System
// Extend behavior without modifying
class OrderPlugin
{
public function aroundPlace(
Order $subject,
callable $proceed,
OrderInterface $order
): OrderInterface {
// Add behavior without modifying Order class
$this->validate($order);
$result = $proceed($order);
$this->log($result);
return $result;
}
}
Service Contracts
// Depend on interfaces (ports)
interface OrderRepositoryInterface
{
public function save(OrderInterface $order): OrderInterface;
public function get(int $orderId): OrderInterface;
}
// Implementation is swappable (adapters)
class MysqlOrderRepository implements OrderRepositoryInterface { /* ... */ }
class ElasticOrderRepository implements OrderRepositoryInterface { /* ... */ }
Configuration Over Code
<!-- Use XML configuration instead of code -->
<config>
<vendor>
<payment>
<gateway>Stripe</gateway>
<timeout>30</timeout>
</payment>
</vendor>
</config>
Key Takeaway
Reduce coupling with DI, events, plugins, service contracts, and configuration. Depend on interfaces, not implementations.
Quiz
1. What is tight coupling?
2. What is high cohesion?
3. How to reduce coupling in Magento?
4. What is CBO (Coupling Between Objects)?
5. What Magento feature reduces coupling?
Flashcards
Question
What is tight coupling?
Click to reveal answer
Answer
Direct dependency on concrete implementations, making changes difficult
Question
What is high cohesion?
Click to reveal answer
Answer
Related functionality grouped together with clear purpose
Question
How to reduce coupling?
Click to reveal answer
Answer
DI with interfaces, events, plugins, service contracts
Question
What is CBO?
Click to reveal answer
Answer
Coupling Between Objects - count of dependencies
Question
What is LCOM?
Click to reveal answer
Answer
Lack of Coherence - measures how methods share instance variables
Question
How do events reduce coupling?
Click to reveal answer
Answer
Allow modules to communicate without direct dependencies
Revision Notes
Key Takeaways
- 1. Reduce coupling by depending on interfaces, not implementations
- 2. High cohesion groups related functionality together
- 3. Measure coupling (CBO) and cohesion (LCOM) with tools
- 4. Use DI, events, plugins to reduce coupling in Magento
- 5. Split low-cohesion classes into focused classes
Interview Tips
- • Explain types of coupling and how to reduce them
- • Discuss cohesion and how to improve it
- • Describe metrics for measuring coupling/cohesion
- • Explain how Magento features reduce coupling
Cheat Sheet
Coupling & Cohesion
Coupling (lower is better):
Depend on interfaces
Use dependency injection
Avoid global state
Cohesion (higher is better):
Group related functionality
Single responsibility
Split unrelated concerns
Magento Tools:
DI: interfaces in di.xml
Events: observer pattern
Plugins: around/after/before
Service Contracts: Api interfaces
Metrics:
CBO: Coupling Between Objects
LCOM: Lack of Coherence