1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: Anti-patterns = common bad practices. God Object = class does everything (SRP violation), Spaghetti Code = no structure, tangled, Golden Hammer = use one pattern for everything, Premature Optimization = optimize before measuring, Circular Dependencies = A depends on B, B depends on A.
- Best practices: YAGNI (You Aren't Gonna Need It), KISS (Keep It Simple), measure before optimizing, refactor to patterns (don't force them), balance complexity vs simplicity!
God Object = Swiss Army knife trying to be every tool (does nothing well). Spaghetti Code = tangled earbuds (mess). Golden Hammer = using hammer for every problem (even screws!). Premature Optimization = buying sports car for grocery shopping!
2. Conceptual Clarity (The "Simple" Tier)
š” The Analogy
- God Object: Control freak (does everyone's job)
- Spaghetti Code: Messy room (can't find anything)
- Golden Hammer: One-trick pony
- YAGNI: Don't build basement bomb shelter (probably won't need it)
3. Technical Mastery (The "Deep Dive")
java
// ===========================================
// ANTI-PATTERNS
// ===========================================
// ā 1. GOD OBJECT
class UserManager {
// Violates SRP - too many responsibilities!
void createUser() { /* ... */ }
void deleteUser() { /* ... */ }
void validateUser() { /* ... */ }
void sendEmail() { /* ... */ }
void logActivity() { /* ... */ }
void connectToDatabase() { /* ... */ }
void generateReport() { /* ... */ }
// 100+ methods...
}
// ā
FIX: Separate responsibilities
class UserService {
private UserValidator validator;
private UserRepository repository;
private EmailService emailService;
private Logger logger;
}
// ā 2. SPAGHETTI CODE
class OrderProcessor {
void process(Order order) {
if (order.isValid()) {
if (order.hasItems()) {
if (order.getTotalreateUser() < 1000) {
// Nested if statements...
if (order.getCustomer() != null) {
// More nesting...
}
}
}
}
// No structure, hard to follow!
}
}
// ā
FIX: Extract methods, use early returns
class OrderProcessor {
void process(Order order) {
validateOrder(order);
processItems(order);
applyDiscounts(order);
finalizeOrder(order);
}
private void validateOrder(Order order) {
if (!order.isValid()) {
throw new IllegalArgumentException("Invalid order");
}
}
}
// ā 3. GOLDEN HAMMER (using Singleton everywhere)
class DatabaseConnection {
private static DatabaseConnection instance; // Singleton
static DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
}
class ConfigManager {
private static ConfigManager instance; // Another Singleton!
// ...
}
class Logger {
private static Logger instance; // Yet another Singleton!
// Problem: Overusing Singleton makes testing hard!
}
// ā
FIX: Use patterns appropriately
// Singleton for truly global resources only (logger, config)
// Use Dependency Injection for others
// ā 4. PREMATURE OPTIMIZATION
class DataProcessor {
// Overly complex optimization without measuring!
int[] complexCache = new int[10000];
Map<String, WeakReference<Object>> pool = new WeakHashMap<>();
void process(List<Data> data) {
// Complex caching, pooling, etc.
// But... is it even a bottleneck?
}
}
// ā
FIX: Measure first, optimize later
class DataProcessor {
void process(List<Data> data) {
// Simple, readable code first
for (Data d : data) {
processItem(d);
}
// Profile ā identify bottleneck ā THEN optimize
}
}
// ā 5. CIRCULAR DEPENDENCIES
class UserService {
private OrderService orderService; // Depends on OrderService
}
class OrderService {
private UserService userService; // Depends on UserService!
// Circular dependency!
}
// ā
FIX: Introduce abstraction or mediator
interface UserProvider {
User getUser(int id);
}
class UserService implements UserProvider {
// No dependency on OrderService
}
class OrderService {
private UserProvider userProvider; // Depends on abstraction
}
// ===========================================
// BEST PRACTICES
// ===========================================
// 1. YAGNI (You Aren't Gonna Need It)
// ā BAD: Over-engineering
interface Animal {
void eat();
void sleep();
void fly(); // ā Not all animals fly!
void swim(); // ā Not all animals swim!
void layEggs(); // ā Not all animals lay eggs!
}
// ā
GOOD: Start simple
interface Animal {
void eat();
void sleep();
}
// Add specialized interfaces only when needed:
interface Flyable {
void fly();
}
// 2. KISS (Keep It Simple, Stupid)
// ā BAD: Overuse of patterns
class SimpleCalculator {
// Using Factory + Strategy + Builder for simple addition!
private OperationFactory factory = new OperationFactory();
private OperationStrategy strategy = new AdditionStrategy();
private CalculatorBuilder builder = new CalculatorBuilder();
int add(int a, int b) {
Operation op = factory.createOperation(strategy);
Calculator calc = builder.withOperation(op).build();
return calc.execute(a, b);
}
}
// ā
GOOD: Simple solution
class SimpleCalculator {
int add(int a, int b) {
return a + b; // That's it!
}
}
// 3. Refactor TO Patterns (don't force them)
// Start simple:
class ReportGenerator {
void generate(String type) {
if ("PDF".equals(type)) {
// Generate PDF
} else if ("Excel".equals(type)) {
// Generate Excel
}
}
}
// When complexity grows, refactor to Strategy:
interface ReportStrategy {
void generate();
}
class ReportGenerator {
private ReportStrategy strategy;
void setStrategy(ReportStrategy strategy) {
this.strategy = strategy;
}
void generate() {
strategy.generate();
}
}5. The Comparison & Decision Layer
| Anti-Pattern | Fix |
|---|---|
| God Object | Apply SRP, separate responsibilities |
| Spaghetti Code | Extract methods, add structure |
| Golden Hammer | Choose right tool for job |
| Premature Optimization | Measure first, optimize second |
| Circular Dependencies | Introduce interfaces, mediators |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "When should you NOT use design patterns?" Answer:
- Simple problems: Don't use Strategy for 2 options
- Over-engineering: Patterns add complexity
- YAGNI: Don't implement for future "might need"
- Performance-critical: Some patterns add overhead
java
// Don't use patterns here:
class Greeter {
String greet(String name) {
return "Hello, " + name; // Simple! No pattern needed
}
}
// DO use patterns here:
// - Multiple greeting strategies (Strategy)
// - Extensible greetings (Open/Closed)
// - Complex greeting rules (Chain of Responsibility)Pro-Tips:
- Pattern selection checklist:
text
ā
Ask:
- Does it solve a real problem?
- Is the code complex enough to warrant it?
- Will others understand it?
- Does it improve maintainability?
ā Don't use if:
- Adds unnecessary complexity
- Team doesn't understand pattern
- Simple solution exists
- "Just because it's cool"- Balance simplicity vs flexibility:
java
// V1: Start simple
void process() {
// Hardcoded logic
}
// V2: If requirements change, refactor
void process(Strategy strategy) {
strategy.execute();
}
// Don't jump to V2 until needed!