OOP Concepts
- Encapsulation: hiding internal details of object and exposing only what's necessary by using
getter/settermethods andprivate, public, protected - Abstraction: focus on essential features while hiding complex implmentation by using abstract classes or interfaces
- Inheritance: mechanism to create a new class from an existing class, reusing its code and supports "is-a" relationship; uses superclass/subclass
- Polymorphism: ability of an object to take many forms by overloading/overriding methods
Inheritance
- Define a "is-a" relation - subclasses extend a base class
- Allow code sharing where subclasses inherit methods, variables and constructors
- Can only inherit from one parent (superclass)
- Subclasses depends on base implementation
- Best for when classes share common logic and state
- Inheritance is a blueprint that all subclasses share
Interface
- Define a "can-do" relationship - classes implement abilities
- No code sharing, only gives methods and properties name
- One class can implements multiple interface
- Class follows a structure
- Best for when unrelated classes need to share the same API
- Like when integrating third party libraries; use interface so even if the library is changed/updated the purpose stays the same, but implemented in different ways
- Interface is a set of rules that the implementing classes share
- The classes do the same thing, but in different ways
Abstract vs Interface
| Feature | Abstract Class | Interface |
|---|---|---|
| Methods | Can be abstract and concrete | Mostly abstract (can have default or static methods since Java 8) |
| Fields | Can have instance variables | Only public static final constants |
| Constructors | Yes | No |
| Multiple Inheritance | No (one parent class) | Yes (can implement many interfaces) |
| Purpose | To share code/state & enforce structure among subclasses | To define behavior contracts or capabilities that can be applied to any class |
| Example Usage | abstract class Animal (shared behavior) | interface Flyable, interface Serializable (capabilities) |
Java Example
Example for coding different ways to pay
// All payments need to be processed
interface Payment {
processPayment(amount: number): void;
}
// Base class for reusable logic
abstract class PaymentProvider implements Payment {
constructor(protected amount: number) {}
validateAmount() {
if (this.amount <= 0) throw new Error("Invalid amount");
}
abstract processPayment(): void;
}
---------------------------------------------------------
// Stripe reuses common logic
class StripePayment extends PaymentProvider {
processPayment() {
this.validateAmount();
console.log(`Stripe processed ${this.amount}`);
}
}
// ApplePay skips base class but still respects interface
class ApplePayPayment implements Payment {
processPayment(amount: number) {
console.log(`ApplePay processed ${amount}`);
}
}
-----------------------------------------------------------
// Common handler for all payments
function handlePayment(payment: Payment, amount: number) {
payment.processPayment(amount);
}
handlePayment(new StripePayment(100), 100);
handlePayment(new ApplePayPayment(), 50);Javascript Example with API Route
Example for coding different ways to pay alongside Next.js API routes
// All payments need to be processed
export interface Payment {
processPayment(amount: number): Promise<string>;
}
// Base class for reusable logic
export abstract class PaymentProvider implements Payment {
constructor(protected amount: number) {}
protected validateAmount() {
if (this.amount <= 0) {
throw new Error("Amount must be greater than 0");
}
}
abstract processPayment(): Promise<string>;
}
-----------------------------------------------------------
// Stripe reuses common logic
export class StripePayment extends PaymentProvider {
async processPayment(): Promise<string> {
this.validateAmount();
// Simulate Stripe API call
return `Stripe successfully processed ${this.amount}`;
}
}
// ApplePay skips base class but still respects interface
export class ApplePayPayment implements Payment {
async processPayment(amount: number): Promise<string> {
// ApplePay doesn’t need base class logic
return `ApplePay processed ${amount}`;
}
}Putting it together with Next.js API routes
import { NextRequest, NextResponse } from "next/server";
import { StripePayment } from "@/lib/payments/StripePayment";
import { PayPalPayment } from "@/lib/payments/PayPalPayment";
import { ApplePayPayment } from "@/lib/payments/ApplePayPayment";
import { CryptoPayment } from "@/lib/payments/CryptoPayment";
import { Payment } from "@/lib/payments/Payment";
export async function POST(req: NextRequest) {
try {
const { provider, amount } = await req.json();
let payment: Payment;
switch (provider) {
case "stripe":
payment = new StripePayment(amount);
return NextResponse.json({ message: await payment.processPayment() });
case "paypal":
payment = new PayPalPayment(amount);
return NextResponse.json({ message: await payment.processPayment() });
case "applepay":
payment = new ApplePayPayment();
return NextResponse.json({ message: await payment.processPayment(amount) });
case "crypto":
payment = new CryptoPayment();
return NextResponse.json({ message: await payment.processPayment(amount) });
default:
return NextResponse.json({ error: "Invalid payment provider" }, { status: 400 });
}
} catch (error: any) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}