ALU

design pattens

Design Patterns: Event Bus

Today, we are tackling something that is kind of new (and by that, I mean not mentioned in the GoF book), and that is the Event Bus.

Motivation

Imagine having a large scale application containing a lot of components interacting with each other, and you want a way to make your components communicate while maintaining loose coupling and separation of concerns principles, the Event Bus pattern can be a good solution for your problem.

Original Link

How to Use the Specification Pattern in Java

This blog post is mainly inspired by the works of Martin Fowler and Eric Evans in their seminal paper Specifications

I find this pattern interesting as it remedies years of head bashing and hair pulling for finding ways of isolating the evolution of a domain model from the mechanisms involved in inspecting and querying it while maintaining a high level of encapsulation. I like to think of it as a "reflection" on the domain model. Hopefully, things will become clearer as we proceed.

Original Link

Using the Adapter Design Pattern in Java

Here I am with another useful design pattern for you — the adapter design pattern. I will also highlight the differences between the decorator design pattern (see my previous article, Decorator Design Pattern in Java, here) and the adapter design pattern.

Adapter Design Pattern

  • The adapter design pattern is a structural design pattern that allows two unrelated/uncommon interfaces to work together. In other words, the adapter pattern makes two incompatible interfaces compatible without changing their existing code.
  • Interfaces may be incompatible, but the inner functionality should match the requirement.
  • The adapter pattern is often used to make existing classes work with others without modifying their source code.
  • Adapter patterns use a single class (the adapter class) to join functionalities of independent or incompatible interfaces/classes.
  • The adapter pattern also is known as the wrapper, an alternative naming shared with the decorator design pattern.
  • This pattern converts the (incompatible) interface of a class (the adaptee) into another interface (the target) that clients require.
  • The adapter pattern also lets classes work together, which, otherwise, couldn’t have worked, because of the incompatible interfaces.
  • For example, let’s take a look at a person traveling in different countries with their laptop and mobile devices. We have a different electric socket, volt, and frequency measured in different countries and that makes the use of any appliance of one country to be freely used in a different country. In the United Kingdom, we use Type G socket with 230 volts and 50 Hz frequency. In the United States, we use Type A and Type B sockets with 120 volts and 60 Hz frequency. In India, we use Type C, Type D. and Type M sockets with 230 volts and 50 Hz. lastly, in Japan, we use Type A and Type B sockets with 110 volts and 50 Hz frequency. This makes the appliances we carry incompatible with the electric specifications we have at different places.
  • This makes the adapter tool essential because it can make/convert incompatible code into compatible code. Please notice here that we have not achieved anything additional here — there is no additional functionality, only compatibility.

To better understand this, let’s look at an example of geometric shapes. I am keeping the example relatively simple to keep the focus on the pattern. Suppose we have a project of drawing, in which we are required to develop different kinds of geometric shapes that will be used in the Drawing via a common interface called  Shape.

Original Link

Object Orientation: Maintaining Relationships Amongst Objects

Any successful programming language lives on long after it is first created. In its lifespan, this language goes through multiple changes that fix different issues as well as adds more and more useful features. In order to go through these changes smoothly, the software should be designed to make it more maintainable and extensible.

The Code Smells! The Code Rots!

Oftentimes, we lack internal quality, and the focus is primarily on "making it work" and not so much on "doing it the right way." Here, I will explain my thoughts on getting software craftsmanship to "do it right" in the object-oriented world.

Original Link

Strategy vs. Factory Design Patterns in Java

Here I am with my another article on design patterns. Many of us use strategy as well as factory design patterns. But, sometimes, it becomes really hard to tell the difference between the two and to decide when and which would be a better fit for the project at hand. Let’s take a look at some of the key points for each of these with an example.

Strategy Design Pattern

  • The strategy design pattern (also known as the policy design pattern) is a behavioral design pattern that allows us to select an algorithm at runtime. In this pattern, the code receives run-time instructions to decide which group of the algorithms to use.

    Original Link

How to Use the Singleton Design Pattern in Java

By using Class-level Member (Lazy Initialization Method), we have various ways of creating singletons in Java. Now, first of all, what is Singleton and why is it required?

The singleton design pattern is used to restrict the instantiation of a class and ensures that only one instance of the class exists in the JVM. In other words, a singleton class is a class that can have only one object (an instance of the class) at a time per JVM instance. There are various ways to design/code a singleton class.

  1. Class-level Member (Eager Initialization Method):
    1. Make constructor private.
    2. Make a private constant static instance (class-member) of this Singleton class.
    3. Write a static/factory method that returns the object of the singleton class that we have created as a class-member instance.
    4. We can also mark a static member as public to access constant static instance directly. But, I like to access class/instance members via methods only.
    5. So, the singleton class is different from a normal Java class in terms of instantiation. For a normal class, we use a constructor, whereas for singleton class we use the getInstance()method.
    public class SingletonClass { private static final SingletonClass SINGLE_INSTANCE = new SingletonClass(); private SingletonClass() {} public static SingletonClass getInstance() { return SINGLE_INSTANCE; }
    }
    
  2. Class-level Member (Lazy Initialization Method):
    1. Make constructor as private.
    2. Make a private static instance (class-member) of this singleton class. But, DO NOT instantiate it.
    3. Write a static/factory method that checks the static instance member for null and creates the instance. At last, it returns an object of the singleton class.
    public class SingletonClass { private static SingletonClass SINGLE_INSTANCE = null; private SingletonClass() {} public static SingletonClass getInstance() { if (SINGLE_INSTANCE == null) { synchronized(SingletonClass.class) { SINGLE_INSTANCE = new SingletonClass(); } } return SINGLE_INSTANCE; }
    }
    

  3. Class-level Member (Lazy Initialization with double lock Method):
    1. Here, we run into a problem. Suppose that there are two threads running. Both can get inside of the if statement concurrently when the instance is null. Then, one thread enters the synchronized block to initialize the instance, while the other is blocked. When the first thread exits in the synchronized block, the waiting thread enters and creates another singleton object. Note that when the second thread enters the synchronized block, it does not check to see if the instance is non-null.
    public class SingletonClass { private static SingletonClass SINGLE_INSTANCE = null; private SingletonClass() {} public static SingletonClass getInstance() { if (SINGLE_INSTANCE == null) { synchronized (SingletonClass.class) { if (SINGLE_INSTANCE == null) { SINGLE_INSTANCE = new SingletonClass(); } } } return SINGLE_INSTANCE; }
    }
    
  4. By using Enums:
    All of the above approaches are not full-proof in all the cases. We can still create multiple instances of the above implementations by using serialization or reflection. In both of the cases, we can bypass the private constructor and, hence, can easily create multiple instances. So, the new approach is to create singleton class by using enums since enums fields are compiled time constants, but they are instances of their enum type. And, they’re constructed when the enum type is referenced for the first time.
public enum SingletonClass { SINGLE_INSTANCE;
}

Original Link

Thinking Less With Scala, Scalapeno 2018 [Video]

Last week, I spoke at Scalapeño about “Thinking Less With Scala.” The video has just been published and I’m excited to share it with you.

The main message of the talk is that Functional Scala allows you to think less when you read and write code. It demonstrates that good Functions, Signatures, Data, and Functional Patterns to keep your mental stack small.

Watch this talk if you’d like to get started with Functional Programming in Scala, or if you’re already doing some FP but want an insight into the benefits.

In this talk, I answer the following questions about Scala:

  • Why are functions with exceptions and side-effects hard to read?
  • What is a total function?
  • What is a pure function?
  • How do you convert a non-total function to a total one?
  • How do you use container types to give new meaning to you function signatures?
  • How do good functions signatures allow you to think less about what a function is implemented?
  • What is a parametric function?
  • What is parametricity?
  • How does parametricity allow you tho think less about how to implement functions?
  • What is an ADT?
  • What is a value class?
  • How does ADTs/Value classes let you think less about the correctness of you programs?
  • What is a functional pattern?
  • Why are they so different than OO design patterns?
  • How do you spot and refactor to these patterns?
  • How do you encode them with typeclasses?
  • How to use typeclasses?
  • What is a Monoid?
  • How do you use cats library to take advantage of these patterns?
  • What is a derived function?
  • How parametricity, typeclasses and derived functions are related?
  • How does all this FP knowledge will benefit your career?

My slides are available here:

https://www.slideshare.net/DanielSebban/scalapeno18-thinking-less-with-scala

Original Link

How to Build Creational Design Patterns: Singleton Pattern

The singleton design pattern is a software design pattern that restricts the instantiation of a class to one object. In comparison with other creational design patterns, such as the abstract factory, the singleton builder pattern will create an object and will also be responsible so that only one instance of that object exists.

When creating a class as a singleton, there are some certain problems to keep in mind:

  • How can it be ensured that a class has only one instance?
  • How can the sole instance of a class be accessed easily?
  • How can a class control its instantiation?
  • How can the number of instances of a class be restricted?

For example, let’s suppose that we have a class that sends messages — the Messenger  class:

package com.gkatzioura.design.creational.singleton; public class Messenger { public void send(String message) { }
}

However, we want the message procedure to be handled only by one instance of the Messenger  class. Let’s imagine a scenario where the Messenger  class opens a tcp connection (for example, XMPP) and has to keep the connection alive in order to send messages. It will be pretty inefficient to open a new XMPP connection each time we have to send a message.

Therefore, we will proceed and make the Messenger  class a singleton.

package com.gkatzioura.design.creational.singleton; public class Messenger { private static Messenger messenger = new Messenger(); private Messenger() {} public static Messenger getInstance() { return messenger; } public void send(String message) { }
}

As you can see, we set the messenger constructor as private, and we initialized a messenger using a static variable. Static variables are class level variables where memory allocation only happens once when the class is loaded in the memory. In doing this, we ensure that the Messenger class will be instantiated only once. The getInstance method will fetch the static messenger instance once it is called.

Obviously, the previous approach has its pros and cons. We don’t have to worry about thread safety, and the instance will be created only when the Messenger  class will be loaded. However, it lacks flexibility. Let’s consider the scenario of passing configuration variables to the Messenger constructor. You will find that it is not possible using the previous approach.

A workaround is to instantiate the Messenger class on the getInstance method.

package com.gkatzioura.design.creational.singleton.lait; public class Messenger { private static Messenger messenger; private Messenger() {} public static Messenger getInstance() { if(messenger==null) { messenger = new Messenger(); } return messenger; } public void send(String message) { }
}

The above approach might work in certain cases, but it misses thread safety in cases where the class might get instantiated in a multithreaded environment.

The easiest approach to make our class thread-safe is to synchronize the getInstance   method.

package com.gkatzioura.design.creational.singleton.lait; public class Messenger { private static Messenger messenger; private Messenger() {} public synchronized static Messenger getInstance() { if(messenger==null) { messenger = new Messenger(); } return messenger; } public void send(String message) { }
}

That one will work. At least the creation of the messenger will be synchronized and no duplicates will be created. The problem with this approach is that the synchronization is only needed once when the object is created. Using the above code will lead to unnecessary overhead.

The other approach is to use the Double-Checked Locking approach. Now, Double-Checked locking needs extra care since it is easy to pick the broken implementation over the correct one. The best approach is to implement lazy loading using the volatile keyword.

package com.gkatzioura.design.creational.singleton.dcl; public class Messenger { private static final Object lock = new Object(); private static volatile Messenger messenger; private Messenger() {} public static Messenger getInstance() { if(messenger==null) { synchronized (lock) { if(messenger==null) { messenger = new Messenger(); } } } return messenger; } public void send(String message) { }
}

By using the volatile keyword, we prevent the write of a volatile to be reordered with respect to any previous read or write and a read of a volatile to be reordered with respect to any following read or write. Also, a mutex object is used to achieve synchronization.

To sum it all up,  we created an object and we also made sure that there will be only one instance of that object. We made sure that there won’t be any problems instantiating the object in a multi-threaded environment.

You can find the source code on GitHub.

Original Link

Creational Design Patterns: An Abstract Factory Pattern

The abstract factory pattern is a creational pattern that is one of the most popular patterns, along with the builder and the factory pattern. Creational patterns are used to create objects without directly creating objects with a constructor.

The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes. The intent of employing the pattern is to insulate the creation of objects from their usage and to create families of related objects without having to depend on their concrete classes.

By using that pattern, the client doesn’t have to be aware of the objects and their implementation details. It is a responsibility of the abstract factory’s implementation to implement the objects and handle all the details successfully.

So, let’s get into action and start solving problems!

Supposing we are responsible for the canning process of various products, there are two objects that have to be created: the can’s main body and the can’s top. Also, considering the fact that we might have various forms of canning, we might as well have various factories that can handle the canning process. For example, we might have a factory for beer canning or a factory for food canning.

In regards to the description above, it seems that abstract factory is the way to go. We do have a family of objects that we want to hide the construction of these objects. We will start by adding two interfaces regarding the functionality of a can’s top and a can’s body.

package com.gkatzioura.design.creational.abstractfactory; public interface CanTop { void open(); }
package com.gkatzioura.design.creational.abstractfactory; public interface CanBody { void fill(); }

Then, we shall create an abstract factory that will provide the methods to create those objects.

package com.gkatzioura.design.creational.abstractfactory; public abstract class CanningFactory { public abstract CanTop createTop(); public abstract CanBody createBody(); }

As mentioned above, we have cases of beer canning. Thus, we will have implementations of the CanTop and the CanBody class.

package com.gkatzioura.design.creational.abstractfactory.beer; import com.gkatzioura.design.creational.abstractfactory.CanTop; public class BeerCanTop implements CanTop { public void open() { }
}
package com.gkatzioura.design.creational.abstractfactory.beer; import com.gkatzioura.design.creational.abstractfactory.CanBody; public class BeerCanBody implements CanBody { public void fill() { }
}

Then, we shall implement a beer canning factory.

package com.gkatzioura.design.creational.abstractfactory.beer; import com.gkatzioura.design.creational.abstractfactory.CanBody;
import com.gkatzioura.design.creational.abstractfactory.CanTop;
import com.gkatzioura.design.creational.abstractfactory.CanningFactory; public class BeerCanningFactory extends CanningFactory { public CanTop createTop() { return new BeerCanTop(); } public CanBody createBody() { return new BeerCanBody(); } }

The other case is food canning. We will provide implementations of the CanTop and CanBody class for this case, too.

package com.gkatzioura.design.creational.abstractfactory.food; import com.gkatzioura.design.creational.abstractfactory.CanBody; public class FoodCanBody implements CanBody { public void fill() { }
}
package com.gkatzioura.design.creational.abstractfactory.food; import com.gkatzioura.design.creational.abstractfactory.CanTop; public class FoodCanTop implements CanTop { public void open() { }
}

As the last step, we will provide the abstract factory implementation for cases of food canning.

package com.gkatzioura.design.creational.abstractfactory.food; import com.gkatzioura.design.creational.abstractfactory.CanBody;
import com.gkatzioura.design.creational.abstractfactory.CanTop;
import com.gkatzioura.design.creational.abstractfactory.CanningFactory; public class FoodCanningFactory extends CanningFactory { public CanTop createTop() { return new FoodCanTop(); } public CanBody createBody() { return new FoodCanBody(); } }

What we just did was using the abstract factory pattern in order to create a family of objects regarding the canning process. We insulated the creation process from the usage of the CanTop and CanBody. Also, we were able to create a family of objects without depending on their concrete classes.

You can find the source code at GitHub.

Original Link

The Open/Closed Principle and Strategy Pattern

SOLID are a set of principles formulated by Robert C.Martin that are meant to guide developers to design and implement software that is easily maintainable, clear, and scalable. In other words, following these principles helps us to write a more solid software. These five principles are:

  • The Single Responsibility Principle: A class should have one, and only one, reason to change.
  • The Open Closed Principle: Software entities should be open to extension but closed to modification.
  • The Liskov Substitution Principle: Derived classes must be substitutable for their base classes.
  • The Interface Segregation Principle: Make fine-grained interfaces that are client specific.
  • The Dependency Inversion Principle: Depend on abstraction, not on concretion.

In this article, I want to focus on the second one: The Open/Closed Principle.

Open to Extension

I highly doubt that there are too many software projects that don’t suffer any changes from the time they were designed. Software design is not a straightforward process. This is just a utopian thought in this industry. Any project will suffer some changes, especially in an agile environment. And even if the project is not developed in an agile environment, to design it perfectly from the beginning is almost impossible. At any time, we might need to add new things or have modifications to do, and if the existing components are not open for extension, then any change would imply a big risk.

One of our responsibilities as software developers is to anticipate what could change in what we write. We must focus to find the right abstraction level and the right point of behavior extension. We should not close our code to future extension and tie it to the current behavior because the behavior could always change and evolve. We should anticipate what could change and what could be extended. This does not sound too pragmatic, as this principle doesn’t tell us how to do that, but this doesn’t mean that there aren’t some good practices for respecting the principle.

So keep in mind when you write a software component to make it open to extension.

But Closed for Modification?

If the scope of this principle is to grow the application’s maintainability, why should the components be closed for modification? Every software entity should know how to do what it is designed to do and to do it well. For example, the Collections.sort method knows how to sort everything that implements the Comparable interface. This method is not limited to sorting just integers or just strings — it is not limited to any specific type. If you have a collection of objects that implement the Comparable interface, then you can sort it using the Collections.sort method. The sorting algorithm will work as it was designed, so we can say that it is closed to modification, but the sorting criteria will vary depending on the compareTo method implementation

The implementation of any software entity should be closed for modification. If the behavior changes, we should not change how a specific entity works, we just need to extend it. Just think of the enormous number of software applications that depend on the sort method. It is tested in the real application, it works good, and it is optimal. So if we need to sort a list of another type, should we change the sort method? Of course not!

So its implementation is closed to modification. This is how any software entity should be. But the key point is to let it be open to extension.

General Repeatable Solution to a Commonly Occurring Problem

The OCP is just a principle and not a generic solution. It describes what your entities should respect, but it does not provide a specific solution. The good guys that designed the sort method let it be open to extension by using a Comparable interface. The method sorts a list of Comparable objects and uses the compareTo method as a sorting criterion. But this is just an example. There isn’t a single way of respecting this principle. However, there are some good general patterns that help us to achieve this.

Program by Interface, not by Implementation

For example, if the parameter types of a method are concrete classes, then the method is tightly coupled to those classes. It can’t receive anything else, except instances of that specific type. In this case, the method is not easily open to extension.

Any method should be simple. It should use a single level of abstraction and should do just one thing. If it respects this, then I’m pretty sure it doesn’t need to call all the parameters’ objects methods. In this case, should the method declare a concrete class as a parameter type? No.

You could create an interface that the class implements and set it as the parameter type — exactly as the Collections.sort method. This way, you can use that method with any class that implements that specific interface. It will work the same. It will call the parameters’ methods in the same way, but the behavior could be changed just by sending, as parameters, different implementations without changing the method.

Sure, you can extend that class and send, as parameters, instances of the child class, but since you cannot extend more than one class, it is more flexible (and clear) to just use interfaces.

The strategy pattern is a perfect example for programming by interface, not by implementation.

Design Patterns to the Rescue

A design pattern is a general repeatable solution to a commonly occurring problem. Like someone says here, the design pattern is a cure against diseases, and the diseases, in our case, are violations of SOLID principles. Design patterns are not the only cure, but they are an efficient one. Even if it is not the only design pattern that accomplishes the OCP, one specific pattern seems to be, by definition, extremely fit for this purpose, and that is the strategy pattern. Via the strategy pattern, you encapsulate some specific strategies and select which one to use at runtime according to some criteria. So, by definition, using this pattern makes your code open to extension.

Strategy Pattern

Like I said before, programming by interface and not by implementation is a best practice that we can use to design and implement code open to extension. Also, programming by interface is the key factor of the strategy pattern. It is a behavioral pattern, and, proven by the industry, one of the most useful design patterns. The principle is very simple: Encapsulate the strategies and decide what to use depending on some specific conditions. Following the strategy pattern, the behavior is decoupled by the classes that use it. You can switch between strategies without any class change.

How Does it Work?

The principle is very simple. All the strategy classes must implement a specific strategy interface. The class that uses the strategies, called the context class, is not bound to those specific strategy classes, but it is tied to the strategy interface. The context class encapsulates a strategy that could be injected in multiple ways (using Dependency Injection or the factory pattern or using a simple if condition — see this article for an introduction to Dependency Injection). So this mechanism is open to extension by giving you a way to use different strategies. Meanwhile, it is closed to modification, as the class that uses a strategy does not have to be changed no matter what the strategy is encapsulating.

Let’s see a simple example to better understand it. We will need a strategy interface, a class that uses the strategies, the context class, and some implementation of the strategy interface.

The context class should know just one thing about the strategies — what methods to call. This is what all the strategies have in common, so we will have a strategy interface with just the common methods (in our case, just one method).

public interface Strategy { public void doSomething();
}

And a context class that encapsulates a strategy implementation and executes it.

public class Context() { private Strategy strategy; // we set the strategy in the constructor public Context(Strategy strategy) { this.strategy = strategy; } public void executeTheStrategy() { this.strategy.doSomething(); }
}

And let’s create two implementations for the Strategy interface.

public class Strategy1 implements Strategy { public void doSomething() { System.out.println(“Execute strategy 1”); } } public class Strategy2 implements Strategy { public void doSomething() { System.out.println(“Execute strategy 2”); } }

Now we can bind this together. The idea is to send to the context class the strategy we want to run. Like I said before, you can use Dependency Injection or the factory pattern for this, but it’s out of this article’s scope, so let’s just make a simple Demo class to see how it works.

public class Demo() { public static void main(String[] args) { Context context = new Context(new Strategy1()); // we inject the Strategy1 context.executeTheStrategy(); // it will print “Execute strategy 1”; context = new Context(new Strategy2()); // we inject the Strategy2 context.executeTheStrategy(); // it will print “Execute strategy 2” }
}

So the context is decoupled from a specific strategy class. You could implement however many strategies you want and no matter how they work and what you want them to do, you don’t need to modify the context class. The context class knows just that it must call doSomething method and it is enough.

This is a trivial example, but the possibilities are unlimited. Just think of the advantages you get here, and it is not hard at all to follow this simple pattern. Just use interfaces and let a concrete class to know just what it needs to know about something by tying it to an interface instead to a concrete class. This way, you can extend the behavior just by implementing different strategies and without changing the context class’s functionality.

Conclusion

When you want to write code that follows the OCP, you should not limit yourself just on the strategy pattern or to “program by interface, not by implementation.” By using these best practices, I just wanted to show the power of having something open to extension and closed to modification.

Like I said before, not following this principle is like a disease, but design patterns aren’t the only cure. Strive to find the right abstraction levels and don’t use more than one single level of abstraction in a method. Find the pointcut between those levels, separate the concerns, and see how you can extend the functionality without changing the context classes. And don’t forget to look at the other SOLID principles.

Original Link

Refactoring to Functional Patterns in Scala

If you have a background in Java like me, you’ve probably read Refactoring To Patterns. It’s a very cool book about refactoring that shows you how to refactor Object Orientated code step by step and eventually reach full-blown Gang of Four design patterns. It had a huge impact on me at the time. It left me with the feeling that code is alive and wants to be rearranged this way, and that patterns emerge naturally.

Fast forward 10 years, and I work at a very cool startup (Bigpanda) where we use Scala and functional programming for our back-end services. Working with FP is very different, and in my opinion, far easier. There are no more complicated class hierarchies and no more complicated patterns, only functions, functions, functions. If GoF design patterns are no longer our destination, then the refactoring steps we must take are very different.

In this post, I’ll introduce you to some approaches to refactoring functional code. I will build from simple refactorings to more advanced ones using the State and Writer monads — functional design patterns.

Make Sure You Have a Full Suite of Tests With Good Coverage

Refactoring without tests is like jumping without a safety net. You can use sbt as a very useful continuous test runner:

~testQuick io.company.service.PricingServiceSpec

Each time you save your file, it will recompile it and rerun only the previously failing tests.

Eliminate Primitive Types With Value Classes

Before:

def buy(lastPrice: String, name: String, exchange: String): (Double, Long) = ???

After:

case class Symbol(name: String) extends AnyVal
case class Exchange(name: String) extends AnyVal
case class Price(value: Long) extends AnyVal
case class Timestamp(ts: Long) extends AnyVal def buy2(lastPrice: Price, symbol: Symbol, exchange: Exchange): (Price, Timestamp) = ???

We have a package called types, and we will put all our value classes in a values.scala file. We will also add Ordering implicits there.

implicit val timestampOrdering: Ordering[Timestamp] = Ordering.by(_.ts)

Rewrite on the Side and Then Switch the Functions

Typically, I do not start by deleting old code. First, I write the new function on the side, make sure it compiles, then switch the old ones and make sure the tests pass. This is a very handy trick to let you backtrack quickly if you have an error somewhere.

Align the Types Between Functions

If your functions compose together in a natural way, it means that you have found the right level of abstraction.

Keep them small and focused on one thing, and add type annotations for the return types to increase readability.

If you find that you need to work hard with type transformations to be able to compose your functions together, then try this:

  • Inline, inline, inline, and retry.

After a while, you get that hang of it and your functions will be focused and composed together. You can also do some upfront design.

Personally, I found A Type Driven Approach to Functional Design helpful. It’s in Haskell, but it is still very relevant and will give you a sense of how to design functions that compose together.

Use State Monads for Functions That Need Previously Computed Values

Let’s define some types to work with:

sealed abstract class CreditRating(val rating: Int) case class Good(points: Int) extends CreditRating(points)
case class Fair(points: Int) extends CreditRating(points)
case class Poor(points: Int) extends CreditRating(points) case class PriceEvent(symbol: Symbol, price: Price, ts: Timestamp)

In any meaningful service, you will need previously computed data. You’ll also want to persist it in case you crash or restart your app. This leads to stateful functions.

In order to rate a stock, for example, we need the previous prices and rating. This usually leads to long, ugly parameter lists. Because our data structures are immutable, we have to return new, updated versions of them.

Before:

def rateStock(historicalPrices: Map[Symbol, List[(Timestamp, Price)]], lastRatings: Map[Symbol, CreditRating], symbol: Symbol, newPrice: Price): (Map[Symbol, CreditRating], List[(Timestamp, Price)]) = ???

Quite ugly!

This is a very common pattern in FP. You can use a State monad to communicate to the reader that they are about to deal with stateful functions.

We use cat’s State.

We encapsulate the moving parts in our own defined type:

case class StockState(historicalPrices: Map[Symbol, List[(Timestamp, Price)]], lastRatings: Map[Symbol, CreditRating])

We use State to clean up the parameter list and the return type:

import cats.implicits._
import cats.data.State def rateStatefulStock(symbol: Symbol, newPrice: Price): State[StockState, CreditRating] = ???

We can improve the type readability with a type alias.

After:

type StateAction[A] = State[StockState, A]
def rateStatefulStock(symbol: Symbol, newPrice: Price): StateAction[CreditRating] = ???

The function is far cleaner and can compute and update the ratings from the previous state.

This gives us the ability to chain state functions as follows and be guaranteed that each function receives the correct, latest updated state. Very cool!

for { a <- rateStatefulStock(Symbol("AAPL"), Price(145.5)) // something magical happens here, // it passes on the correct StockState to the next function s <- rateStatefulStock(Symbol("SAMSNG"), Price(2123.3)) } yield (a, s)

Use a Writer Monad to Track State Transitions When Using State

If you work with event sourcing, you will want to recreate your state from all the transitions you carried out. In order to keep track of state transitions without complicating your function, you can use a Writer monad to log all the transitions in a List.

First, let’s define some more types:

sealed trait Transition
case class UpgradedRating(newRating: CreditRating) extends Transition
case class DowngradedRating(newRating: CreditRating) extends Transition

We want to use State and Writer together, so let’s use WriteT to combine them:

import cats.data.WriterT type StateActionWithTransitions[A] = WriterT[StateAction, List[Transition], A]

Use this function to log transitions and add to the final transition list:

def archive(evts: List[Transition]): StateActionWithTransitions[Unit] = WriterT.tell(evts)

Boilerplate to wire up State and Writer together:

def lift[A](s: StateAction[A]): StateActionWithTransitions[A] = WriterT.lift(s)

Pure functions have simple return types that are not wrapped in StateActionWithTransitions. This tells the reader that this function does not change the state.

def calculateRating(stock: Symbol, old: CreditRating, newPrice: Price): CreditRating = if (stock.name == "AAPL") Good(1000) else if(newPrice.value == 0) Poor(0) else Fair(300) def calculateTransition(oldRating: CreditRating, newRating: CreditRating): Transition = if(newRating.rating > oldRating.rating) UpgradedRating(newRating) else DowngradedRating(oldRating)

Stateful functions have the return type StateActionWithTransitions. This tells the reader to pay special care because this function uses or updates the state:

import com.softwaremill.quicklens._ def setNewRating(symbol: Symbol, newRating: CreditRating): StateActionWithTransitions[Unit] = lift(State.modify(_.modify(_.lastRatings).using(_ + (symbol -> newRating)))) def getRating(s: Symbol): StateActionWithTransitions[CreditRating] = lift(State.inspect[StockState, CreditRating](_.lastRatings.get(s).getOrElse(Poor(0))))

Here is the final version of our function:

  • Whenever the reader sees <-, they know to pay special attention, as it is a stateful function
  • Whenever the reader sees =, they know it’s a pure function and nothing related to state happens there
def rateStatefulStock(symbol: Symbol, newPrice: Price): StateActionWithTransitions[CreditRating] = for { oldRating < -getRating(symbol) newRating = calculateRating(symbol, oldRating, newPrice) _ < -setNewRating(symbol, newRating) transition = calculateTransition(oldRating, newRating) _ < -archive(transition::Nil) } yield newRating

Takeaways

  • Before refactoring, make sure you have good tests with decent coverage
  • Strongly type as much as you can. Use meaningful names and abstractions
  • Design your functions so their types align and compose together
  • Use cats’s State data type to write functions that need state
  • Use type aliases to clean up boilerplate types
  • Use cat’s Writer data type to log state transitions

Original Link