Pattern 1: Extract Method
```python
# Before: Long method with multiple responsibilities
def process_order(order):
# Validate order
if not order.items:
raise ValueError("Empty order")
if order.total < 0:
raise ValueError("Invalid total")
# Calculate discount
discount = 0
if order.customer.is_premium:
discount = order.total * 0.1
if order.total > 1000:
discount += order.total * 0.05
# Apply discount and save
order.final_total = order.total - discount
order.save()
# After: Extracted methods with single responsibility
def process_order(order):
validate_order(order)
discount = calculate_discount(order)
finalize_order(order, discount)
def validate_order(order):
if not order.items:
raise ValueError("Empty order")
if order.total < 0:
raise ValueError("Invalid total")
def calculate_discount(order) -> float:
discount = 0
if order.customer.is_premium:
discount = order.total * 0.1
if order.total > 1000:
discount += order.total * 0.05
return discount
def finalize_order(order, discount: float):
order.final_total = order.total - discount
order.save()
```
Pattern 2: Replace Magic Numbers with Constants
```javascript
// Before
if (response.status === 200) {
setTimeout(retry, 3000);
if (attempts > 5) {
throw new Error("Max retries exceeded");
}
}
// After
const HTTP_OK = 200;
const RETRY_DELAY_MS = 3000;
const MAX_RETRY_ATTEMPTS = 5;
if (response.status === HTTP_OK) {
setTimeout(retry, RETRY_DELAY_MS);
if (attempts > MAX_RETRY_ATTEMPTS) {
throw new Error("Max retries exceeded");
}
}
```
Pattern 3: Replace Nested Conditionals with Guard Clauses
```python
# Before
def get_payment_amount(employee):
if employee.is_active:
if employee.is_full_time:
if employee.tenure > 5:
return employee.salary * 1.1
else:
return employee.salary
else:
return employee.hourly_rate * employee.hours
else:
return 0
# After
def get_payment_amount(employee):
if not employee.is_active:
return 0
if not employee.is_full_time:
return employee.hourly_rate * employee.hours
if employee.tenure > 5:
return employee.salary * 1.1
return employee.salary
```
Pattern 4: Introduce Parameter Object
```typescript
// Before
function createUser(
firstName: string,
lastName: string,
email: string,
phone: string,
street: string,
city: string,
state: string,
zip: string
) {
// ...
}
// After
interface UserDetails {
name: PersonName;
contact: ContactInfo;
address: Address;
}
interface PersonName {
first: string;
last: string;
}
interface ContactInfo {
email: string;
phone: string;
}
interface Address {
street: string;
city: string;
state: string;
zip: string;
}
function createUser(details: UserDetails) {
// ...
}
```
Pattern 5: Replace Type Code with Polymorphism
```rust
// Before
enum ShapeType { Circle, Rectangle, Triangle }
struct Shape {
shape_type: ShapeType,
radius: f64,
width: f64,
height: f64,
base: f64,
}
impl Shape {
fn area(&self) -> f64 {
match self.shape_type {
ShapeType::Circle => 3.14159 self.radius self.radius,
ShapeType::Rectangle => self.width * self.height,
ShapeType::Triangle => 0.5 self.base self.height,
}
}
}
// After
trait Shape {
fn area(&self) -> f64;
}
struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }
struct Triangle { base: f64, height: f64 }
impl Shape for Circle {
fn area(&self) -> f64 {
3.14159 self.radius self.radius
}
}
impl Shape for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
impl Shape for Triangle {
fn area(&self) -> f64 {
0.5 self.base self.height
}
}
```
Pattern 6: Extract Data Pipeline Stage
```python
# Before: Monolithic processing
def process_data(raw_data):
# Validate
if not raw_data:
raise ValueError("Empty data")
# Clean
cleaned = []
for item in raw_data:
if item.get('status') == 'valid':
cleaned.append(item)
# Transform
transformed = []
for item in cleaned:
transformed.append({
'id': item['id'],
'value': item['raw_value'] * 100,
'timestamp': item['ts']
})
# Aggregate
total = sum(item['value'] for item in transformed)
return {'items': transformed, 'total': total}
# After: Pipeline with composable stages
def validate_input(raw_data):
if not raw_data:
raise ValueError("Empty data")
return raw_data
def filter_valid(data):
return [item for item in data if item.get('status') == 'valid']
def transform_items(data):
return [
{
'id': item['id'],
'value': item['raw_value'] * 100,
'timestamp': item['ts']
}
for item in data
]
def aggregate_results(transformed):
total = sum(item['value'] for item in transformed)
return {'items': transformed, 'total': total}
def process_data(raw_data):
return (raw_data
|> validate_input
|> filter_valid
|> transform_items
|> aggregate_results)
```
Pattern 7: Simplify Test with Builder
```go
// Before
func TestUserRegistration(t *testing.T) {
user := &User{
FirstName: "John",
LastName: "Doe",
Email: "john@example.com",
Phone: "555-0100",
Address: Address{
Street: "123 Main St",
City: "Springfield",
State: "IL",
Zip: "62701",
},
Preferences: Preferences{
NewsletterEnabled: true,
Theme: "dark",
},
CreatedAt: time.Now(),
}
err := RegisterUser(user)
assert.NoError(t, err)
}
// After
func TestUserRegistration(t *testing.T) {
user := NewUserBuilder().
WithName("John", "Doe").
WithEmail("john@example.com").
Build()
err := RegisterUser(user)
assert.NoError(t, err)
}
// Test builder focuses on what matters for each test
func TestUserWithNewsletter(t *testing.T) {
user := NewUserBuilder().WithNewsletter(true).Build()
// ...
}
```