#{@title}
#{@content}
In this section, we’ll explore more advanced use cases, demonstrating how composition can simplify complex systems and make them more maintainable.
Content management systems often require rendering different page types, such as blog posts and product pages. Let’s refactor this use case to composition.
class Page def initialize(title, content) @title = title @content = content end def render "#{@title}
#{@content}
" end end class BlogPage < Page def render "" end end class ProductPage < Page def render " #{@title}
#{@content}
" end end#{@title}
#{@content}
Each subclass overrides the `render` method, duplicating logic and creating tight coupling between page types and the parent class.
class PageRenderer def initialize(template) @template = template end def render(title, content) @template.gsub("{title}", title).gsub("{content}", content) end end blog_template = "" 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:{title}
{content}
puts product_renderer.render("Product A", "This is Product A.") # Outputs: My Blog
Welcome to my blog!
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.
In e-commerce systems, discounts can vary by product, category, or promotion. Let’s refactor an inheritance-based discount system to use composition.
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.
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.
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