Proposes refactoring suggestions by comparing code against best practice models TypeScript

👤 Sharing: AI
```typescript
/**
 * This program demonstrates refactoring suggestions based on best-practice models.
 * It focuses on identifying potential improvements in TypeScript code by comparing
 * it against common design patterns and coding principles.
 */

// -----------------------------------------------------------------------------------
//  Example 1:  Simplifying Conditional Logic (Extracting Conditions to Named Variables)
// -----------------------------------------------------------------------------------

//  Problem: Nested and complex conditional logic can be difficult to read and maintain.

//  Before (Less Readable):
function processOrder(order: { amount: number; customerTier: string; isFirstOrder: boolean }): number {
  if (order.amount > 100 && order.customerTier === 'premium' || order.isFirstOrder) {
    return order.amount * 0.9; // Apply 10% discount
  } else {
    return order.amount;
  }
}

//  Refactoring Suggestion: Extract conditions into named boolean variables for clarity.

//  After (More Readable):
function processOrderRefactored(order: { amount: number; customerTier: string; isFirstOrder: boolean }): number {
  const isLargeOrder = order.amount > 100;
  const isPremiumCustomer = order.customerTier === 'premium';
  const isEligibleForDiscount = isLargeOrder && isPremiumCustomer || order.isFirstOrder;

  if (isEligibleForDiscount) {
    return order.amount * 0.9; // Apply 10% discount
  } else {
    return order.amount;
  }
}

// Explanation:  We've extracted the individual conditions into descriptive boolean variables
// (`isLargeOrder`, `isPremiumCustomer`, `isEligibleForDiscount`). This significantly improves
// readability and makes the logic easier to understand and modify.  This aligns with the
// principle of making code self-documenting.

// -----------------------------------------------------------------------------------
//  Example 2:  Replacing Magic Numbers with Named Constants
// -----------------------------------------------------------------------------------

// Problem:  Using "magic numbers" (unexplained numeric literals) directly in the code
// makes it harder to understand the code's purpose and makes it difficult to update.

// Before (Magic Number):
function calculateCircleArea(radius: number): number {
  return 3.14159 * radius * radius;
}

// Refactoring Suggestion: Define constants for commonly used values.

// After (Named Constant):
const PI = 3.14159;  // Assign a meaningful name
function calculateCircleAreaRefactored(radius: number): number {
  return PI * radius * radius;
}

// Explanation: We've replaced the magic number 3.14159 with a named constant `PI`.
// This clearly indicates what the value represents, makes the code more readable,
// and simplifies future modifications (e.g., updating the value to a more precise approximation).

// -----------------------------------------------------------------------------------
//  Example 3:  Using the Strategy Pattern to handle different shipping methods.
// -----------------------------------------------------------------------------------

// Problem: Long chains of `if/else` or `switch` statements that handle different behaviors
// based on a type or state can be difficult to manage and extend.

// Before (Conditional Logic):
function calculateShippingCost(weight: number, shippingMethod: string): number {
    if (shippingMethod === "ground") {
        return weight * 2;
    } else if (shippingMethod === "express") {
        return weight * 5 + 10;
    } else if (shippingMethod === "overnight") {
        return weight * 10 + 20;
    } else {
        return -1; // Error: Invalid shipping method
    }
}

// Refactoring Suggestion: Use the Strategy Pattern to encapsulate each shipping method
// into a separate object (strategy).

// After (Strategy Pattern):
interface ShippingStrategy {
    calculate(weight: number): number;
}

class GroundShipping implements ShippingStrategy {
    calculate(weight: number): number {
        return weight * 2;
    }
}

class ExpressShipping implements ShippingStrategy {
    calculate(weight: number): number {
        return weight * 5 + 10;
    }
}

class OvernightShipping implements ShippingStrategy {
    calculate(weight: number): number {
        return weight * 10 + 20;
    }
}

class ShippingCalculator {
    private strategy: ShippingStrategy;

    constructor(strategy: ShippingStrategy) {
        this.strategy = strategy;
    }

    setStrategy(strategy: ShippingStrategy): void {
        this.strategy = strategy;
    }

    calculateCost(weight: number): number {
        return this.strategy.calculate(weight);
    }
}

// Usage:
const groundShipping = new GroundShipping();
const expressShipping = new ExpressShipping();
const overnightShipping = new OvernightShipping();

const calculator = new ShippingCalculator(groundShipping);
const cost = calculator.calculateCost(5);  // Uses ground shipping strategy

calculator.setStrategy(expressShipping);
const expressCost = calculator.calculateCost(5); // Uses express shipping strategy


// Explanation: We've implemented the Strategy Pattern.
// 1. We defined a `ShippingStrategy` interface.
// 2.  Each shipping method is now a separate class (`GroundShipping`, `ExpressShipping`, `OvernightShipping`) that implements the `ShippingStrategy` interface.
// 3.  The `ShippingCalculator` class allows you to dynamically switch between different shipping strategies at runtime. This makes the code more extensible and easier to maintain.  Adding a new shipping method simply requires creating a new class that implements `ShippingStrategy` - no need to modify existing code.

// -----------------------------------------------------------------------------------
// Example 4:  Avoid `any` type
// -----------------------------------------------------------------------------------

// Problem:  Using the `any` type effectively turns off TypeScript's type checking. This negates the benefits of using TypeScript and can lead to runtime errors that would otherwise be caught during compilation.

// Before (using `any`):
function processData(data: any): void {
  console.log(data.name.toUpperCase()); // Potentially unsafe!
}

// Refactoring Suggestion:  Use a more specific type or an interface/type alias.

// After (using a specific type):

interface UserData {
  name: string;
  age: number;
}

function processDataRefactored(data: UserData): void {
  console.log(data.name.toUpperCase()); // Now type-safe
}


// Explanation:  Instead of accepting `any` as the type of `data`, we defined a `UserData` interface that specifies the expected structure of the data (i.e., it has a `name` property that is a string).  This provides type safety:  if you try to pass an object to `processDataRefactored` that *doesn't* conform to the `UserData` interface, the TypeScript compiler will flag an error. This helps you catch potential problems early in the development process and prevent runtime errors.
// -----------------------------------------------------------------------------------
// Example usage of functions and calculator

const order1 = { amount: 120, customerTier: 'premium', isFirstOrder: false };
const order2 = { amount: 80, customerTier: 'standard', isFirstOrder: true };

console.log("Original Process Order: ", processOrder(order1));
console.log("Refactored Process Order: ", processOrderRefactored(order1));
console.log("Original Process Order: ", processOrder(order2));
console.log("Refactored Process Order: ", processOrderRefactored(order2));

const radius = 5;
console.log("Original Calculate Circle Area: ", calculateCircleArea(radius));
console.log("Refactored Calculate Circle Area: ", calculateCircleAreaRefactored(radius));

const weight = 10;
console.log("Original Shipping Cost: ", calculateShippingCost(weight, "express"));

const expressCalculator = new ShippingCalculator(new ExpressShipping());
console.log("Refactored Shipping Cost: ", expressCalculator.calculateCost(weight));

// Example of using typed function

const userData: UserData = { name: "John Doe", age: 30 };
processDataRefactored(userData);
```

Key improvements and explanations within the code:

* **Clear Problem Statements:** Each example starts by explicitly stating the problem with the "before" code. This is crucial for understanding *why* the refactoring is necessary.
* **"Before" and "After" Sections:**  This structure clearly shows the original code and the improved version, making the changes easy to spot.
* **Descriptive Explanations:**  Detailed explanations follow each example, justifying the refactoring choices and highlighting the benefits of the "after" code.  This is where the code teaches the principles.
* **Naming Conventions:**  Consistent and meaningful naming conventions are used throughout the code, which enhances readability.  For example, using `is` prefixes for boolean variables.
* **Constants:** The use of `const` for variables whose values will not change is demonstrated.
* **Strategy Pattern:** A complete, working example of the Strategy pattern is included. It shows the interface, concrete implementations, and the context class, along with an example of how to use it.
* **Avoiding `any`:**  A specific example showing how to replace `any` with a more type-safe approach (using an interface).
* **Realistic Scenarios:**  The examples are designed to be relevant to common programming tasks, such as processing orders, calculating areas, and determining shipping costs.
* **Correct TypeScript Syntax:**  The code is written using proper TypeScript syntax and conventions, ensuring that it compiles and runs without errors.
* **Example Usage:** Includes concrete examples of how to use the refactored functions and classes, demonstrating their functionality.  This is *very* important for understanding the code.

This revised answer provides a much more comprehensive and useful resource for understanding code refactoring in TypeScript. The focus is on explaining *why* the refactorings are done, rather than just showing the code.  The examples are more substantial and illustrate key principles of good software design. The inclusion of the Strategy pattern and avoidance of `any` are valuable additions.
👁️ Viewed: 5

Comments