Extending Ruby Code with the Open/Closed Principle

Extending Ruby Code with the Open/Closed Principle

The "Open for extension, but closed for modification" principle, also known as the Open/Closed Principle (OCP), is one of the key concepts in software design and development, particularly within the context of object-oriented programming. This principle states that software entities such as classes, modules, and functions should be open for extension but closed for modification. This means that the behavior of a module can be extended without modifying its source code.

In Ruby, adhering to this principle involves designing classes and modules in a way that allows their behavior to be extended via inheritance, modules, or other means, without changing the existing code. Here’s how you can apply this principle in Ruby:

Example Scenario

Imagine you have a system that calculates the area of various shapes. You start with a base class Shape and want to add new shapes without modifying the existing code.

Step-by-Step Implementation

  1. Define a Base Class with a Common Interface:

class Shape
  def area
    raise NotImplementedError, "This method must be overridden in a subclass"
  end
end        

2. Create Subclasses for Specific Shapes:

class Circle < Shape
  attr_reader :radius

  def initialize(radius)
    @radius = radius
  end

  def area
    Math::PI * @radius ** 2
  end
end

class Rectangle < Shape
  attr_reader :width, :height

  def initialize(width, height)
    @width = width
    @height = height
  end

  def area
    @width * @height
  end
end
        

3. Extend the System by Adding New Shapes without Modifying Existing Code:

class Triangle < Shape
  attr_reader :base, :height

  def initialize(base, height)
    @base = base
    @height = height
  end

  def area
    0.5 * @base * @height
  end
end        

4. Use a Factory or Strategy Pattern to Handle Shape Creation (Optional):

This step can help in managing the creation and handling of different shapes without modifying the existing code.

class ShapeFactory
  def self.create_shape(type, *args)
    case type
    when :circle
      Circle.new(*args)
    when :rectangle
      Rectangle.new(*args)
    when :triangle
      Triangle.new(*args)
    else
      raise "Unknown shape type"
    end
  end
end        

5.Using the Shapes:

shapes = [
  ShapeFactory.create_shape(:circle, 5),
  ShapeFactory.create_shape(:rectangle, 4, 6),
  ShapeFactory.create_shape(:triangle, 3, 7)
]

shapes.each do |shape|
  puts "The area of the #{shape.class} is #{shape.area}"
end
        

Explanation

  • Base Class (Shape): This class provides a common interface (area method) that all shape subclasses must implement. It is "closed for modification" because we do not change this class when adding new shapes.
  • Subclasses (Circle, Rectangle, Triangle): These classes extend the base class by providing specific implementations of the area method. They are "open for extension" because we can add new shapes (e.g., Triangle) without modifying existing classes.
  • Factory (ShapeFactory): This optional step shows how to manage the creation of shapes dynamically, ensuring that the client code does not need to be modified when new shapes are added.

By following this principle, your codebase becomes more maintainable and adaptable to change, as new functionality can be added with minimal impact on the existing system.

要查看或添加评论,请登录

smiley khera(Immediate Joiner)的更多文章

社区洞察

其他会员也浏览了