solid-principles by thebushidocollective/han
npx skills add https://github.com/thebushidocollective/han --skill solid-principles应用 SOLID 设计原则,构建可维护、灵活的代码架构。
# 不好 - 多重职责
defmodule UserManager do
def create_user(attrs) do
# 创建用户
# 发送欢迎邮件
# 记录分析数据
# 更新缓存
end
end
# 好 - 单一职责
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
// 不好 - 多重职责
class UserComponent {
render() { /* UI */ }
fetchData() { /* API */ }
formatDate() { /* 格式化 */ }
validateInput() { /* 验证 */ }
}
// 好 - 单一职责
function UserProfile({ user }: Props) {
return <View>{/* 仅 UI */}</View>;
}
function useUserData(id: string) {
// 仅数据获取
}
function formatUserDate(date: Date): string {
// 仅格式化
}
自问: "这个模块做的唯一一件事是什么?"
软件实体应对扩展开放,对修改关闭。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
# 定义接口
defmodule PaymentProvider do
@callback process_payment(amount :: Money.t(), token :: String.t()) ::
{:ok, transaction :: map()} | {:error, reason :: String.t()}
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
# 使用 - 添加新提供商无需更改此代码
def charge(provider_module, amount, token) do
provider_module.process_payment(amount, token)
end
// 不好 - 添加新类型需要修改
function renderItem(item: Item) {
if (item.type === 'gig') {
return <TaskCard />;
} else if (item.type === 'shift') {
return <WorkPeriodCard />;
}
// 必须修改此函数以支持新类型
}
// 好 - 通过属性扩展
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} />;
}
自问: "我能否在不更改现有代码的情况下添加新功能?"
# 不好 - 违反 LSP (基类型会返回时却引发异常)
defmodule PaymentCalculator do
def calculate_total(items) when length(items) > 0 do
Enum.sum(items)
end
# 缺少子句 - 空列表会引发异常
end
# 好 - 遵守契约
defmodule PaymentCalculator do
def calculate_total(items) when is_list(items) do
Enum.sum(items) # 空列表返回 0
end
end
// 不好 - 违反 LSP
class Bird {
fly(): void { /* 飞翔 */ }
}
class Penguin extends Bird {
fly(): void {
throw new Error('Penguins cannot fly'); // 破坏契约
}
}
// 好 - 正确的抽象
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 { /* 游泳 */ }
}
自问: "我能否用其父类/接口替换此对象而不破坏行为?"
客户端不应被迫依赖其不使用的接口。
# 不好 - 臃肿的接口
defmodule User do
@callback work() :: :ok
@callback take_break() :: :ok
@callback eat_lunch() :: :ok
@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
# 仅实现所需部分
defmodule ContractUser do
@behaviour Workable
def work(), do: :ok
# 不需要时间追踪
end
// 不好 - 臃肿的接口
interface User {
work(): void;
takeBreak(): void;
clockIn(): void;
clockOut(): void;
receiveBenefits(): void;
// 并非所有用户都需要所有方法
}
// 好 - 隔离的接口
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;
自问: "这个接口是否强制实现类定义未使用的方法?"
# 不好 - 直接依赖具体实现
defmodule UserService do
def create_user(attrs) do
PostgresRepo.insert(attrs) # 紧耦合
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
# 应用配置决定实现
config :yourapp, :user_service, PostgresUserService
// 不好 - 直接依赖
class UserManager {
private api = new StripeAPI(); // 紧耦合
async processPayment(amount: number) {
return this.api.charge(amount);
}
}
// 好 - 依赖抽象
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);
自问: "我能否在不更改依赖代码的情况下替换实现?"
boy-scout-rule: 改进代码时应用 SOLIDtest-driven-development: 为每个职责编写测试elixir-code-quality-enforcer: Credo 强制执行部分 SOLID 原则typescript-code-quality-enforcer: TypeScript 接口支持 ISP/DIPSOLID 是关于管理依赖和职责,而不是创建更多代码。
良好的设计源于务实而非教条地应用这些原则。
每周安装数
111
代码仓库
GitHub 星标数
121
首次出现
2026年1月22日
安全审计
安装于
codex101
opencode101
gemini-cli100
github-copilot96
cursor91
amp85
Apply SOLID design principles for maintainable, flexible code architecture.
# BAD - Multiple responsibilities
defmodule UserManager do
def create_user(attrs) do
# Creates user
# Sends welcome email
# Logs to analytics
# Updates cache
end
end
# GOOD - Single responsibility
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
// 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?"
Software entities should be open for extension, closed for modification.
# Define interface
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
# Usage - add new providers without changing this code
def charge(provider_module, amount, token) do
provider_module.process_payment(amount, token)
end
// 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?"
# BAD - Violates LSP (raises when base type would return)
defmodule PaymentCalculator do
def calculate_total(items) when length(items) > 0 do
Enum.sum(items)
end
# Missing clause - raises on empty list
end
# GOOD - Honors contract
defmodule PaymentCalculator do
def calculate_total(items) when is_list(items) do
Enum.sum(items) # Returns 0 for empty list
end
end
// 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?"
Clients should not be forced to depend on interfaces they don't use.
# BAD - Fat interface
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
# GOOD - Segregated interfaces
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
// 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?"
# BAD - Direct dependency on implementation
defmodule UserService do
def create_user(attrs) do
PostgresRepo.insert(attrs) # Tightly coupled
end
end
# GOOD - Depend on abstraction
defmodule UserService do
def create_user(attrs, repo \\ YourApp.Repo) do
repo.insert(attrs) # Can inject any Repo implementation
end
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
# Application config determines implementation
config :yourapp, :user_service, PostgresUserService
// 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?"
boy-scout-rule: Apply SOLID when improving codetest-driven-development: Write tests for each responsibilityelixir-code-quality-enforcer: Credo enforces some SOLID principlestypescript-code-quality-enforcer: TypeScript interfaces support ISP/DIPSOLID is about managing dependencies and responsibilities, not about creating more code.
Good design emerges from applying these principles pragmatically, not dogmatically.
Weekly Installs
111
Repository
GitHub Stars
121
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex101
opencode101
gemini-cli100
github-copilot96
cursor91
amp85
冲刺回顾模板:敏捷团队回顾会议指南与模板(开始-停止-继续/愤怒-悲伤-高兴/4Ls)
10,400 周安装