# OOP-brained design patterns

**URL:** <https://discourse.julialang.org/t/oop-brained-design-patterns/135423>\
**Category:** General Usage\
**Created:** [February 3, 2026, 12:03pm UTC](https://discourse.julialang.org/t/oop-brained-design-patterns/135423 "2026-02-03T12:03:29Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [February 3, 2026, 12:03pm UTC](https://discourse.julialang.org/t/oop-brained-design-patterns/135423/1 "2026-02-03T12:03:29Z")

</div>

I recently found myself writing julia code while somewhat OOP-brained. Surprisingly, the following design pattern works pretty well, so I want to share it for comments:

```julia-auto
# or mutable struct
struct Base{Ext}
   baseField::Int
   ext::Ext
end

function do_something(base::Base)
   base.baseField
end

```

I can get unextended “Base and nothing else” by e.g. `Base(baseField, nothing)`.

Now suppose I want to subclass my `Base`, adding new fields, overriding behaviors and using superclass calls. Sure, can do:

```julia-auto
const AbstractDerived = Base{<:Extension}
struct Extension{Ext}
   newField::Int
   ext::Ext
end

function do_something(base::AbstractDerived)
  base.ext.newField + @invoke do_something (base::Base)
end

```

Ah, but now I later want to add a new interface that has a fallback implementation in terms of Base, and a faster specialization for AbstractDerived. Sure, can do:

```julia-auto
function somethingExtra(base::Base)
    #fall-back code
end
function somethingExtra(base::AbstractDerived)
   #...
end

```

Note that the Extension itself has an `ext` field. Of course it has: this is needed if I want to subclass the subclass.

This naively looks convoluted / type-unstable, but it is not: The concrete types are all statically known to the compiler.

The memory layout naively looks like a horror show, but it is not: Just make all extensions immutable structs, then they will be inlined into `base`. This should give almost the exact same memory layout as the same thing in C++. I am saying “almost” because of internal structure padding.

This is not a surprise: This is exactly how you do OOP of data in C. And as we all know, C++ is just overengineered syntactic sugar over good old C.

Making the extensions immutable is no real price to pay: They always live inside some mutable `base`.

Next, suppose that you want to mix-and-match behaviors, a la java interfaces. For that, we can use contravariant / upper type bounds and unions:

```julia-auto
struct MixinCarrier{Mixins} end;
struct Mixin1 end;
struct Mixin2 end;

mutable struct Base{Ext, Mix}
   basefield::Int,
   ext::Ext
   mix::Mix
end

function foo(base::Base{<:Any, MixinCarrier{>:Mixin1}}) 
#...
end

foo(Base(1, nothing, MixinCarrier{Union{Mixin1}}()))

```

Note that the mixed in things cannot carry data, just like java interfaces.

I found the direct type constructors in this thing very cumbersome. But if you have a factory function that is supposed to create and do something with one of our objects (which is almost always the case), you can just use keyword arguments

```julia-auto
function makeAndUseBase(args; ext=nothing, mixins=MixinCarrier{Union{}}())
Base(arg, ext, mixins)
end

```

Initialization order needs some time to get used to. It’s less complex than the insane constructor rules for e.g. java; here, you must construct Ext before constructing Base.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [February 3, 2026, 1:46pm UTC](https://discourse.julialang.org/t/oop-brained-design-patterns/135423/2 "2026-02-03T13:46:33Z")

</div>

Not a fan of how the types look but there’s not much wiggle-room in a language where concrete types can’t subtype each other. Abstract types are still doing the subtyping of course, and taking away the shortcuts and aliases, we have

- `::Base{<:Any}` methods working on the concrete “parent” `Base{Nothing}` as well as concrete “child” types like `Base{Extension{Nothing}}`.
- `::Base{<:Extension{<:Any}}` methods working on the concrete “parent” `Base{Extension{Nothing}}` as well as concrete “child” types like `Base{Extension{Further{Nothing}}}`

So the couple issues I see here is that

1. With a new component `struct` for each set of additional fields, child types get longer unless we make aliases that look kind of like a shuffled subtyping statement, and those don’t always get printed. I don’t know if this recovers all the features, but `abstract type` statements with \*bona fide` subtyping and nesting parent fields instead would be cleaner and more familiar.
2. It’s possible to nested repeated fields, causing strange field shadowing (which isn’t good practice anyway). In a dynamically typed language, we’d expect the exact runtime type to pick the field, in this case the most nested one. In a statically typed language, the static type might pick the field, but there might also be a dynamic option. Julia is dynamically typed, but a fallback method would only match its annotation, not the exact runtime type:

```julia-auto
julia> const ShadowedBase = Base{<:Base} # simplest subtype with repeated field
ShadowedBase (alias for Base{<:Base})

julia> function do_something(base::ShadowedBase) # overriding method
         base.ext.baseField
       end
do_something (generic function with 2 methods)

julia> do_something(Base(1, Nothing))
1

julia> do_something(Base(1, Base(2, Nothing)))
2

julia> function do_another(base::Base) # only fallback
         base.baseField
       end
do_another (generic function with 1 method)

julia> do_another(Base(1, Nothing))
1

julia> do_another(Base(1, Base(2, Nothing)))
1

```

To get the expected dynamic behavior, I don’t see any way around getters that effectively flatten field access, and that’s probably better enforced with some sort of macro.

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [February 3, 2026, 2:30pm UTC](https://discourse.julialang.org/t/oop-brained-design-patterns/135423/3 "2026-02-03T14:30:20Z")

</div>

> With a new component `struct` for each set of additional fields, child types get longer unless we make aliases that look kind of like a shuffled subtyping statement…

True! This is verbose, and that is somewhat annoying.

It doesn’t really allow to re-use extensions for multiple Base classes, a la scala mixin `trait`. But then, the data layout for scala `trait` data fields is a horror show.

My main use was for very old-style OOP: You traverse a graph, e.g. for parsing a document, and then you call a variety of `onFooBarEnter` / `onFooBarExit` functions, SAX style / “event-driven parsing”. So you have

```julia-auto
struct Visitor{Ext}
ext::Ext
end

struct Traversal{Ext}
ext::Ext
end

handleFooEvent(::Visitor, arg) = nothing
handleBarEvent(::Visitor, arg) = nothing

function traverse(traversal, visitor, data)
   ...
   handleFooEvent(visitor, something)
   handleBarEvent(visitor, something)
end

```

In this type of thing, it is very convenient to be able to “subclass” a visitor with all its existing functionality (data, all the event handlers) and just modify some of them, maybe adding more internal data.

Likewise, it is very convenient to be able to add new events without having to touch code for other visitors – just need a fallback implementation.

Especially for testing, it is quite valuable to be able to mix and match behaviors by constructing bespoke objects that combine various behaviors, with a minimum of boilerplate. Also useful for println debugging: Subtype your nice solver with one that adds strategic println and then `@invoke` the super.

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [June 11, 2026, 5:35am UTC](https://discourse.julialang.org/t/oop-brained-design-patterns/135423/4 "2026-06-11T05:35:08Z")

</div>

This is an interesting pattern which leads to very efficient code. However, the readability is not so great.

What I use in these cases:

- Always introduce an abstract type per concrete type, at least for types which are public. For `MyType` this would be `AMyType` (because it is _a_ `MyType` in the sense of “I don’t care which one” – interestingly, this implies the same meaning as `AbstractMyType` or `AnyMyType`, but is shorter and simpler to read after getting used to)
- Use the concrete type in the field constraints of your structs (obviously).
- Never type-constraint your methods to the concrete type, but to the abstract type.
- If you want to extend a type, it’s then simply introducing another abstract type (to again be reusable) and letting a macro from one of the packages copy over the fields into a new type.

No downsides of this procedure have been identified [when I asked here](https://discourse.julialang.org/t/only-abstract-types-in-method-signatures/125435).  
One day I should probably create a package for this so that you could e.g. write

```julia
@extendable struct Base
    baseField::Int
end

function do_something(base::ABase)
    base.baseField
end

```

and it would just work. Or even better

```julia
using ExtendableModules

@extendable module MyModule
struct Base
    baseField::Int
end

function do_something(base::Base)
    base.baseField
end
end

```

where the macro would rewrite all `struct`s in the module to define their abstract type, too. Additionally, it would rewrite all method type constraints to use the abstract types. That would be nice.
