Code Examples 3-4: Advanced Use Cases

In this section, we’ll explore more advanced use cases, demonstrating how composition can simplify complex systems and make them more maintainable.

Example 3: Content Management System (CMS)

Content management systems often require rendering different page types, such as blog posts and product pages. Let’s refactor this use case to composition.

Inheritance-Based Implementation

class Page
  def initialize(title, content)
    @title = title
    @content = content
  end

  def render
    "

#{@title}

#{@content}

" end end class BlogPage < Page def render "

#{@title}

#{@content}

" end end class ProductPage < Page def render "

#{@title}

#{@content}

" end end

Each subclass overrides the `render` method, duplicating logic and creating tight coupling between page types and the parent class.

Refactored to Composition

class PageRenderer
  def initialize(template)
    @template = template
  end

  def render(title, content)
    @template.gsub("{title}", title).gsub("{content}", content)
  end
end

blog_template = "

{title}

{content}

" product_template = "

{title}

{content}

" blog_renderer = PageRenderer.new(blog_template) product_renderer = PageRenderer.new(product_template) puts blog_renderer.render("My Blog", "Welcome to my blog!") # Outputs:

My Blog

Welcome to my blog!

puts product_renderer.render("Product A", "This is Product A.") # Outputs:

Product A

This is Product A.

This approach separates rendering logic from page-specific details, allowing templates to be customized independently of the `PageRenderer` class.

Example 4: E-Commerce Discount System

In e-commerce systems, discounts can vary by product, category, or promotion. Let’s refactor an inheritance-based discount system to use composition.

Inheritance-Based Implementation

class Discount
  def apply(price)
    price
  end
end

class PercentageDiscount < Discount
  def initialize(percent)
    @percent = percent
  end

  def apply(price)
    price - (price * @percent / 100.0)
  end
end

class FixedAmountDiscount < Discount
  def initialize(amount)
    @amount = amount
  end

  def apply(price)
    price - @amount
  end
end
        

Each discount type requires its own subclass, leading to a proliferation of classes as more discount types are added.

Refactored to Composition

class DiscountStrategy
  def initialize(strategy)
    @strategy = strategy
  end

  def apply(price)
    @strategy.call(price)
  end
end

percentage_discount = lambda { |price| price - (price * 10 / 100.0) }
fixed_discount = lambda { |price| price - 20 }

percentage_strategy = DiscountStrategy.new(percentage_discount)
fixed_strategy = DiscountStrategy.new(fixed_discount)

puts percentage_strategy.apply(100) # Outputs 90
puts fixed_strategy.apply(100) # Outputs 80
        

With this approach, discount logic is decoupled from the main class and can be defined inline or reused across multiple instances.

Conclusion

These examples demonstrate how composition can replace rigid inheritance structures, making systems more modular and maintainable. By focusing on reusable components, we can simplify complex systems and adapt them to changing requirements with minimal effort.

Continue to Conclusion