fireproofsocks

fireproofsocks

I’m working on a problem that requires that a vendor-specific module be loaded to handle vendor-specific tasks related to an order. I’ve been trying out the behaviours, and that all makes sense. Coming from an OO background, however, I’m feeling the need to have a factory in this case… where I want to load up the proper module that implements the given interface, then call the do_something method on it. Conceptually, it’s pretty common OO.

However, in Elixir, the best I’ve come up with is either a big case statement or a series of declared functions that rely on pattern-matching in their signatures to send the execution flow to a specific vendor module, e.g. something like this:

def process_order("vendor-one"), do: VendorOne.process()
def process_order("vendor-two"), do: VendorTwo.process()
def process_order("vendor-two"), do: VendorThree.process()

Or outlined more academically here design-patterns-in-elixir/factory/run.exs at master · joshnuss/design-patterns-in-elixir · GitHub

Is this idiomatic? Is there some other way to dynamically get a module name based on a parameter?

Showing Posts 23 to 14

OvermindDL1

OvermindDL1

Just referencing modern day and what people would know it from. I’ve used factories way back since before C was standardized so I know it is very old pattern. ^.^;

This is the big key!!

You can put the ‘hardcoded’ map in the config files, then in the app anytime you need it access it via Application.get_env/2 and query it directly. You can even do that at compile-time to build up function matchers but by keeping it at run-time you can allow for it to be changed while the system is running to add/remove things to it without needing a hot code swap or reload. :slight_smile:

The configuration itself would just be a mapping of stringnames ↔ module names, or even full MFA’s if you want. :slight_smile:

Protocols are for dispatching on a type like a struct type so if you have that then absolutely yes, otherwise the plugin model is better (which you can also do via any arbitrary tag, like a string, with my ProtocolEx library ^.^).

asummers

asummers

Protocols are what you’re looking for, I think. Elixir doesn’t know about all the enumerable data structures, but it defines a protocol that allows extension of your data structure to become enumerable, by doing

defimpl Enumerable, for: MyModule do
  # enum protocol implementation
end

Similarly if your code defines a behaviour, and the library has a function that knows how to dispatch to the appropriate behaviour callbacks, you can simply pass your module into the dispatcher and it can execute the relevant callbacks, having faith that they’ll be there.

fireproofsocks

fireproofsocks OP

Could you elaborate on that? One of the things I’m stuck on here is how I might have a package (say, something that gets published on Hex), and then how could I build it in a way that would open up customizations to users without requiring them to edit my code (which would be a big no-no)?

E.g. if I build out the mapping internally that handles the mapping of “condition-one” to SomePrivateModule.do_something() and “condition-two” to SomeOtherPrivateModule.do_something(), how could that mapping be extended via configuration so that a user could supply their own mapping so that “condition-three” maps to UserDefinedModule.do_something() ?

Thanks!

roblally

roblally

Neither Java nor C# existed when the GoF patterns were written down. It should also be noted that the GoF patterns book does not contain a Factory pattern. It contains two patterns that have the word Factory in them and two more that are factories in the modern sense but don’t contain the work Factory.

  • AbstractFactory
  • FactoryMethod
  • Builder
  • Prototype

Not all patterns are useful in all contexts, and you’re 100% right that different languages have different idioms and different needs.

roblally

roblally

The GoF patterns have nothing to do with static types. The GoF patterns were created based on work done primarily in Smalltalk, and to a lesser degree C++. Smalltalk is dynamically typed. Indeed, many of the patterns were about how to effectively make decisions using blocks (AKA functions) without static types or isMemberOf:/isKindOf: calls.

Smalltalk was also one of the big influences for Ruby, which in turn influenced Elixir.

sasajuric

sasajuric

Author of Elixir In Action

I agree. The mechanics are inevitably somewhat different, but I think the idea is in its spirit similar: we’re consolidating a conditional and extracting it into a dedicated place. I think this is a useful pattern, even in an FP language.

keathley

keathley

This is exactly how I would go about doing things. I suppose that if you squint a bit this kinda looks like a factory from the OO world. But that comparison aside I think this is a very reasonable pattern for these sorts of scenarios.

sribe

sribe

Good point, I could have been more explicit about statically typed languages with rather inexpressive type systems…

Yes.

OvermindDL1

OvermindDL1

This explicitness could even by a map in a config file for example for ease of changing later or downstream. :slight_smile:

sasajuric

sasajuric

Author of Elixir In Action

I definitely wouldn’t advise doing this, because it introduces a non-obvious coupling between the data in the database and the code. For example, if you refactor the code and don’t pay attention, the mapping is gone. Or even worse, some unwanted mapping might be introduced.

Instead, I’d make it explicit:

def vendor("vendor-one"), do: VendorOne
def vendor("vendor-two"), do: VendorTwo

I would then convert the string to the alias (module name) immediately after the data is loaded. Similarly, I’d convert it back to string before it’s persisted. If you’re using Ecto, you can write a type for that.

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews