Using Parameter Hashes to Reduce Coupling in Ruby Applications

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 called do_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.