Skip to content
intermediate Phase 8 · SOLID & Design Principles

Encapsulation vs Abstraction Deep Dive

Detailed exploration of encapsulation and abstraction, access modifiers, information hiding, public API design, and Magento service contracts

45m
0 problems
Topic Progress 0%

Encapsulation vs Abstraction: The Distinction

Core Difference

Encapsulation is about hiding implementation details — it controls what parts of the code can access the internal state of an object. It's a mechanism.

Abstraction is about hiding complexity — it presents only the essential features while hiding unnecessary detail. It's a concept/design approach.

Analogy

Consider a car:

  • Encapsulation: The engine is enclosed under the hood. You can't directly touch the pistons.
  • Abstraction: The steering wheel, pedals, and dashboard present a simple interface to the driver. The driver doesn't need to understand fuel injection to drive.

Code Example: Both at Work

namespace Vendor\Inventory\Model;

// Abstraction: This interface defines WHAT, not HOW
interface StockCheckerInterface
{
    public function isAvailable(string $sku, int $quantity): bool;
    public function getStockLevel(string $sku): int;
}

// Encapsulation: Private methods hide internal logic
class DatabaseStockChecker implements StockCheckerInterface
{
    private int $cacheTtl = 300;
    private array $cache = [];

    public function __construct(
        private \Magento\Framework\DB\Adapter\AdapterInterface $connection
    ) {}

    public function isAvailable(string $sku, int $quantity): bool
    {
        // Public API: simple method
        $stock = $this->getStockLevel($sku);
        return $stock >= $quantity;
    }

    public function getStockLevel(string $sku): int
    {
        if ($this->isCached($sku)) {
            return $this->getCached($sku);
        }

        $level = $this->queryStockFromDatabase($sku);  // private
        $this->cacheResult($sku, $level);               // private
        return $level;
    }

    private function queryStockFromDatabase(string $sku): int
    {
        // Complex SQL hidden from callers
        $result = $this->connection->fetchOne(
            'SELECT qty FROM catalog_inventory WHERE sku = ?',
            [$sku]
        );
        return (int) $result;
    }

    private function isCached(string $sku): bool
    {
        return isset($this->cache[$sku])
            && $this->cache[$sku]['expires'] > time();
    }

    private function getCached(string $sku): int
    {
        return $this->cache[$sku]['value'];
    }

    private function cacheResult(string $sku, int $value): void
    {
        $this->cache[$sku] = [
            'value' => $value,
            'expires' => time() + $this->cacheTtl,
        ];
    }
}

Callers use StockCheckerInterface (abstraction) without knowing about caching, SQL, or expiration logic (encapsulation).

Information Hiding Beyond Access Modifiers

Visibility Isn't Everything

PHP access modifiers are just one tool. Information hiding also includes:

1. Returning Copies Instead of References

class ShoppingCart
{
    private array $items = [];

    public function addItem(string $sku, int $qty): void
    {
        $this->items[$sku] = $qty;
    }

    // BAD: Returns reference to internal array — caller can modify it
    public function getItemsBad(): array
    {
        return $this->items;
    }

    // GOOD: Returns a copy — internal state is safe
    public function getItems(): array
    {
        return $this->items; // PHP 7+ returns copy by default
    }
}

2. Using Value Objects

final class Money
{
    public function __construct(
        private float $amount,
        private string $currency
    ) {
        if ($amount < 0) {
            throw new \InvalidArgumentException('Amount cannot be negative');
        }
    }

    public function getAmount(): float { return $this->amount; }
    public function getCurrency(): string { return $this->currency; }

    public function add(Money $other): Money
    {
        if ($this->currency !== $other->currency) {
            throw new \InvalidArgumentException('Cannot add different currencies');
        }
        return new Money($this->amount + $other->amount, $this->currency);
    }
}

// Usage: impossible to create invalid Money
$price = new Money(29.99, 'USD');
// $price->amount = -10; // Impossible — no setter, private property

3. Behavioral Contracts via Type Hints

// Type hints enforce abstraction boundaries
public function process(PaymentInterface $payment): void
{
    // Only PaymentInterface methods are available
    // Caller cannot access payment gateway internals
    $payment->charge($this->amount);
}

Magento Service Contracts as Abstraction

Service Contracts in Magento 2

Magento's service contracts provide a stable API layer that abstracts the underlying implementation. This is the canonical example of abstraction in Magento.

Three Types of Service Contracts

1. Repository Interfaces (CRUD)

// Magento\Catalog\Api\ProductRepositoryInterface
interface ProductRepositoryInterface
{
    public function save(
        \Magento\Catalog\Api\Data\ProductInterface $product,
        $saveOptions = false
    );

    public function get(
        $sku,
        $editMode = false,
        $storeId = null,
        $forceReload = false
    );

    public function getById($productId, $editMode = false, $storeId = null);

    public function delete(\Magento\Catalog\Api\Data\ProductInterface $product);

    public function deleteById($productId);
}

2. Data Interfaces (DTOs)

// Magento\Catalog\Api\Data\ProductInterface
class ProductInterface
{
    public function getId(): ?int;
    public function getSku(): ?string;
    public function getName(): ?string;
    public function getPrice(): ?float;
    public function setStatus(int $status): self;
    // ... 50+ methods for every product attribute
}

3. Service Classes (Business Logic)

// Magento\Catalog\Api\ProductAttributeRepositoryInterface
interface ProductAttributeRepositoryInterface
{
    public function get($attributeCode);
    public function getList(
        \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
    );
}

Why This Matters for Developers

// Your code depends on interfaces, not implementations
namespace Vendor\Import\Model;

class ProductImporter
{
    public function __construct(
        private \Magento\Catalog\Api\ProductRepositoryInterface $productRepo
    ) {}

    public function import(array $data): void
    {
        $product = $this->productRepo->get($data['sku']);
        $product->setName($data['name']);
        $this->productRepo->save($product);
    }
}

If Adobe changes the internal implementation from MySQL to Elasticsearch for product storage, your code doesn't change — you depend on the interface, not the implementation.

Designing Clean Public APIs

Principles of API Design

1. Minimal Interface

// BAD: Exposes everything
interface UserInterface
{
    public function getId(): int;
    public function getEmail(): string;
    public function getPasswordHash(): string; // Security leak!
    public function getResetToken(): ?string; // Internal!
    public function setLastLogin(DateTime $time): void; // Audit detail
}

// GOOD: Only what consumers need
interface UserFacadeInterface
{
    public function getById(int $id): ?UserViewInterface;
    public function findByEmail(string $email): ?UserViewInterface;
    public function create(CreateUserRequest $request): UserViewInterface;
}

interface UserViewInterface
{
    public function getId(): int;
    public function getEmail(): string;
    public function getFullName(): string;
}

2. Immutability by Default

// Prefer immutable objects in APIs
final class OrderSummary
{
    public function __construct(
        private readonly int $orderId,
        private readonly float $total,
        private readonly string $status
    ) {}

    public function getOrderId(): int { return $this->orderId; }
    public function getTotal(): float { return $this->total; }
    public function getStatus(): string { return $this->status; }
    // No setters — create new instance to change state
}

3. Fail Fast with Clear Errors

public function placeOrder(CartInterface $cart): OrderInterface
{
    if ($cart->isEmpty()) {
        throw new \Magento\Framework\Exception\LocalizedException(
            new \Magento\Framework\Phrase('Cannot place order with empty cart')
        );
    }

    if (!$cart->hasShippingAddress()) {
        throw new \Magento\Framework\Exception\LocalizedException(
            new \Magento\Framework\Phrase('Shipping address is required')
        );
    }

    // Proceed with order placement
}

Quiz

1. What is the fundamental difference between encapsulation and abstraction?

Question 1 options

2. In Magento, Service Contracts primarily serve which purpose?

Question 2 options

3. Why should API methods return copies of internal data rather than references?

Question 3 options

Flashcards

Question

What is encapsulation?

Answer

Hiding implementation details through access control and information hiding

Question

What is abstraction?

Answer

Presenting only essential features while hiding unnecessary complexity

Question

What are Magento service contracts?

Answer

API interfaces (Repository, Data, Service) that abstract underlying implementations

Question

Why use value objects for APIs?

Answer

They are immutable and self-validating, preventing invalid states

Revision Notes

Key Takeaways

  • 1. Encapsulation = mechanism (hiding state), Abstraction = concept (hiding complexity)
  • 2. Information hiding goes beyond access modifiers: copies, value objects, type hints
  • 3. Magento service contracts are the primary abstraction layer in the platform
  • 4. Design minimal, immutable, fail-fast public APIs
  • 5. Abstraction enables swapping implementations without changing consumers

Interview Tips

  • Clearly articulate the difference: encapsulation is about control, abstraction is about simplification
  • Explain how Magento service contracts protect against implementation changes
  • Discuss when abstraction is over-engineering vs necessary

Cheat Sheet

Encapsulation → Controls access (private/protected/public)
Abstraction   → Simplifies interface (hides complexity)

Magento Service Contracts:
  RepositoryInterface → CRUD operations
  DataInterface       → DTOs (product, order, etc.)
  ServiceInterface    → Business logic

API Design Rules:
  1. Minimal interface (don't expose internals)
  2. Immutable where possible (readonly)
  3. Fail fast with clear errors
  4. Return copies, not references