OOP and design patterns in Julia
Julia Taiwan 發起人 杜岳華
Outline
• State-centered and behavior-centered OOP
• The beauty of multiple dispatch
• Changes in v0.6
• Design patterns in Julia
Linguisticrelativity
• or, the Sapir-Whorf hypothesis
• The language determines or constrains cognition.
• The tools you use affect your thought. (frame effect)
你的語言如何影響了你的「思考」?
Whatcanwe do with/toa thing?
• OOP with class
buy
top up
pay fare
loss
buy
top up
pay fare
loss
buy
pay fare
loss
methods objects
Multipledefinitions…
buy
top up
pay fare
loss
Introducehierarchyto aidreusability
buy
top up
pay fare
loss
rechargeable
Single-use
card
Inheritancehierarchydo harmto
flexibility
buy
top up
pay fare
loss
rechargeable
Single-use
card
State-based OOP bundle
the behaviors to datatype!
datatype
behaviors
Composite over
inheritance!
Decouplethe behaviorand state
buy
top up
pay fare
loss
rechargeabl
e
Single-
use
card
datatype
behaviors
Juliafocusonthebehavior
top up(rechargeable, money)
buy(card, money)
pay(card, fare)
loss(card)
We focused on the subject of
an action in the past.
Multipledispatchismorefine-grained
marry(you, me)
shake_hand(you, me)
multiply(matrixA, matrixB)
What if there were no
subjects or several subjects?
connect(ip, port)
We are definingbehaviorsfor things!
Method B
Method A
Method C
怎麼用multiple
dispatch寫猜拳遊戲?
The beautyof multipledispatch
abstract type Shape end
struct Rock <: Shape end
struct Paper <: Shape end
struct Scissors <: Shape end
play(::Type{Paper}, ::Type{Rock}) = 1
play(::Type{Scissors}, ::Type{Paper}) = 1
play(::Type{Rock}, ::Type{Scissors}) = 1
play(::Type{T}, ::Type{T}) where {T <: Shape} = 0
play(a::Type{<:Shape}, b::Type{<:Shape}) = - play(b, a) # Commutativity
https://giordano.github.io/blog/2017-11-03-rock-paper-scissors/
Definethe Shape
abstract type Shape end
struct Rock <: Shape end
struct Paper <: Shape end
struct Scissors <: Shape end
https://giordano.github.io/blog/2017-11-03-rock-paper-scissors/
Definerules
• 1: 前者贏
• 0: 平手
play(::Type{Paper}, ::Type{Rock}) = 1
play(::Type{Scissors}, ::Type{Paper}) = 1
play(::Type{Rock}, ::Type{Scissors}) = 1
play(::Type{T}, ::Type{T}) where {T <: Shape} = 0
https://giordano.github.io/blog/2017-11-03-rock-paper-scissors/
The beautyof multipledispatch
• -1: 前者輸
play(a::Type{<:Shape}, b::Type{<:Shape}) = - play(b, a) # Commutativity
https://giordano.github.io/blog/2017-11-03-rock-paper-scissors/
Decoupledatatypeandbehaviors
• Helps the algebraic definitions
• Enforce engineer to think about the completeness of operation
Changesinv0.6
abstract type Newspaper end
(mutable) struct SubscriberA <: Subscriber
name::String
end
Design patterns
https://github.com/yuehhua/patterns.jl
Compositepattern
• 希望結構上呈現「部分-整體」的概念
• Composite lets clients treat individual objects and compositions of objects
uniformly.
• Recursive composition
https://github.com/yuehhua/patterns.jl
Compositepattern
Picture
Line
Picture
Text
Text Circle
Compositepattern
abstract type Graphic end
Compositepattern
mutable struct Picture <: Graphic
children::Vector{Graphic}
Picture() = new(Graphic[])
End
add!(pic::Picture, g::Graphic) = push!(pic.children, g)
remove!(pic::Picture, g::Graphic) = deleteat!(pic.children,
getindex(pic.children, g))
children(pic::Picture) = pic.children
draw(pic::Picture) = foreach(draw, pic.children)
Compositepattern
struct Line <: Graphic
length::Float64
End
draw(l::Line) = println("Draw a $(l.length) cm line.")
struct Text <: Graphic
str::String
end
draw(t::Text) = println(t.str)
Compositepattern
struct Circle <: Graphic
r::Float64
end
draw(c::Circle) = println("Draw a circle within a radius of
$(c.r) cm.")
Compositepattern
pic = Picture()
add!(pic, Line(5.0))
add!(pic, Text("ABC"))
add!(pic, Circle(5.0))
draw(pic)
pic2 = Picture()
add!(pic2, Text("DEF"))
add!(pic2, pic)
draw(pic2)
Decoratorpattern
• Attach additional responsibilities to an object dynamically.
• Client-specified embellishment of a core object by recursively wrapping it.
• Wrapping a gift, putting it in a box, and wrapping the box.
Decoratorpattern
Decoratorpattern
Decoratorpattern
abstract type LCD end
struct Window <: LCD
size::Float64
end
draw(::Window) = println("Draw basic window view.")
Decoratorpattern
abstract type Decorator <: LCD end
mutable struct Border <: Decorator
component::LCD
size::Float64
end
function draw(b::Border)
println("Draw a $(b.size) width border")
draw(b.component)
# do something
end
Decoratorpattern
mutable struct VerticalScrollBar <: Decorator
component::LCD
end
function draw(vsb::VerticalScrollBar)
# do something
draw(vsb.component)
println("Draw vertical scroll bar.")
end
scroll(vsb::VerticalScrollBar, direction::Symbol) =
println("Vertical scroll bar scrolls $(direction)")
Decoratorpattern
mutable struct HorizontalScrollBar <: Decorator
component::LCD
end
function draw(hsb::HorizontalScrollBar)
# do something
draw(hsb.component)
println("Draw horizontal scroll bar.")
end
scroll(hsb::HorizontalScrollBar, direction::Symbol) =
println("Horizontal scroll bar scrolls $(direction)")
Decoratorpattern
widget = VerticalScrollBar(Border(Window(2.5), 5.0))
draw(widget)
Draw a 5.0 width border
Draw basic window view.
Draw vertical scroll bar.
Observerpattern
• Define a one-to-many dependency between objects so that when one object
changes state, all its dependents are notified and updated automatically.
Observerpattern
Observerpattern
abstract type Newspaper end
abstract type Subscriber end
function subscribe(::Subscriber, ::Newspaper) end
function unsubscribe(::Subscriber, ::Newspaper) end
function notify(::Newspaper) end
Observerpattern
struct SubscriberA <: Subscriber
name::String
end
update(a::SubscriberA, s::String) = println("$(a.name) is notified
by $(s).")
struct SubscriberB <: Subscriber
name::String
end
update(b::SubscriberB, s::String) = println("$(b.name) is notified
by $(s).")
Observerpattern
mutable struct AppleNews <: Newspaper
subscribers::Vector{Subscriber}
AppleNews() = new(Subscriber[])
end
subscribe(s::Subscriber, a::AppleNews) = push!(a.subscribers, s)
notify(a::AppleNews) = foreach(x -> update(x, "AppleNews"),
a.subscribers)
Observerpattern
mutable struct BananaNews <: Newspaper
subscribers::Set{Subscriber}
BananaNews() = new(Set{Subscriber}())
end
subscribe(s::Subscriber, b::BananaNews) = union!(b.subscribers, [s])
notify(b::BananaNews) = foreach(x -> update(x, "BananaNews"),
b.subscribers)
Observerpattern
a = AppleNews()
suba = SubscriberA("Joe")
subb = SubscriberB("Kay")
subscribe(suba, a)
subscribe(subb, a)
notify(a)
Joe is notified by AppleNews.
Kay is notified by AppleNews.
Observerpattern
b = BananaNews()
subscribe(suba, b)
subscribe(subb, b)
notify(b)
Joe is notified by BananaNews.
Kay is notified by BananaNews.
Chainof responsibilitypattern
• Launch-and-leave requests with a single processing pipeline that contains many
possible handlers.
Chainof responsibilitypattern
abstract type Account end
can_pay(acc::Account, amount) = acc.balance >= amount
function pay(acc::Account, amount)
if can_pay(acc, amount)
acc.balance -= amount
else
pay(acc.successor, amount)
end
end
Chainof responsibilitypattern
struct NullAccount <: Account end
mutable struct Bank <: Account
balance::Float64
successor::Account
end
Bank(balance) = Bank(balance, NullAccount())
Chainof responsibilitypattern
mutable struct Paypal <: Account
balance::Float64
successor::Account
end
Paypal(balance) = Paypal(balance, NullAccount())
mutable struct Bitcoin <: Account
balance::Float64
successor::Account
end
Bitcoin(balance) = Bitcoin(balance, NullAccount())
Chainof responsibilitypattern
bank = Bank(100)
paypal = Paypal(300)
bitcoin = Bitcoin(1000)
bank.successor = paypal
paypal.successor = bitcoin
pay(bank, 500)
Chainof responsibilitypattern
julia> println(bank.balance)
100.0
julia> println(paypal.balance)
300.0
julia> println(bitcoin.balance)
500.0
Thank you for attention
Inspired by
Jiahao Chen: Why language matters: Julia and multiple dispatch
https://www.youtube.com/watch?v=gZJFHrYopxw

20171117 oop and design patterns in julia