Lesson 1
Clean Code with Multiple Classes in Ruby
Introduction

Welcome to the very first lesson of the "Clean Code with Multiple Classes in Ruby" 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 Ruby:

Ruby
1class ShoppingCart 2 def initialize 3 @items = [] 4 end 5 6 def calculate_total_price 7 total = 0 8 @items.each do |item| 9 total += item.price * item.quantity 10 end 11 total 12 end 13end 14 15class Item 16 attr_accessor :price, :quantity 17 18 def initialize(price, quantity) 19 @price = price 20 @quantity = quantity 21 end 22end

In this scenario, calculate_total_price in ShoppingCart overly accesses data from Item, indicating feature envy.

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

Ruby
1class ShoppingCart 2 def initialize 3 @items = [] 4 end 5 6 def calculate_total_price 7 total = 0 8 @items.each do |item| 9 total += item.calculate_total 10 end 11 total 12 end 13end 14 15class Item 16 attr_accessor :price, :quantity 17 18 def initialize(price, quantity) 19 @price = price 20 @quantity = quantity 21 end 22 23 def calculate_total 24 price * quantity 25 end 26end

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

Inappropriate Intimacy

Inappropriate Intimacy occurs when a class is overly dependent on the internal details of another class. Here's an example in Ruby:

Ruby
1class Library 2 def initialize(book) 3 @book = book 4 end 5 6 def print_book_details 7 puts "Title: #{@book.title}" 8 puts "Author: #{@book.author}" 9 end 10end 11 12class Book 13 attr_accessor :title, :author 14 15 def initialize(title, author) 16 @title = title 17 @author = author 18 end 19end

In this scenario, the Library class relies too heavily on the details of the Book class, demonstrating inappropriate intimacy.

To refactor, allow the Book class to handle its own representation:

Ruby
1class Library 2 def initialize(book) 3 @book = book 4 end 5 6 def print_book_details 7 puts @book.details 8 end 9end 10 11class Book 12 attr_accessor :title, :author 13 14 def initialize(title, author) 15 @title = title 16 @autor = author 17 end 18 19 def details 20 "Title: #{title}\nAuthor: #{author}" 21 end 22end

This adjustment enables Book to encapsulate its own details, encouraging better encapsulation and separation of concerns. 🛡️

Message Chains

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

Ruby
1class User 2 attr_accessor :address 3 4 def initialize(address) 5 @address = address 6 end 7end 8 9class Address 10 attr_accessor :zip_code 11 12 def initialize(zip_code) 13 @zip_code = zip_code 14 end 15end 16 17class ZipCode 18 def self.postal_code 19 "90210" 20 end 21end 22 23# Usage 24user = User.new(Address.new(ZipCode)) 25postal_code = user.address.zip_code.postal_code

The chain user.address.zip_code.postal_code illustrates this problem.

To simplify, encapsulate the access within methods:

Ruby
1class User 2 attr_accessor :address 3 4 def initialize(address) 5 @address = address 6 end 7 8 def user_postal_code 9 address.postal_code 10 end 11end 12 13class Address 14 attr_accessor :zip_code 15 16 def initialize(zip_code) 17 @zip_code = zip_code 18 end 19 20 def postal_code 21 zip_code.postal_code 22 end 23end 24 25class ZipCode 26 def self.postal_code 27 "90210" 28 end 29end 30 31# Usage 32user = User.new(Address.new(ZipCode)) 33postal_code = user.user_postal_code

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 Ruby:

Ruby
1class Controller 2 def initialize(service) 3 @service = service 4 end 5 6 def execute 7 @service.perform_action 8 end 9end 10 11class Service 12 def perform_action 13 # Action performed 14 end 15end

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

To refactor, simplify delegation or reassign responsibilities:

Ruby
1class Service 2 def perform_action 3 # Action performed 4 end 5end 6 7# Usage 8service = Service.new 9service.perform_action

By removing the unnecessary middleman, 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! 🌟

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.