Using Parameter Hashes to Reduce Coupling in Ruby Applications
Last updated:When defining methods (and their parameters) in Ruby, there's at least two ways of doing it.
You can:
Define all arguments (and possible default values) one by one. For example:
def my_method(foo,bar,baz) # method body end
Define all arguments as a parameter hash:
def my_method(hsh={}) # use hsh[:foo] # use hsh[:bar] # use hsh[:baz] end
Using one or the other will not automatically make your project better or worse, but it may have implications in how coupled client code will be to your code.
Coupling
Coupling can be thought of as the level of explicit dependence one piece of code (function, class, module, etc) has with another; in general, we consider dependency upon external code (which you have no control of) to be a especially important kind of coupling.
Code you've written that uses external code (external libraries, third-party code, etc) is said to be coupled with those if changes on those would cause your code to break.
In the same vein, we can talk about code that is tightly coupled (i.e. very dependent upon external code) and also about code that is loosely coupled (not very dependent upon external code).
It is generally well-understood that instantiating external classes in your code and using methods from it will lead to your code being dependent upon (i.e. coupled with) those classes:
require 'external-project'
def my_method(foo,bar)
var = ExternalClass.new
result = var.do_task_1
another_result = result.process_further
end
If you look at the previous snippet, you can see that you code is coupled because of the following:
- it knows module
external-module
by name. - it knows class
ExternalClass
by name. - it knows that the constructor for class
ExternalClass
takes no parameters. - it knows that class
ExternalClass
has an instance method calleddo_task_1
and that it takes no parameters. - it know that instance method
do_task_1
returns a value. - it knows that that value responds to a method called
process_further
and returns another value.
Each of the bullet points reflects an expectation you have about the external library. If any of those expectations stops being met (due to updates in the external code, changes in the API, the project maintainer being hit by a bus, etc), your code will break.
Each dependency reflects an expectation your code has about external code.
This is not necessarily bad. Your code needs to interact with the external world otherwise it would be useless. You just need to be aware of this.
Parameter hashes
Using parameter hashes can reduce coupling client code (code that uses your code) will have with yours.
At the end of the day, it will give you more freedom because you are not required to maintain the order your arguments are defined to prevent client code from breaking.
In addition, you will be able to add as many parameters (in the hash) as you want to your methods, without breaking client that uses older versions of your APIs.
def my_method
var1 = SomeExternalClass.new({
:foo => 10,
:bar => 'a string'
})
var2 = SomeExternalClass.new({
:bar => 'a string',
:foo => 10
})
# when using parameter hashes, the order doesn't matter
var3 = SomeExternalClass.new({
:foo => 'bar'
})
# nor does it matter if you only define some parameters
end
Possible Drawbacks
This technique has its pros and cons. Some potential pitfalls are as follows:
More tendency to create classes/method that do too much. - When using parameter hashes, you may be tempted to provide a single method/class with many possible uses (because you can hide the number of parameters in a single
hsh
variable, for instance) and thus create few, overly burdened objects instead of many "lighter" ones.Greater need for documentation - Code should be clear enough to show what and how you are doing, and comments should generally be concerned with the why you are doing something. Explicit parameter lists can help others understand what you are trying to do with a method. If you use parameter hashes, these will be missing and you'll probably need to add some documentation defining accepted parameters.
Less support in IDEs - Most IDEs use method definition and explicitly-defined parameters to supply you with suggestions when you use inteli-sense (autocompletion) and other similar features.