# How to write idiomatic (non-OOP) Julia version of OOP design pattern?

**URL:** <https://discourse.julialang.org/t/how-to-write-idiomatic-non-oop-julia-version-of-oop-design-pattern/83548>\
**Category:** General Usage\
**Tags:** design-pattern\
**Created:** [June 30, 2022, 4:35am UTC](https://discourse.julialang.org/t/how-to-write-idiomatic-non-oop-julia-version-of-oop-design-pattern/83548 "2022-06-30T04:35:40Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![merlin](https://avatars.discourse-cdn.com/v4/letter/m/8baadc/32.png) [@merlin](https://discourse.julialang.org/u/merlin)\
**Post date:** [June 30, 2022, 4:35am UTC](https://discourse.julialang.org/t/how-to-write-idiomatic-non-oop-julia-version-of-oop-design-pattern/83548/1 "2022-06-30T04:35:40Z")

</div>

I learned a lot about Object Oriented Programming from [this blog article from 2020 about “Gang of Four” design patterns.](https://python-patterns.guide/gang-of-four/composition-over-inheritance/) I should say that I learned about OOP long ago via a Java book but as a data professional I have never needed to know much about these things. I should emphasize that OOP never clicked with me, and when I discovered Julia it felt like a natural language.

**How would we properly write idiomatic (non-OOP) Julia version of the solution?** The python for the blog author’s preferred solution is pasted far below.

Here is my attempt in Julia to respond to the author’s preferred solution to a toy implementation of Logger in python, using the “Composition over Inheritance” principle from “Design Patterns: Elements of Reusable Object-Oriented Software”. Their solution avoids the explosion of subclasses problem that can happen with inheritance in OOP.

```julia
function log_message(message::String, sink::T) where {T <: AbstractPath}
    io = open(sink, "a")
    write(io, message * "\n")
    close(io)
end

function log_message(message::String, sink::TCPSocket)
    send(socket::UDPSocket, message)
end

function log_message(message::String, sink::SysLog)
    write(sink, message)
end

function message_filter(pattern::Vector{String}, message::String)
    for x in pattern
        if occursin(x, message)
            return message
        end
    end
end

# TEST: log_message function
using FilePathsBase
msg1 = "Error: Its a bad bad problem here"
msg2 = "Warn: eh ok fine"
file = Path("./test-logger.txt")

log_message(message_filter(["Error"], msg1), file)
# or pipe the filtered message
message_filter(["Warn"], msg2) |> (x -> log_message(x, file))

shell> cat test-logger.txt
    # Error: Its a bad bad problem here
    # Warn: eh ok fine

```

#### From “Solution #4: Beyond the Gang of Four patterns”, the python from the post is copied below.

This is the blog author’s favored solution in python using Composition instead of Inheritance.

```python
class Logger:
    def __init__ (self, filters, handlers):
        self.filters = filters
        self.handlers = handlers

    def log(self, message):
        if all(f.match(message) for f in self.filters):
            for h in self.handlers:
                h.emit(message)

# Filters now know only about strings!

class TextFilter:
    def __init__ (self, pattern):
        self.pattern = pattern

    def match(self, text):
        return self.pattern in text

# Handlers look like “loggers” did in the previous solution.

class FileHandler:
    def __init__ (self, file):
        self.file = file

    def emit(self, message):
        self.file.write(message + '\n')
        self.file.flush()

class SocketHandler:
    def __init__ (self, sock):
        self.sock = sock

    def emit(self, message):
        self.sock.sendall((message + '\n').encode('ascii'))

class SyslogHandler:
    def __init__ (self, priority):
        self.priority = priority

    def emit(self, message):
        syslog.syslog(self.priority, message)

# results
f = TextFilter('Error')
h = FileHandler(sys.stdout)
logger = Logger([f], [h])

logger.log('Ignored: this will not be logged')
logger.log('Error: this is important')
# Error: this is important

```

---

<div class="post-metadata">

**Author:** ![purplishrock](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/purplishrock/32/13451_2.png) [@purplishrock](https://discourse.julialang.org/u/purplishrock)\
**Post date:** [July 7, 2022, 4:25am UTC](https://discourse.julialang.org/t/how-to-write-idiomatic-non-oop-julia-version-of-oop-design-pattern/83548/2 "2022-07-07T04:25:07Z")

</div>

This might help.

> [@Composition and inheritance: the Julian way](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231):
>
> I am a relatively newbie and I am wondering what is the most appropriate Julian way to reuse the code. I don’t want to trigger discussions on topics already discussed a million times. This post simply aims to lay down a clear list of rules to be followed to write good Julia code, and pose a couple of questions. Let’s start with an example: I have a type Person mutable struct Person \<: AbstractPerson name::String age::Int end and a lot (hundreds) of methods dealing with it. Now I wa…

if you search the forums for “oop”, you will find many threads that have discussed this topic.

---

<div class="post-metadata">

**Author:** ![merlin](https://avatars.discourse-cdn.com/v4/letter/m/8baadc/32.png) [@merlin](https://discourse.julialang.org/u/merlin)\
**Post date:** [July 7, 2022, 3:25pm UTC](https://discourse.julialang.org/t/how-to-write-idiomatic-non-oop-julia-version-of-oop-design-pattern/83548/3 "2022-07-07T15:25:22Z")

</div>

Thanks for the reply! I did see the post you referenced, and others that are attempting to migrate OOP design patterns into julia.

My question is a little different as my goal is idiomatic Julia with no OOP patterns, about a specific well-known example from a famous OOP book. I’ll rephrase the title:

> I rewrote a toy OOP design pattern solution (“Composition over Inheritance”) into Julia using multiple dispatch instead of object methods, does this seem correct?

---

<div class="post-metadata">

**Author:** ![Jeff\_Emanuel](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeff_emanuel/32/15440_2.png) [@Jeff\_Emanuel](https://discourse.julialang.org/u/Jeff_Emanuel)\
**Post date:** [July 7, 2022, 4:11pm UTC](https://discourse.julialang.org/t/how-to-write-idiomatic-non-oop-julia-version-of-oop-design-pattern/83548/4 "2022-07-07T16:11:33Z")

</div>

The sink is likely to be common among many calls. I think carrying the sink around in the calling code is onerous. Users of the OOP class wouldn’t need to do that; the instance encapsulates the filter and handlers. You can encapsulate the sink with a closure.

---

<div class="post-metadata">

**Author:** ![merlin](https://avatars.discourse-cdn.com/v4/letter/m/8baadc/32.png) [@merlin](https://discourse.julialang.org/u/merlin)\
**Post date:** [July 8, 2022, 5:09am UTC](https://discourse.julialang.org/t/how-to-write-idiomatic-non-oop-julia-version-of-oop-design-pattern/83548/5 "2022-07-08T05:09:05Z")

</div>

Thanks for helping, I’m sorry for misunderstanding what you wrote. In Julia isn’t it preferable to use method dispatch based on argument type? In the toy problem above we can call `log_message` with a text message and desired target, the function argument `sink`.

```julia
# filepath is a subtype of AbstractPath, indicating a file location
# socket is a TCPSocket type
# syslog is a system log location of type SysLog

log_message("Error: This error just happened.", filepath)
log_message("Error: This error just happened.", socket)
log_message("Error: This error just happened.", syslog)

```

It doesn’t seem necessary to have handler objects, because `log_message` is defined for the types of log locations that we intend to support.

Sorry again if I missed something, I don’t understand the part about “carrying the sink around in the calling code.” In my imagination, if I need to send to a socket then the client code defines the socket and log\_message can just write to there when its called.

Also I don’t understand why the `sink` argument needs to be encapsulated with a closure.

---

<div class="post-metadata">

**Author:** ![Jeff\_Emanuel](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeff_emanuel/32/15440_2.png) [@Jeff\_Emanuel](https://discourse.julialang.org/u/Jeff_Emanuel)\
**Post date:** [July 8, 2022, 2:09pm UTC](https://discourse.julialang.org/t/how-to-write-idiomatic-non-oop-julia-version-of-oop-design-pattern/83548/6 "2022-07-08T14:09:55Z")

</div>

The sink argument, and the filter, and whatever other logging configuration ought to be encapsulated, so that clients don’t need to pass them through their entire call stack to wherever they need to log something. If only the sink is being encapsulated, then passing the sink is not much different than passing the closure, But if there are multiple different arguments to configuring logging, then keeping one closure is simpler and more maintainable than all those options. Also, a logging closure provides a higher level abstraction than passing around what’s essentially the implementation details of logging. If you need to add another option, you just modify the closure creation rather all the code that needs to do logging and all the code that may call code that needs to do logging. That’s error prone and makes code harder to maintain because at every level you must be concerned with the logging configuration details. Alternatively, you pass the logging configuration in a struct, and it looks like passing the OOP class instance. Indeed, a closure in Julia is just syntactic sugar for a callable struct. You could store the configured log\_message closure in a const global if you have to and not pass it through your functions at all. You still use multiple dispatch to handle different sink types for the function that creates the closure. This is analogous to overloaded constructors for a logger class in OOP.

```julia
create_logger(sink::TCPSocket) = message -> send(sink, message)
create_logger(sink::SysLog) = message -> write(sink)
# etc, possibly more methods with filters and maybe other logging configuration.

# usage option a
high_level_function(some_args, create_logger(s))
function some_lower_level_function(args, logger)
  #...
   logger(msg)
end

# usage option b
const log_message = create_logger(s)
high_level_function(some_args)
function some_lower_level_function(args)
   log_message(msg)
end

```

---

<div class="post-metadata">

**Author:** ![Vincent\_Picaud](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vincent_picaud/32/3108_2.png) [@Vincent\_Picaud](https://discourse.julialang.org/u/Vincent_Picaud)\
**Post date:** [July 8, 2022, 5:01pm UTC](https://discourse.julialang.org/t/how-to-write-idiomatic-non-oop-julia-version-of-oop-design-pattern/83548/7 "2022-07-08T17:01:04Z")

</div>

Just a remark, if you still want to use OOP under Julia, I just saw this package: [GitHub - Suzhou-Tongyuan/ObjectOriented.jl: Conventional object-oriented programming in Julia without breaking Julia's core design ideas](https://github.com/Suzhou-Tongyuan/TyOOP.jl)  
Maybe it can be helpful.

---

<div class="post-metadata">

**Author:** ![merlin](https://avatars.discourse-cdn.com/v4/letter/m/8baadc/32.png) [@merlin](https://discourse.julialang.org/u/merlin)\
**Post date:** [July 8, 2022, 6:30pm UTC](https://discourse.julialang.org/t/how-to-write-idiomatic-non-oop-julia-version-of-oop-design-pattern/83548/8 "2022-07-08T18:30:13Z")

</div>

Excellent example, this makes it much more clear. I think I have a better understanding about the purpose of encapsulation. This functionality is part of the python code, but so elemental it isn’t mentioned in the article.

Since it hasn’t been mentioned, I guess the basic goal of comparing OOP compositional design to julia type dispatch is successful.

---

<div class="post-metadata">

**Author:** ![merlin](https://avatars.discourse-cdn.com/v4/letter/m/8baadc/32.png) [@merlin](https://discourse.julialang.org/u/merlin)\
**Post date:** [July 13, 2022, 5:30am UTC](https://discourse.julialang.org/t/how-to-write-idiomatic-non-oop-julia-version-of-oop-design-pattern/83548/9 "2022-07-13T05:30:41Z")

</div>

Thanks! This is a helpful resource, even though I do not want to use OOP. But in contrast to non-OOP designs it is helping me get a deeper understanding.
