The pimp my library design pattern

In our daily job as developers, we often use different libraries. They, however, are usually made to be generic and allow many people to use them, so sometimes we need to do something extra that is specific to our use case in order to make things work properly. The fact that we cannot really modify the original library code means that we have to do something different. Something that we have already looked at is the decorator and the adapter design pattern. Well, pimp my library achieves something similar, but it does this in the Scala way and some of the extra work is given to the compiler to deal with.

The pimp my library design pattern is really similar to extension methods in C#. We will see some examples in the following subsections.

Using the pimp my library

The pimp my library design pattern is really easy to use. Let's see an example in which we want to add some useful methods to the standard String class. Of course, we cannot modify its code, so we need to do something else:

package object pimp {

  implicit class StringExtensions(val s: String) extends AnyVal {
    
    def isAllUpperCase: Boolean =
      (0 to s.size - 1).find {
        case index =>
          !s.charAt(index).isUpper
      }.isEmpty
    
  }
}

In the preceding code, we have a package object. It gives us the convenience to not do anything extra in order to be able to access its members from the classes in the same package in Scala. It can be a simple object, but then we will have to import ObjectName._ in order to gain access to the members.

The preceding object is just a detail and is not related to the design pattern. The pimp my library code is the internal class. There are a few important things about this:

  • It is implicit
  • It extends AnyVal

These features allow us to write the following application:

object PimpExample {

  def main(args: Array[String]): Unit = {
    System.out.println(s"Is 'test' all upper case: ${"test".isAllUpperCase}")
    System.out.println(s"Is 'Tes' all upper case: ${"Test".isAllUpperCase}")
    System.out.println(s"Is 'TESt' all upper case: ${"TESt".isAllUpperCase}")
    System.out.println(s"Is 'TEST' all upper case: ${"TEST".isAllUpperCase}")
  }
}

We basically added an extension method to the standard string that checks whether the entire string is in uppercase or not. The only thing we need to do is make sure that the implicit class is available in the scope where we want to use the methods defined by it.

The output of the preceding application is shown as follows:

Using the pimp my library

In our example, we didn't have to write code that wraps strings in our extension class. Our code shows the type as a normal string; however, we can just do extra things with it. Additionally, the decorator design pattern will suffer in the cases where the class we are trying to decorate is final. Here there is no issue. Again, all the magic happens because we have an implicit class and the Scala compiler automatically figures out that it can wrap and unwrap a string depending on the methods we call on it.

We can, of course, add more methods to the StringExtensions class and they will be available to all the strings where the implicit class is available. We can also add other classes:

implicit class PersonSeqExtensions(val seq: Iterable[Person]) extends AnyVal {
  
  def saveToDatabase(): Unit = {
    seq.foreach {
      case person =>
        System.out.println(s"Saved: ${person} to the database.")
    }
  }
  
}

The preceding code is capable of saving an entire collection of the Person type to a database (even though, in the example, we just print the collection to the standard output). For completeness, our Person model class is defined as follows:

case class Person(name: String, age: Int)

Using the new extension is then similar to the earlier extension:

object PimpExample2 {
  def main(args: Array[String]): Unit = {
    val people = List(
      Person("Ivan", 26),
      Person("Maria", 26),
      Person("John", 25)
    )
    people.saveToDatabase()
  }
}

The preceding example will produce the expected result, as follows:

Using the pimp my library

We can also apply the pimp my library design pattern to our custom classes if we need to and if it makes sense.

Pimp my library in real life

As you can see from the preceding section, the pimp my library design pattern is extremely easy to use. This is seen quite often, especially when a decorator or adapter design pattern is needed. We can, of course, figure out ways to deal with issues without this library design, but in reality, it helps us to avoid boilerplate. It also really helps in making our code more readable. Last but not least, it can be used to simplify the use of specific libraries.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.47.166