# Inheritance, Visibility, Struct and Data — Ruby

Source: https://www.geekswithgeeks.com/en/ruby/o-inherit

> Use inheritance and method visibility, and create value objects with Struct and Data.

## Inheritance and lightweight objects

A class inherits from **one** superclass with `class SavingsAccount < Account`; it can override methods and call the parent's version with **`super`** (bare `super` forwards the same arguments, `super()` passes none). Every class ultimately inherits from `Object` (and `BasicObject`). **Method visibility** controls access: methods are **`public`** by default; **`private`** methods can only be called without an explicit receiver (on the current object); **`protected`** methods can be called by other instances of the same class family, useful for comparisons. For simple data holders, **`Struct.new(:sku, :price)`** generates a class with accessors, equality and `to_a`, and **`Data.define(:sku, :price)`** (Ruby 3.2) creates **immutable value objects** with keyword or positional construction, equality by value and `with` for modified copies. Prefer composition and modules to deep inheritance trees, and use `Data` for values such as money, coordinates and API results that should not change after creation.

## Inheritance, private methods and Data value objects

super, visibility and an immutable value object.

```ruby
class Account
  attr_reader :owner, :balance

  def initialize(owner, balance = 0)
    @owner = owner
    @balance = balance
  end

  def deposit(amount)
    validate!(amount)
    @balance += amount
  end

  private

  def validate!(amount)
    raise ArgumentError, "amount must be positive" unless amount.positive?
  end
end

class SavingsAccount < Account
  def initialize(owner, balance = 0, rate:)
    super(owner, balance)                           # explicit arguments to the parent
    @rate = rate
  end

  def add_monthly_interest = deposit((balance * @rate / 12).round(2))
end

Point = Data.define(:lat, :lng) do
  def to_s = "(#{lat}, #{lng})"
end

pune = Point.new(lat: 18.52, lng: 73.86)
moved = pune.with(lng: 73.90)                       # a new object; pune is unchanged
puts pune == Point.new(lat: 18.52, lng: 73.86)       # true: value equality
# pune.instance_variable_set(:@lat, 0)              # FrozenError: Data objects are frozen
```

## Data for values, classes for behaviour

If an object is mainly a bundle of values that should not change (an address, a money amount, a coordinate), `Data.define` gives you immutability and value equality for free.

**Quiz:** What does Data.define (Ruby 3.2+) create?

- [ ] A database table
- [ ] A mutable struct with setters
- [x] An immutable value-object class with value equality
- [ ] A module

*Answer:* An immutable value-object class with value equality. Data classes are frozen value objects with keyword construction and equality by value.
