solid-principles
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSOLID 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
undefinedelixir
undefinedBAD - 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
undefineddefmodule 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
undefinedTypeScript 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
undefinedelixir
undefinedDefine 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
undefineddef charge(provider_module, amount, token) do
provider_module.process_payment(amount, token)
end
undefinedTypeScript 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
undefinedelixir
undefinedBAD - 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
undefineddefmodule PaymentCalculator do
def calculate_total(items) when is_list(items) do
Enum.sum(items) # 空列表时返回0
end
end
undefinedTypeScript 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
undefinedelixir
undefinedBAD - 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
undefineddefmodule ContractUser do
@behaviour Workable
def work(), do: :ok
无需时间追踪功能
end
undefinedTypeScript 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
undefinedelixir
undefinedBAD - 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
undefinedApplication config determines implementation
TypeScript 设计模式(DIP)
config :yourapp, :user_service, PostgresUserService
undefinedtypescript
// 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)
- : 改进代码时应用SOLID原则
boy-scout-rule - : 为每个职责编写测试
test-driven-development - : Credo可强制执行部分SOLID原则
elixir-code-quality-enforcer - : TypeScript接口支持ISP/DIP
typescript-code-quality-enforcer
Integration with Existing Skills
谨记
Works with
—
- : Apply SOLID when improving code
boy-scout-rule - : Write tests for each responsibility
test-driven-development - : Credo enforces some SOLID principles
elixir-code-quality-enforcer - : TypeScript interfaces support ISP/DIP
typescript-code-quality-enforcer
SOLID的核心是管理依赖和职责,而非编写更多代码。
优秀的设计源于务实而非教条地应用这些原则。
Remember
—
SOLID is about managing dependencies and responsibilities, not about
creating more code.
Good design emerges from applying these principles pragmatically, not
dogmatically.
—