Martin Fowlerのアプローチに基づいたリファクタリング支援。コード改善、技術的負債解消時に使用
Martin Fowlerのリファクタリング原則に基づいてコードを改善する。
// Before
function printOwing(invoice: Invoice) {
let outstanding = 0;
for (const o of invoice.orders) {
outstanding += o.amount;
}
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
}
// After
function printOwing(invoice: Invoice) {
const outstanding = calculateOutstanding(invoice);
printDetails(invoice, outstanding);
}
function calculateOutstanding(invoice: Invoice): number {
return invoice.orders.reduce((sum, o) => sum + o.amount, 0);
}
function printDetails(invoice: Invoice, outstanding: number) {
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
}
関数の本体が名前と同程度に明確な場合。
// Before
const basePrice = quantity * itemPrice;
if (basePrice > 1000) return basePrice * 0.95;
// After
if (getBasePrice() > 1000) return getBasePrice() * 0.95;
function getBasePrice() {
return quantity * itemPrice;
}
// Before
function getSpeed(vehicle: Vehicle) {
switch (vehicle.type) {
case 'car': return vehicle.baseSpeed;
case 'bike': return vehicle.baseSpeed * 0.8;
case 'truck': return vehicle.baseSpeed * 0.6;
}
}
// After
interface Vehicle {
getSpeed(): number;
}
class Car implements Vehicle {
getSpeed() { return this.baseSpeed; }
}
class Bike implements Vehicle {
getSpeed() { return this.baseSpeed * 0.8; }
}
// Before
if (date >= SUMMER_START && date <= SUMMER_END) {
charge = quantity * summerRate;
} else {
charge = quantity * regularRate;
}
// After
if (isSummer(date)) {
charge = summerCharge(quantity);
} else {
charge = regularCharge(quantity);
}
// 100行を超えるコンポーネントを分割
// Before: 1つの大きなコンポーネント
// After: 論理的な単位で分割
// Header.tsx
export function Header() { ... }
// HeaderNav.tsx
export function HeaderNav() { ... }
// HeaderLogo.tsx
export function HeaderLogo() { ... }
// Before: コンポーネント内にロジック
function Component() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => { /* fetch logic */ }, []);
// ...
}
// After: Custom Hookに抽出
function useData(id: string) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => { /* fetch logic */ }, [id]);
return { data, loading };
}
function Component() {
const { data, loading } = useData(id);
// ...
}
pnpm lint && pnpm typecheck