Composition and inheritance: the Julian way

You don’t have to re-define all the methods accepting a Person object if you write those methods using functions instead of field access. You just have to define some basic functions

Changing your original example:

abstract type AbstractPerson end
# basic methods to define: name,age,set_age

#basic AbstractPerson
mutable struct Person <: AbstractPerson
    name::String
    age::Int 
end

# CONSTRUCTOR
function Person(name)
    return Person(name, 0)
end

#basic methods
name(a::Person) = a.name
age(a::Person) = a.age
set_age(a::Person,x::Integer) = (a.age = x; x)

# TYPE METHODS: always use `AbstractPerson` as input type...
import Base.display
function display(p::AbstractPerson)
    println("Person: ", name(p), " (age: ", age(p), ")")
end

function happybirthday(p::AbstractPerson)
    set_age(p, age(p) + 1)
    println(name(p), " is now ", age(p), " year(s) old")
end

function call(p::AbstractPerson)
    print_with_color(:red, uppercase(name(p)), "!")
end

#---------------------------------------------------------------------
# DERIVED TYPE : Citizen

# Use abstract type for the interface name, by convention prepend
# `Abstract` to the type name.
abstract type AbstractCitizen <: AbstractPerson end
# here you should think of basic methods for a AbstrctCitizen, such as nationality(c::AbstractCitizen).

# TYPE MEMBERS (composition of `Person` fields and new ones)
mutable struct Citizen <: AbstractCitizen
    person::Person
    nationality::String # new field (not present in Person)
end

#now would be a good time to use macros...
name(c::Citizen) = name(c.person)
age(c::Citizen) = age(c.person)
set_age(c::Citizen,x::Int) = set_age(c.person, x)

#basic abstractcitizen method
nationality(c::Citizen) = c.nationality

#Now everything defined for AbstractPerson should work for Citizen

#And you are not tied to field names anymore:

struct EternalBeing <: AbstractPerson end

name(e::EternalBeing) = "The One who Is"
age(e::EternalBeing) = typemax(Int)
set_age(e::EternalBeing,x) = nothing

const eternal = EternalBeing()

# All just work
display(eternal)
happybirthday(eternal)
call(eternal)
3 Likes