core-engineering

📁 justinedevs/collection 📅 3 days ago
3
总安装量
3
周安装量
#62648
全站排名
安装命令
npx skills add https://github.com/justinedevs/collection --skill core-engineering

Agent 安装分布

opencode 3
gemini-cli 3
github-copilot 3
codex 3
amp 3
kimi-cli 3

Skill 文档

Object-Oriented Design Skill

This skill defines Core Engineering: Object-Oriented Programming (OOP), SOLID, clean-code principles, and relationship types with concrete examples so an AI agent or developer can apply them consistently.


1. Four Pillars of OOP

1.1 Abstraction

What it is: Focus on what an object does, not how. Hide complex details and expose only essential behavior.

How it looks: Public methods define the contract; internals are private or hidden.

// Abstraction: caller uses start() and stop() without knowing engine details
class Car {
  private engine: Engine;

  start(): void {
    this.engine.ignite();
  }
  stop(): void {
    this.engine.shutOff();
  }
}

Types of abstraction:

  • Data abstraction: Hide how data is stored (e.g. use getters instead of raw fields).
  • Control abstraction: Hide how a process is implemented (e.g. save() instead of open/write/close).
// Data abstraction: storage details hidden
class UserRepo {
  private store: Map<string, User>;
  getById(id: string): User | undefined {
    return this.store.get(id);
  }
}

1.2 Encapsulation

What it is: Bundle data and methods that act on that data in one unit (class). Restrict direct access to internal state to avoid accidental misuse.

How it looks: Private fields, public accessors or methods; no direct property access from outside.

// Encapsulation: balance is protected; changes only through deposit/withdraw
class BankAccount {
  private balance: number = 0;

  deposit(amount: number): void {
    if (amount > 0) this.balance += amount;
  }
  withdraw(amount: number): boolean {
    if (amount > 0 && amount <= this.balance) {
      this.balance -= amount;
      return true;
    }
    return false;
  }
  getBalance(): number {
    return this.balance;
  }
}

1.3 Inheritance

What it is: A child class gets properties and behavior from a parent class to reuse code and model “is-a” relationships.

How it looks: Child extends parent and can override methods or add new ones.

// Inheritance: SportsCar is-a Vehicle with extra behavior
class Vehicle {
  move(): void {
    console.log("Moving");
  }
}
class SportsCar extends Vehicle {
  turboBoost(): void {
    console.log("Turbo engaged");
  }
}

1.4 Polymorphism

What it is: Different classes can be used through the same interface; the same method name can behave differently per type.

How it looks: Parent reference, child instances; overridden methods or shared interface.

// Polymorphism: same call, different behavior per type
abstract class Shape {
  abstract draw(): void;
}
class Circle extends Shape {
  draw(): void {
    console.log("Drawing circle");
  }
}
class Square extends Shape {
  draw(): void {
    console.log("Drawing square");
  }
}
function render(s: Shape): void {
  s.draw();
}
render(new Circle());
render(new Square());

2. SOLID Principles

2.1 Single Responsibility Principle (SRP)

What it is: A class should have only one reason to change (one responsibility).

How it looks: One class does one job; split reporting, persistence, and validation into separate types.

// SRP: each class has one job
class Order {
  constructor(
    public id: string,
    public total: number
  ) {}
}
class OrderRepository {
  save(order: Order): void {
    // persist only
  }
}
class OrderReport {
  format(order: Order): string {
    return `Order ${order.id}: ${order.total}`;
  }
}

2.2 Open/Closed Principle (OCP)

What it is: Open for extension, closed for modification. Add behavior via new code (e.g. new classes), not by changing existing code.

How it looks: Use abstractions and new implementations instead of editing existing classes.

// OCP: extend via new class, not by changing Discount
interface Discount {
  apply(amount: number): number;
}
class PercentDiscount implements Discount {
  constructor(private percent: number) {}
  apply(amount: number): number {
    return amount * (1 - this.percent / 100);
  }
}
class FixedDiscount implements Discount {
  constructor(private fixed: number) {}
  apply(amount: number): number {
    return Math.max(0, amount - this.fixed);
  }
}

2.3 Liskov Substitution Principle (LSP)

What it is: Subclasses must be substitutable for their base class without breaking callers.

How it looks: Overrides preserve contracts (same preconditions/postconditions); no throwing new errors or weakening guarantees.

// LSP: Rectangle subclass can replace Shape without breaking callers
class Shape {
  area(): number {
    return 0;
  }
}
class Rectangle extends Shape {
  constructor(private w: number, private h: number) {
    super();
  }
  area(): number {
    return this.w * this.h;
  }
}
function printArea(s: Shape): void {
  console.log(s.area());
}
printArea(new Rectangle(3, 4));

2.4 Interface Segregation Principle (ISP)

What it is: Clients should not depend on methods they do not use. Prefer small, focused interfaces.

How it looks: Many small interfaces instead of one large one; classes implement only what they need.

// ISP: split fat interface into small ones
interface Readable {
  read(): string;
}
interface Writable {
  write(data: string): void;
}
class FileReader implements Readable {
  read(): string {
    return "data";
  }
}
class FileWriter implements Writable {
  write(data: string): void {}
}
// Client that only reads depends only on Readable
function consume(r: Readable): void {
  r.read();
}

2.5 Dependency Inversion Principle (DIP)

What it is: Depend on abstractions (interfaces), not concrete classes. High-level logic should not import low-level details.

How it looks: Inject interfaces; concrete implementations passed in or provided by composition root.

// DIP: OrderService depends on abstraction, not concrete Logger
interface Logger {
  log(msg: string): void;
}
class OrderService {
  constructor(private logger: Logger) {}
  placeOrder(): void {
    this.logger.log("Order placed");
  }
}
class ConsoleLogger implements Logger {
  log(msg: string): void {
    console.log(msg);
  }
}
const service = new OrderService(new ConsoleLogger());

3. Composition Over Inheritance

What it is: Favor composing objects (has-a) over deep inheritance (is-a) to reduce coupling and increase flexibility.

How it looks: Classes hold references to other types instead of extending them; behavior is delegated.

// Composition: Engine is a component, not a parent
class Engine {
  start(): void {
    console.log("Engine started");
  }
}
class Car {
  private engine: Engine;
  constructor() {
    this.engine = new Engine();
  }
  start(): void {
    this.engine.start();
  }
}

4. Clean Code Principles

4.1 DRY (Don’t Repeat Yourself)

What it is: Avoid duplicating logic; centralize in one place.

// DRY: single function for validation
function isValidEmail(s: string): boolean {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s);
}
// Reuse everywhere instead of copying the regex

4.2 KISS (Keep It Simple, Stupid)

What it is: Avoid unnecessary complexity; prefer straightforward solutions.

// KISS: simple condition instead of over-engineered pattern
function isEligible(age: number): boolean {
  return age >= 18;
}

4.3 YAGNI (You Aren’t Gonna Need It)

What it is: Do not add functionality until it is required.

How it looks: No speculative features or “might need later” code; implement when the need exists.


4.4 Law of Demeter (LoD)

What it is: An object should only talk to its immediate neighbors (its own attributes, method arguments, or objects it creates). Avoid long chains like a.getB().getC().doSomething().

How it looks: Delegate to a neighbor so the caller uses one dot.

// LoD violation: client knows internal structure
// client.getAddress().getStreet().toUpperCase()

// Better: one level of indirection
class Client {
  getStreetUpperCase(): string {
    return this.address.getStreet().toUpperCase();
  }
}

5. Advanced Relationship Types

5.1 Association

What it is: General link between two objects; they can interact but are not necessarily owned.

// Association: Teacher and Student know each other
class Teacher {
  constructor(public students: Student[]) {}
}
class Student {
  constructor(public teacher: Teacher) {}
}

5.2 Aggregation (has-a; child can outlive parent)

What it is: Whole has parts; parts can exist without the whole (e.g. department has employees; employees can exist without the department).

// Aggregation: Department has Employees; employees can exist independently
class Department {
  constructor(public employees: Employee[] = []) {}
  add(e: Employee): void {
    this.employees.push(e);
  }
}
class Employee {
  constructor(public name: string) {}
}

5.3 Composition (part-of; child cannot outlive parent)

What it is: Strong ownership; the part does not exist without the whole (e.g. Engine cannot exist without the Car in the same lifecycle).

// Composition: Engine is created and owned by Car; lifecycle bound
class Car {
  private engine: Engine;
  constructor() {
    this.engine = new Engine();
  }
}
class Engine {}

Quick Reference

Concept One-line
Abstraction Expose what, hide how
Encapsulation Bundle data + methods; restrict direct access
Inheritance Child reuses parent behavior (is-a)
Polymorphism Same interface, different behavior per type
SRP One reason to change per class
OCP Extend via new code, don’t modify existing
LSP Subtypes substitutable for base type
ISP Small interfaces; no unused methods
DIP Depend on abstractions, not concretions
Composition Prefer has-a over deep inheritance
DRY One place for each piece of logic
KISS Prefer simple design
YAGNI Build only what is needed now
LoD Talk only to immediate neighbors
Association General link between objects
Aggregation Has-a; part can outlive whole
Composition Part-of; part bound to whole lifecycle