0

Abstraction — hiding implementation detail behind a stable interface

Beginner5 min read·eng-16-009
interviewsolid

Concept

Abstraction — hiding implementation details behind a stable, simpler interface. Users of an abstraction see only WHAT it does, not HOW it does it.

Levels of abstraction:

  • A function is an abstraction: sort($array) hides the sorting algorithm.
  • A class is an abstraction: File::read($path) hides file system calls.
  • An interface is an abstraction: LoggerInterface::log($msg) hides whether it logs to file, DB, or Slack.
  • A module/service is an abstraction: "The payment service" hides Stripe, PayPal, or any gateway.

Why abstraction is valuable:

  • Reduced complexity: Callers only need to understand the interface, not the implementation.
  • Changeability: Swap the implementation without changing callers. Replace FileLogger with SlackLogger everywhere by just changing what you bind in the container.
  • Testability: Code depending on an interface can be tested with a test double.
  • Separation of concerns: The caller doesn't need to know about database connections, HTTP clients, file handles.

The right level of abstraction: Too low = leaking details. Too high = hiding things that callers need to know. Finding the right level is a design skill.

PHP mechanisms for abstraction:

  • Interfaces: interface LoggerInterface { public function log(string $msg): void; }
  • Abstract classes: Partial implementation, force subclasses to fill in details.
  • Facades (Laravel): Log::info('...') is a facade abstraction over the log manager.
  • Service classes: Abstract away external APIs.

Abstraction ≠ complexity: A GOOD abstraction simplifies. A BAD abstraction (over-engineering, wrong level) adds complexity. The goal is simplicity at the point of use.

Code Example

php
<?php
// LOW ABSTRACTION — implementation exposed everywhere
$curl = curl_init('https://api.stripe.com/v1/charges');
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Authorization: Bearer sk_test_...']);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query(['amount' => 1000, 'currency' => 'usd']));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($curl), true);
// Every payment use-case duplicates this — tight coupling to Stripe's API!

// HIGH ABSTRACTION — implementation hidden
interface PaymentGateway
{
    public function charge(int $amountCents, string $currency, string $token): PaymentResult;
    public function refund(string $chargeId): void;
}

// Implementation detail hidden from callers
class StripeGateway implements PaymentGateway
{
    public function __construct(private readonly \Stripe\StripeClient $client) {}

    public function charge(int $amountCents, string $currency, string $token): PaymentResult
    {
        $charge = $this->client->charges->create([
            'amount'   => $amountCents,
            'currency' => $currency,
            'source'   => $token,
        ]);
        return new PaymentResult(success: true, transactionId: $charge->id);
    }

    public function refund(string $chargeId): void
    {
        $this->client->refunds->create(['charge' => $chargeId]);
    }
}

// Caller only knows the interface — abstracted from Stripe
class CheckoutService
{
    public function __construct(private readonly PaymentGateway $gateway) {}

    public function checkout(Cart $cart, string $token): Order
    {
        $result = $this->gateway->charge($cart->totalCents(), 'usd', $token);
        // No knowledge of Stripe, curl, or API keys here
        return Order::create([
            'total'          => $cart->total(),
            'payment_method' => $result->transactionId,
        ]);
    }
}

// ABSTRACTION LEVELS in PHP built-ins:
strlen('hello');   // low-level abstraction over C string operations
\Carbon\Carbon::now(); // abstraction over PHP DateTime and timezone handling
\Illuminate\Support\Facades\Storage::put($path, $contents); // abstraction over S3, local, SFTP