用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Objective-Arts/lens-dist --skill legacy命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
基于 SOC 职业分类
| name | legacy |
| description | Legacy code testing patterns from Working Effectively with Legacy Code |
Apply Michael Feathers' techniques for testing and refactoring legacy code.
"Legacy code is code without tests."
It doesn't matter how old or how clean—if it lacks tests, it's legacy code.
"When we change code, we should have tests in place. To put tests in place, we often have to change code."
Solution: Use safe, mechanical refactorings to create seams for testing.
Characterization tests document what the code actually does, not what it should do.
// 1. Write a test that calls the code
@Test
void characterize_calculateDiscount() {
Order order = new Order();
order.setTotal(100.0);
order.setCustomerType("GOLD");
double result = discountCalculator.calculate(order);
// 2. Run it and let it fail
// 3. Use the actual output as the expected value
assertEquals(15.0, result, 0.001);
}
"Preserve behavior first, then change it."
A seam is a place where you can alter behavior without editing the code.
// BEFORE: Hard to test - creates its own dependency
public class OrderProcessor {
public void process(Order order) {
EmailService emailer = new EmailService(); // Untestable!
emailer.send(order.getCustomerEmail(), "Order received");
}
}
// AFTER: Object seam via constructor injection
public class OrderProcessor {
private final EmailService emailer;
public OrderProcessor(EmailService emailer) {
this.emailer = emailer;
}
public void process(Order order) {
emailer.send(order.getCustomerEmail(), "Order received");
}
}
// Test with fake
@Test
void process_sendsEmail() {
FakeEmailService fakeEmail = new FakeEmailService();
OrderProcessor processor = new OrderProcessor(fakeEmail);
processor.process(testOrder);
assertTrue(fakeEmail.wasSentTo("customer@test.com"));
}
Replace a dependency at link/build time.
// Production: uses real database
// Test: link against in-memory database
// C/C++ - use preprocessor for test seams
#ifdef TESTING
#define getCurrentTime() mockTime
#else
#define getCurrentTime() time(NULL)
#endif
"Every seam has an enabling point—a place where you can make the decision to use one behavior or another."
// BEFORE: Untestable - uses system time
public class Scheduler {
public boolean isOverdue(Task task) {
Date now = new Date(); // Hard dependency
return task.getDueDate().before(now);
}
}
// AFTER: Extract to protected method, override in test
public class Scheduler {
public boolean isOverdue(Task task) {
Date now = getCurrentTime();
return task.getDueDate().before(now);
}
protected Date getCurrentTime() {
return new Date();
}
}
// Test subclass
class TestableScheduler extends Scheduler {
private Date fixedTime;
public void setCurrentTime(Date time) {
this.fixedTime = time;
}
@Override
protected Date getCurrentTime() {
return fixedTime;
}
}
// BEFORE
public class Report {
private Database db = Database.getInstance(); // Singleton!
public List<Row> generate() {
return db.query("SELECT * FROM data");
}
}
// AFTER: Parameterize constructor
public class Report {
private final Database db;
public Report() {
this(Database.getInstance());
}
public Report(Database db) { // Seam!
this.db = db;
}
public List<Row> generate() {
return db.query("SELECT * FROM data");
}
}
// BEFORE: Static method - untestable
public class Validator {
public static boolean isValid(String input) {
return Pattern.matches("[A-Z]+", input);
}
}
// AFTER: Keep static for compatibility, add instance method
public class Validator {
public static boolean isValid(String input) {
return new Validator().validate(input);
}
public boolean validate(String input) { // Testable!
return Pattern.matches("[A-Z]+", input);
}
}
When adding new functionality to legacy code:
// BEFORE: Long method, need to add validation
public void processOrder(Order order) {
// ... 100 lines of legacy code ...
// NEW: Add validation here
if (!isValidOrder(order)) { // Sprout!
throw new InvalidOrderException();
}
// ... more legacy code ...
}
// SPROUTED: New method is tested separately
@Test
void isValidOrder_rejectsEmptyItems() {
Order order = new Order();
assertFalse(isValidOrder(order));
}
When the new functionality deserves its own class:
// Legacy code - huge class
public class OrderProcessor {
// ... 2000 lines ...
public void process(Order order) {
// NEW: Sprout entire class for new feature
OrderValidator validator = new OrderValidator();
validator.validate(order);
// ... legacy processing ...
}
}
// New class - fully tested
public class OrderValidator {
public void validate(Order order) {
// Clean, tested code
}
}
When you need to add behavior before/after existing code:
// BEFORE
public void pay(Employee employee, Money amount) {
employee.addToBalance(amount);
}
// AFTER: Wrap with logging
public void pay(Employee employee, Money amount) {
logPayment(employee, amount); // Before
dispatchPay(employee, amount); // Renamed original
}
private void dispatchPay(Employee employee, Money amount) {
employee.addToBalance(amount);
}
private void logPayment(Employee employee, Money amount) {
// New, tested logging
}
For large-scale legacy refactoring:
Goal: Extract OrderValidator class
├── Need to inject dependencies
│ ├── OrderProcessor uses singleton → Parameterize constructor
│ └── Database uses static → Extract interface
└── Need to separate validation logic
└── Validation mixed with persistence → Extract method first
When working with legacy code:
| Situation | Technique |
|---|---|
| Don't know what code does | Characterization test |
| Need to test untestable code | Find/create seam |
| Adding new feature to legacy | Sprout method/class |
| Adding behavior to existing method | Wrap method |
| Hard-coded dependency | Parameterize constructor |
| Static method dependency | Introduce instance delegator |
| Large legacy refactoring | Mikado method |
"Dependency is one of the most critical problems in software development."
"Programming is the art of doing one thing at a time."
"Legacy code is code without tests. Code without tests is bad code."