Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/microwind/ai-skills --skill dry명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | DRY原则 |
| description | Don't Repeat Yourself - 系统中每一个知识点都应该有唯一、权威的表示,避免重复代码和重复逻辑。 |
| license | MIT |
DRY 原则由 Andy Hunt 和 Dave Thomas 在《程序员修炼之道》中提出:系统中每一个知识点都应该有唯一、明确、权威的表示。
核心思想:
重复的三种类型:
// ❌ 代码重复
public class OrderService {
public double calculateTotal(Order order) {
double total = 0;
for (Item item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
}
if (total > 1000) total *= 0.9; // 折扣逻辑
total *= 1.13; // 税率
return total;
}
public double calculateQuote(List<Item> items) {
double total = 0;
for (Item item : items) {
total += item.getPrice() * item.getQuantity(); // 重复!
}
if (total > 1000) total *= 0.9; // 重复!
total *= 1.13; // 重复!
return total;
}
}
// ✅ 消除重复
public class PricingService {
private static final double DISCOUNT_THRESHOLD = 1000;
private static final double DISCOUNT_RATE = 0.9;
private static final double TAX_RATE = 1.13;
public double calculateSubtotal(List<Item> items) {
return items.stream()
.mapToDouble(i -> i.getPrice() * i.getQuantity())
.sum();
}
public double applyDiscountAndTax(double subtotal) {
double discounted = subtotal > DISCOUNT_THRESHOLD
? subtotal * DISCOUNT_RATE : subtotal;
return discounted * TAX_RATE;
}
public double calculateTotal(List<Item> items) {
return applyDiscountAndTax(calculateSubtotal(items));
}
}
# ❌ 代码不同,但逻辑重复(都在验证邮箱)
class UserRegistration:
def register(self, email: str):
if "@" not in email or "." not in email.split("@")[1]:
raise ValueError("无效邮箱")
class Newsletter:
def subscribe(self, email: str):
import re
if not re.match(r'^[\w.]+@[\w.]+\.\w+$', email):
raise ValueError("邮箱格式错误")
# ✅ 统一验证逻辑
class EmailValidator:
@staticmethod
def validate(email: str) -> bool:
import re
return bool(re.match(r'^[\w.]+@[\w.]+\.\w+$', email))
class UserRegistration:
def register(self, email: str):
if EmailValidator.validate(email):
ValueError()
:
():
EmailValidator.validate(email):
ValueError()
// ❌ 同一业务规则在多处定义
// config.ts
const MAX_LOGIN_ATTEMPTS = 5;
// auth.service.ts
if (attempts > 5) { // 硬编码,与 config 不同步
lockAccount();
}
// auth.test.ts
expect(lockAfterAttempts).toBe(5); // 测试中又写了一遍
// ✅ 单一来源
// config.ts
export const AUTH_CONFIG = {
MAX_LOGIN_ATTEMPTS: 5,
} as const;
// auth.service.ts
import { AUTH_CONFIG } from './config';
if (attempts > AUTH_CONFIG.MAX_LOGIN_ATTEMPTS) {
lockAccount();
}
1. 偶然重复:两段代码现在相同但变化原因不同
- 订单折扣和会员折扣恰好都是 9 折
- 未来可能独立变化,强行合并反而有害
2. 跨服务重复:微服务间的 DTO
- 不同服务可能独立演进
- 共享库引入更大的耦合问题
3. 测试代码:测试中的重复有时提高可读性
- 每个测试方法自包含,比抽象更清晰
第1次:直接写
第2次:允许重复,标注 TODO
第3次:重构,提取共同逻辑
不要在重复出现2次时就抽象——你可能还不了解正确的抽象方向
| 重复类型 | 消除方法 | 示例 |
|---|---|---|
| 代码片段重复 | 提取方法 | extractMethod() |
| 类间重复 | 提取基类/工具类 | BaseService, Utils |
| 配置重复 | 集中配置 | config.yaml |
| 数据定义重复 | 代码生成 | OpenAPI → DTO |
| 文档重复 | 从代码生成文档 | JavaDoc |
| 原则 | 关系 |
|---|---|
| SRP | SRP 拆分职责,DRY 合并重复知识 |
| KISS | DRY 有时与 KISS 冲突,过度 DRY 增加复杂性 |
| YAGNI | 不要为了未来可能的 DRY 而提前抽象 |
DRY 核心:每个知识点只有一个权威表示。
实践要点: