Julia OOP example

I am a C++ and Python user and quite accustomed to OOP for some tasks. The following is a small example of using OOP style in Julia. Please give comments if there are better ways to do that. Thank! Hope it could help the users have the similar demand.

# animal.jl
abstract type AbstractAnimal end

mutable struct BaseAnimal
    name::String 
    weight::Float64 
    BaseAnimal() = new("", 1.)
end

function get_weight( animal::AbstractAnimal )
    return animal.base.weight        #no member variable in AbstractAnimal
end
#file2, dog.jl
mutable struct Dog <: AbstractAnimal
    base::BaseAnimal 
    owner_name::String
    Dog( name::String ) = new( BaseAnimal(),  name)
end

function get_owner_name( dog::Dog )
    return dog.owner_name
end
#file3 cat.jl
mutable struct Cat <: AbstractAnimal
    base::BaseAnimal 
    climbing_speed::Float64
    Cat( speed::Float64 ) = new( BaseAnimal(),  speed )
end

function get_climbing_speed( cat::Cat )
    return cat.climbing_speed
end
#file4 OOPTest.jl
module OOPTest

export Dog, Cat,
        get_climbing_speed, get_owner_name, get_weight

include("animal.jl")   #it must be put before cat.jl and dog.jl, or there is error
include("cat.jl")
include("dog.jl")
end # module
#file5 runtest.jl
using OOPTest

dog = Dog("Tom")
cat = Cat(10.)

@show get_owner_name( dog )
@show get_climbing_speed( cat ) 
@show get_weight( dog )  
@show get_weight( cat )  
1 Like

This is not so convenient to access the member variable of the base class. The user should remember which variable belongs to the parent class and use base variable to access the parent’s variable. It made extra workload for the user. For the up mentioned example, the user should access the member variable of dog as follows,

dog.owner_name      # very straightforward
dog.base.weight      # user should remember that this variable belong's to its parent, or there will be error
dog.base.name       

@StefanKarpinski Is it possible to solve this on the Language-level?

Then the user can access the parent’s variable directly using

dog.name
dog.weight

there was an intersting discusion about this:

3 Likes

There are some macros to make composition easier, eg Lazy.@forward and similar.

You can also define

Base.parent(dog::Dog) = dog.base

etc and then the user would not need to remember slot names.

I would advise to just let go of OO as a general style in Julia (which does not mean that you can’t use building blocks from it, just that you should not design your code like you would in C++), multiple dispatch meshes better with other idioms.

The problem with simplistic examples of animal classes etc is that they rarely reflect the problems of real life code. You can study code from others (eg read Base, it is very instructive).

(Also, I am not sure why you are pinging Stefan Karpinski on this; generally it is considered unnecessary unless you have a good reason).

6 Likes

Thank you so much! Very helpful information. I am currently rewrite some simulation system written in Python with OOP design. I try to translate into Julia and found the coding style is a quite different and and is not convenient to translate directly.
If there are some books or manuals, such as Thinking in Julia or From C++/Python to Julia, it would be helpful. I will try to learn how to drop the C++ OOP style in Julia.

https://benlauwens.github.io/ThinkJulia.jl/latest/book.html

5 Likes

As variable.something translates into getproperty(variable, :something), you can overload getproperty() for the derived types, like:

function Base.getproperty(animal::T, p::Symbol) where {T<:AbstractAnimal}
    if p in fieldnames(T)
        getfield(animal, p)
    elseif :base in fieldnames(T)
        Base.getproperty(animal.base, p)
    else
        error("type $T has no property $p")
    end
end

However, the dot syntax is intended mainly for the development purposes (like private fields in more OOP languages, I guess). The users are supposed to have accessor functions.

While I don’t personally find the ideas of OOP helpful when thinking about programming, @ninjaaron wrote a guide to his view of the topic a few months ago that didn’t seem to drag too many of the objectionable bits of OOP along when I skimmed it. It at least frames some of the patterns of the language in ways that may be familiar if you’re more comfortable with OOP. The section in the polymorphism notebook titled “Composition and method forwarding: an alternative to inheritance.” specifically addresses what you’re trying to do here.

My own advice for grokking the “Julia” way of structuring code is to not overuse types. Think of them as simple containers for data rather than “objects” that interact. And as for how I would structure your example (although I also think animal examples aren’t super helpful):

abstract type Animal end

name(a::Animal) = a.name
weight(a::Animal) = a.weight

struct Dog <: Animal
    name::String
    weight:Float64
    owner::String
end#struct

owner(d::Dog) = d.owner

struct Cat <: Animal
    name::String
    weight::Float64
    climbing_speed::Float64
end#struct

climbing_speed(c::Cat) = c.climbing_speed

This provides fallback name and weight implementations for Animals that happen to have fields with those names while still allowing overriding if the data comes from elsewhere:

mutable struct Fred <: Animal
    burgers_consumed::Int
end
Fred() = Fred(0)

eat!(fred::Fred) = fred.burgers_consumed += 1

name(::Fred) = "Fred"
weight(fred::Fred) = 200.0 + 0.5fred.burgers_consumed
8 Likes

Thanks for the sharing! The suggested document is concise and useful, which saves me a lot of time to find solution from large amount of discussions on this website. Object Orientation and Polymorphism in Julia

As a C++/Python user, I found Julia’s solution for OOP is quite different from the languages I used. Maybe, Julia just weakly supports OOP and have better solution to replace OOP.
Type-Dispatch Design: Post Object-Oriented Programming for Julia

It seems you copy twice the same links.
Second one may be http://www.stochasticlifestyle.com/type-dispatch-design-post-object-oriented-programming-julia/