Introduction

Welcome to the very first lesson of the "Clean Code with Multiple Classes" course! 🎉 This course aims to guide you in writing code that's easy to understand, maintain, and enhance. Within the broader scope of clean coding, effective class collaboration is crucial for building well-structured applications. In this lesson, we will delve into the intricacies of class collaboration and coupling — key factors that can make or break the maintainability of your software. Specifically, we'll address some common "code smells" that indicate problems in class interactions and explore ways to resolve them.

Overview of Class Collaboration Challenges

Let's dive into the challenges of class collaboration by focusing on four common code smells:

  • Feature Envy: Occurs when a method in one class is overly interested in methods or data in another class.
  • Inappropriate Intimacy: Describes a situation where two classes are too closely interconnected, sharing private details.
  • Message Chains: Refers to sequences of method calls across several objects, indicating a lack of clear abstraction.
  • Middle Man: Exists when a class mainly delegates its behavior to another class without adding functionality.

Understanding these code smells will enable you to improve your class designs, resulting in cleaner and more maintainable code.

Problems Arising During Class Collaboration

These code smells can significantly impact system design and maintainability. Let's consider their implications:

  • They can lead to tightly coupled classes, making them difficult to modify or extend. 🔧
  • Code readability decreases, as it becomes unclear which class is responsible for which functionality.

Addressing these issues often results in code that's not only easier to read but also more flexible and scalable. Tackling these problems can markedly enhance software architecture, making it more robust and adaptable.

Feature Envy

Feature Envy occurs when a method in one class is more interested in the fields or methods of another class than its own. Here's an example in TypeScript:

class ShoppingCart {
    private items: Item[] = [];

    public calculateTotalPrice(): number {
        return this.items.reduce((total, item) => total + item.getPrice() * item.getQuantity(), 0);
    }
}

class Item {
    private price: number;
    private quantity: number;

    constructor(price: number, quantity: number) {
        this.price = price;
        this.quantity = quantity;
    }

    public getPrice(): number {
        return this.price;
    }

    public getQuantity(): number {
        return this.quantity;
    }
}

In this scenario, calculateTotalPrice() in ShoppingCart overly accesses data from Item, indicating feature envy.

To refactor, consider moving the logic to the Item class:

class ShoppingCart {
    private items: Item[] = [];

    public calculateTotalPrice(): number {
        return this.items.reduce((total, item) => total + item.calculateTotal(), 0);
    }
}

class Item {
    private price: number;
    private quantity: number;

    constructor(price: number, quantity: number) {
        this.price = price;
        this.quantity = quantity;
    }

    public calculateTotal(): number {
        return this.price * this.quantity;
    }
}

Now, each Item calculates its own total, reducing dependency and distributing responsibility appropriately. ✔️

Inappropriate Intimacy
Message Chains

Message Chains occur when classes need to traverse multiple objects to access the methods they require. Here's a demonstration in TypeScript:

class User {
    private address: Address;

    constructor(address: Address) {
        this.address = address;
    }

    public getAddress(): Address {
        return this.address;
    }
}

class Address {
    private zipCode: ZipCode;

    constructor(zipCode: ZipCode) {
        this.zipCode = zipCode;
    }

    public getZipCode(): ZipCode {
        return this.zipCode;
    }
}

class ZipCode {
    public getPostalCode(): string {
        return "90210";
    }
}

// Usage
const user = new User(new Address(new ZipCode()));
console.log(user.getAddress().getZipCode().getPostalCode());

The chain user.getAddress().getZipCode().getPostalCode() illustrates this problem.

To simplify, encapsulate the access within methods:

class User {
    private address: Address;

    constructor(address: Address) {
        this.address = address;
    }

    public getUserPostalCode(): string {
        return this.address.getPostalCode();
    }
}

class Address {
    private zipCode: ZipCode;

    constructor(zipCode: ZipCode) {
        this.zipCode = zipCode;
    }

    public getPostalCode(): string {
        return this.zipCode.getPostalCode();
    }
}

// Usage
const user = new User(new Address(new ZipCode()));
console.log(user.getUserPostalCode());

This adjustment makes the User class responsible for retrieving its postal code, creating a clearer and more direct interface. 📬

Middle Man

A Middle Man problem occurs when a class primarily exists to delegate its functionalities. Here's an example in TypeScript:

class Controller {
    private service: Service;

    constructor(service: Service) {
        this.service = service;
    }

    public execute(): void {
        this.service.performAction();
    }
}

class Service {
    public performAction(): void {
        // Action performed
    }
}

The Controller doesn't do much beyond delegating to Service.

To refactor, simplify delegation or reassign responsibilities:

class Service {
    public performAction(): void {
        // Action performed
    }
}

// Usage
const service = new Service();
service.performAction();

By removing the unnecessary middle man, the design becomes more streamlined and efficient. 🔥

Summary and Practice Heads-Up

In this lesson, you've explored several code smells associated with suboptimal class collaboration and coupling, including Feature Envy, Inappropriate Intimacy, Message Chains, and Middle Man. By identifying and refactoring these smells, you can elevate your code's clarity and maintainability.

Get ready to put these concepts into practice with upcoming exercises, where you'll identify and refactor code smells, strengthening your skills. Keep striving for cleaner, more effective code! 🌟

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal