solid-principles

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SOLID Principles

SOLID Principles

Apply SOLID design principles for maintainable, flexible code architecture.
应用SOLID设计原则,打造可维护、灵活的代码架构。

The Five Principles

五大原则

1. Single Responsibility Principle (SRP)

1. 单一职责原则(SRP)

A module should have one, and only one, reason to change

一个模块应该有且仅有一个变更理由

Elixir Pattern

Elixir 设计模式

elixir
undefined
elixir
undefined

BAD - Multiple responsibilities

BAD - 承担多个职责

defmodule UserManager do def create_user(attrs) do # Creates user # Sends welcome email # Logs to analytics # Updates cache end end
defmodule UserManager do def create_user(attrs) do # 创建用户 # 发送欢迎邮件 # 记录分析数据 # 更新缓存 end end

GOOD - Single responsibility

GOOD - 单一职责

defmodule User do def create(attrs), do: Repo.insert(changeset(attrs)) end
defmodule UserNotifier do def send_welcome_email(user), do: # email logic end
defmodule UserAnalytics do def track_signup(user), do: # analytics logic end
undefined
defmodule User do def create(attrs), do: Repo.insert(changeset(attrs)) end
defmodule UserNotifier do def send_welcome_email(user), do: # 邮件逻辑 end
defmodule UserAnalytics do def track_signup(user), do: # 分析逻辑 end
undefined

TypeScript Pattern

TypeScript 设计模式

typescript
// BAD - Multiple responsibilities
class UserComponent {
  render() { /* UI */ }
  fetchData() { /* API */ }
  formatDate() { /* Formatting */ }
  validateInput() { /* Validation */ }
}

// GOOD - Single responsibility
function UserProfile({ user }: Props) {
  return <View>{/* UI only */}</View>;
}

function useUserData(id: string) {
  // Data fetching only
}

function formatUserDate(date: Date): string {
  // Formatting only
}
Ask yourself: "What is the ONE thing this module does?"
typescript
// BAD - 承担多个职责
class UserComponent {
  render() { /* UI 渲染 */ }
  fetchData() { /* API 请求 */ }
  formatDate() { /* 日期格式化 */ }
  validateInput() { /* 输入验证 */ }
}

// GOOD - 单一职责
function UserProfile({ user }: Props) {
  return <View>{/* 仅负责UI */}</View>;
}

function useUserData(id: string) {
  // 仅负责数据获取
}

function formatUserDate(date: Date): string {
  // 仅负责日期格式化
}
自问: "这个模块的核心职责是什么?"

2. Open/Closed Principle (OCP)

2. 开闭原则(OCP)

Software entities should be open for extension, closed for modification.
软件实体应对扩展开放,对修改关闭。

Elixir Pattern (Behaviours)

Elixir 设计模式(Behaviours)

elixir
undefined
elixir
undefined

Define interface

定义接口

defmodule PaymentProvider do @callback process_payment(amount :: Money.t(), token :: String.t()) :: {:ok, transaction :: map()} | {:error, reason :: String.t()} end
defmodule PaymentProvider do @callback process_payment(amount :: Money.t(), token :: String.t()) :: {:ok, transaction :: map()} | {:error, reason :: String.t()} end

Implementations extend without modifying

通过扩展实现,无需修改原代码

defmodule StripeProvider do @behaviour PaymentProvider def process_payment(amount, token), do: # Stripe logic end
defmodule PayPalProvider do @behaviour PaymentProvider def process_payment(amount, token), do: # PayPal logic end
defmodule StripeProvider do @behaviour PaymentProvider def process_payment(amount, token), do: # Stripe 逻辑 end
defmodule PayPalProvider do @behaviour PaymentProvider def process_payment(amount, token), do: # PayPal 逻辑 end

Usage - add new providers without changing this code

使用方式 - 添加新支付提供商无需修改此代码

def charge(provider_module, amount, token) do provider_module.process_payment(amount, token) end
undefined
def charge(provider_module, amount, token) do provider_module.process_payment(amount, token) end
undefined

TypeScript Pattern (Composition)

TypeScript 设计模式(组合)

typescript
// BAD - Requires modification for new types
function renderItem(item: Item) {
  if (item.type === 'gig') {
    return <TaskCard />;
  } else if (item.type === 'shift') {
    return <WorkPeriodCard />;
  }
  // Have to modify this function for new types
}

// GOOD - Extension through props
interface CardRenderer {
  (item: Item): ReactElement;
}

const renderers: Record<string, CardRenderer> = {
  gig: (item) => <TaskCard gig={item} />,
  shift: (item) => <WorkPeriodCard shift={item} />,
  // Add new types here without modifying renderItem
};

function renderItem(item: Item) {
  const renderer = renderers[item.type];
  return renderer ? renderer(item) : <DefaultCard item={item} />;
}
Ask yourself: "Can I add new functionality without changing existing code?"
typescript
// BAD - 添加新类型需修改原函数
function renderItem(item: Item) {
  if (item.type === 'gig') {
    return <TaskCard />;
  } else if (item.type === 'shift') {
    return <WorkPeriodCard />;
  }
  // 新增类型时必须修改此函数
}

// GOOD - 通过属性扩展
interface CardRenderer {
  (item: Item): ReactElement;
}

const renderers: Record<string, CardRenderer> = {
  gig: (item) => <TaskCard gig={item} />,
  shift: (item) => <WorkPeriodCard shift={item} />,
  // 新增类型只需在此添加,无需修改renderItem
};

function renderItem(item: Item) {
  const renderer = renderers[item.type];
  return renderer ? renderer(item) : <DefaultCard item={item} />;
}
自问: "我能否在不修改现有代码的情况下添加新功能?"

3. Liskov Substitution Principle (LSP)

3. 里氏替换原则(LSP)

Subtypes must be substitutable for their base types

子类型必须能够替换其基类型

Elixir Pattern (LSP)

Elixir 设计模式(LSP)

elixir
undefined
elixir
undefined

BAD - Violates LSP (raises when base type would return)

BAD - 违反LSP(基类型本应返回结果时抛出异常)

defmodule PaymentCalculator do def calculate_total(items) when length(items) > 0 do Enum.sum(items) end

Missing clause - raises on empty list

end
defmodule PaymentCalculator do def calculate_total(items) when length(items) > 0 do Enum.sum(items) end

缺少空列表处理分支 - 会抛出异常

end

GOOD - Honors contract

GOOD - 遵守契约

defmodule PaymentCalculator do def calculate_total(items) when is_list(items) do Enum.sum(items) # Returns 0 for empty list end end
undefined
defmodule PaymentCalculator do def calculate_total(items) when is_list(items) do Enum.sum(items) # 空列表时返回0 end end
undefined

TypeScript Pattern (LSP)

TypeScript 设计模式(LSP)

typescript
// BAD - Violates LSP
class Bird {
  fly(): void { /* flies */ }
}

class Penguin extends Bird {
  fly(): void {
    throw new Error('Penguins cannot fly');  // Breaks contract
  }
}

// GOOD - Correct abstraction
interface Bird {
  move(): void;
}

class FlyingBird implements Bird {
  move(): void { this.fly(); }
  private fly(): void { /* flies */ }
}

class SwimmingBird implements Bird {
  move(): void { this.swim(); }
  private swim(): void { /* swims */ }
}
Ask yourself: "Can I replace this with its parent/interface without breaking behavior?"
typescript
// BAD - 违反LSP
class Bird {
  fly(): void { /* 飞行逻辑 */ }
}

class Penguin extends Bird {
  fly(): void {
    throw new Error('Penguins cannot fly');  // 破坏契约
  }
}

// GOOD - 正确抽象
interface Bird {
  move(): void;
}

class FlyingBird implements Bird {
  move(): void { this.fly(); }
  private fly(): void { /* 飞行逻辑 */ }
}

class SwimmingBird implements Bird {
  move(): void { this.swim(); }
  private swim(): void { /* 游泳逻辑 */ }
}
自问: "我能否用父类/接口替换它而不破坏现有行为?"

4. Interface Segregation Principle (ISP)

4. 接口隔离原则(ISP)

Clients should not be forced to depend on interfaces they don't use.
客户端不应被迫依赖它们不需要的接口。

Elixir Pattern (ISP)

Elixir 设计模式(ISP)

elixir
undefined
elixir
undefined

BAD - Fat interface

BAD - 臃肿接口

defmodule User do @callback work() :: :ok @callback take_break() :: :ok @callback eat_lunch() :: :ok @callback clock_in() :: :ok @callback clock_out() :: :ok

Not all users need all these

end
defmodule User do @callback work() :: :ok @callback take_break() :: :ok @callback eat_lunch() :: :ok @callback clock_in() :: :ok @callback clock_out() :: :ok

并非所有用户都需要这些方法

end

GOOD - Segregated interfaces

GOOD - 拆分后的接口

defmodule Workable do @callback work() :: :ok end
defmodule Breakable do @callback take_break() :: :ok end
defmodule TimeTrackable do @callback clock_in() :: :ok @callback clock_out() :: :ok end
defmodule Workable do @callback work() :: :ok end
defmodule Breakable do @callback take_break() :: :ok end
defmodule TimeTrackable do @callback clock_in() :: :ok @callback clock_out() :: :ok end

Implement only what you need

仅实现所需的接口

defmodule ContractUser do @behaviour Workable def work(), do: :ok

No time tracking needed

end
undefined
defmodule ContractUser do @behaviour Workable def work(), do: :ok

无需时间追踪功能

end
undefined

TypeScript Pattern (ISP)

TypeScript 设计模式(ISP)

typescript
// BAD - Fat interface
interface User {
  work(): void;
  takeBreak(): void;
  clockIn(): void;
  clockOut(): void;
  receiveBenefits(): void;
  // Not all users need all methods
}

// GOOD - Segregated interfaces
interface Workable {
  work(): void;
}

interface TimeTrackable {
  clockIn(): void;
  clockOut(): void;
}

interface BenefitsEligible {
  receiveBenefits(): void;
}

// Compose only what you need
type FullTimeUser = Workable & TimeTrackable & BenefitsEligible;
type ContractUser = Workable & TimeTrackable;
type TaskUser = Workable;
Ask yourself: "Does this interface force implementations to define unused methods?"
typescript
// BAD - 臃肿接口
interface User {
  work(): void;
  takeBreak(): void;
  clockIn(): void;
  clockOut(): void;
  receiveBenefits(): void;
  // 并非所有用户都需要这些方法
}

// GOOD - 拆分后的接口
interface Workable {
  work(): void;
}

interface TimeTrackable {
  clockIn(): void;
  clockOut(): void;
}

interface BenefitsEligible {
  receiveBenefits(): void;
}

// 仅组合所需的接口
type FullTimeUser = Workable & TimeTrackable & BenefitsEligible;
type ContractUser = Workable & TimeTrackable;
type TaskUser = Workable;
自问: "这个接口是否迫使实现类定义未使用的方法?"

5. Dependency Inversion Principle (DIP)

5. 依赖倒置原则(DIP)

Depend on abstractions, not concretions

依赖于抽象,而非具体实现

Elixir Pattern (DIP)

Elixir 设计模式(DIP)

elixir
undefined
elixir
undefined

BAD - Direct dependency on implementation

BAD - 直接依赖具体实现

defmodule UserService do def create_user(attrs) do PostgresRepo.insert(attrs) # Tightly coupled end end
defmodule UserService do def create_user(attrs) do PostgresRepo.insert(attrs) # 紧耦合 end end

GOOD - Depend on abstraction

GOOD - 依赖抽象

defmodule UserService do def create_user(attrs, repo \ YourApp.Repo) do repo.insert(attrs) # Can inject any Repo implementation end end
defmodule UserService do def create_user(attrs, repo \ YourApp.Repo) do repo.insert(attrs) # 可注入任意Repo实现 end end
// 更优方案 - 使用Behaviour defmodule UserService do @callback create_user(attrs :: map()) :: {:ok, User.t()} | {:error, term()} end
defmodule PostgresUserService do @behaviour UserService def create_user(attrs), do: Repo.insert(User.changeset(attrs)) end

Even better - use behaviour

由应用配置决定具体实现

defmodule UserService do @callback create_user(attrs :: map()) :: {:ok, User.t()} | {:error, term()} end
defmodule PostgresUserService do @behaviour UserService def create_user(attrs), do: Repo.insert(User.changeset(attrs)) end
config :yourapp, :user_service, PostgresUserService
undefined

Application config determines implementation

TypeScript 设计模式(DIP)

config :yourapp, :user_service, PostgresUserService
undefined
typescript
// BAD - 直接依赖
class UserManager {
  private api = new StripeAPI();  // 紧耦合

  async processPayment(amount: number) {
    return this.api.charge(amount);
  }
}

// GOOD - 依赖抽象
interface PaymentAPI {
  charge(amount: number): Promise<Transaction>;
}

class UserManager {
  constructor(private paymentAPI: PaymentAPI) {}  // 依赖注入

  async processPayment(amount: number) {
    return this.paymentAPI.charge(amount);
  }
}

// 使用方式
const stripeAPI: PaymentAPI = new StripeAPI();
const manager = new UserManager(stripeAPI);
自问: "我能否在不修改依赖代码的情况下替换实现?"

TypeScript Pattern (DIP)

应用检查清单

编写新代码前

typescript
// BAD - Direct dependency
class UserManager {
  private api = new StripeAPI();  // Tightly coupled

  async processPayment(amount: number) {
    return this.api.charge(amount);
  }
}

// GOOD - Depend on abstraction
interface PaymentAPI {
  charge(amount: number): Promise<Transaction>;
}

class UserManager {
  constructor(private paymentAPI: PaymentAPI) {}  // Injected

  async processPayment(amount: number) {
    return this.paymentAPI.charge(amount);
  }
}

// Usage
const stripeAPI: PaymentAPI = new StripeAPI();
const manager = new UserManager(stripeAPI);
Ask yourself: "Can I swap implementations without changing dependent code?"
  • 明确单一职责
  • 设计扩展点(Behaviour、接口)
  • 先定义抽象,再实现具体逻辑
  • 保持接口精简且聚焦

Application Checklist

实现过程中

Before writing new code

  • Identify the single responsibility
  • Design for extension points (behaviours, interfaces)
  • Define abstractions before implementations
  • Keep interfaces minimal and focused
  • 每个模块仅有一个变更理由(SRP)
  • 新功能通过扩展实现,而非修改原有代码(OCP)
  • 实现遵守契约(LSP)
  • 接口精简聚焦(ISP)
  • 依赖可注入/配置(DIP)

During implementation

代码评审时

  • Each module has ONE reason to change (SRP)
  • New features extend, don't modify (OCP)
  • Implementations honor contracts (LSP)
  • Interfaces are minimal (ISP)
  • Dependencies are injected/configurable (DIP)
  • 职责是否清晰分离?
  • 能否在不修改现有代码的情况下添加功能?
  • 所有实现是否都遵守契约?
  • 接口是否精简聚焦?
  • 依赖是否已抽象?

During code review

代码库中的常见违规情况

SRP 违规

  • Are responsibilities clearly separated?
  • Can we add features without modifying existing code?
  • Do all implementations fulfill their contracts?
  • Are interfaces focused and minimal?
  • Are dependencies abstracted?
  • GraphQL解析器同时包含业务逻辑(应使用命令处理程序)
  • 组件同时负责数据获取和UI渲染(应使用Hooks+展示组件)

Common Violations in Codebase

OCP 违规

SRP Violation

  • GraphQL resolvers that also contain business logic (use command handlers)
  • Components that fetch data AND render (use hooks + presentation components)
  • 针对类型的长if/else或case语句(应使用Behaviour/多态)
  • 硬编码的提供商逻辑(应使用依赖注入)

OCP Violation

LSP 违规

  • Long if/else or case statements for types (use behaviours/polymorphism)
  • Hardcoded provider logic (use dependency injection)
  • 当基类型本应返回nil/错误元组时,实现类抛出异常
  • 不同实现之间返回类型不一致

LSP Violation

ISP 违规

  • Raising exceptions in implementations when base would return nil/error tuple
  • Changing return types between implementations
  • 臃肿的GraphQL类型要求所有字段(应使用片段)
  • 单体式组件属性(应拆分为聚焦的接口)

ISP Violation

DIP 违规

  • Fat GraphQL types requiring all fields (use fragments)
  • Monolithic component props (split into focused interfaces)
  • 直接调用外部服务(应使用Behaviour包装)
  • 硬编码的Repo调用(应注入仓库)

DIP Violation

与现有技能的集成

适配技能

  • Direct calls to external services (wrap in behaviours)
  • Hardcoded Repo calls (inject repository)
  • boy-scout-rule
    : 改进代码时应用SOLID原则
  • test-driven-development
    : 为每个职责编写测试
  • elixir-code-quality-enforcer
    : Credo可强制执行部分SOLID原则
  • typescript-code-quality-enforcer
    : TypeScript接口支持ISP/DIP

Integration with Existing Skills

谨记

Works with

  • boy-scout-rule
    : Apply SOLID when improving code
  • test-driven-development
    : Write tests for each responsibility
  • elixir-code-quality-enforcer
    : Credo enforces some SOLID principles
  • typescript-code-quality-enforcer
    : TypeScript interfaces support ISP/DIP
SOLID的核心是管理依赖和职责,而非编写更多代码。
优秀的设计源于务实而非教条地应用这些原则。

Remember

SOLID is about managing dependencies and responsibilities, not about creating more code.
Good design emerges from applying these principles pragmatically, not dogmatically.