Reader small image

You're reading from  Kotlin Design Patterns and Best Practices - Second Edition

Product typeBook
Published inJan 2022
Reading LevelBeginner
PublisherPackt
ISBN-139781801815727
Edition2nd Edition
Languages
Right arrow
Author (1)
Alexey Soshin
Alexey Soshin
author image
Alexey Soshin

Alexey Soshin is a software architect with 15 years of experience in the industry. He started exploring Kotlin when Kotlin was still in beta, and since then has been a big enthusiast of the language. He's a conference speaker, published writer, and the author of a video course titled Pragmatic System Design.
Read more about Alexey Soshin

Right arrow

Chapter 3: Understanding Structural Patterns

This chapter covers structural patterns in Kotlin. In general, structural patterns deal with relationships between objects.

We'll discuss how to extend the functionality of our objects without producing complex class hierarchies. We'll also discuss how to adapt to changes in the future or fix some of the design decisions taken in the past, as well as how to reduce the memory footprint of our program.

In this chapter, we will cover the following patterns:

  • Decorator
  • Adapter
  • Bridge
  • Composite
  • Facade
  • Flyweight
  • Proxy

By the end of this chapter, you'll have a better understanding of how to compose your objects so that they can be simpler to extend and adapt to different types of changes.

Technical requirements

The requirements for this chapter are the same as the previous chapters—you'll need IntelliJ IDEA and the JDK.

You can find the code files for this chapter on GitHub at https://github.com/PacktPublishing/Kotlin-Design-Patterns-and-Best-Practices/tree/main/Chapter03.

Decorator

In the previous chapter, we discussed the Prototype design pattern, which allows us to create instances of classes with slightly (or not so slightly) different data. This raises a question:

What if we want to create a set of classes that all have slightly different behavior?

Well, since functions in Kotlin are first-class citizens (which we will explain in this chapter), you could use the Prototype design pattern to achieve this aim. After all, creating a set of classes with slightly different behavior is what JavaScript does successfully. But the goal of this chapter is to discuss another approach to the same problem. After all, design patterns are all about approaches.

By implementing the Decorator design pattern, we allow the users of our code to specify the abilities they want to add.

Enhancing a class

Let's say that we have a rather simple class that registers all of the captains in the Star Trek universe along with their vessels:

open class StarTrekRepository...

Adapter

The main goal of the Adapter design pattern is to convert one interface to another interface. In the physical world, the best example of this idea would be an electrical plug adapter or a USB adapter.

Imagine yourself in a hotel room late in the evening, with 7% battery left on your phone. Your phone charger was left in the office at the other end of the city. You only have an EU plug charger with a Mini USB cable. But your phone uses USB-C, as you had to upgrade. You're in New York, so all of your outlets are (of course) USB-A. So, what do you do? Oh, it's easy. You look for a Mini USB to USB-C adapter in the middle of the night and hope that you have remembered to bring your EU to US plug adapter as well. Only 5% battery left – time is running out!

So, now that we understand what adapters are for in the physical world, let's see how we can apply the same principle in code.

Let's start with interfaces.

USPlug assumes that power is Int...

Bridge

While the Adapter design pattern helps you to work with legacy code, the Bridge design pattern helps you to avoid abusing inheritance. The way it works is actually very simple.

Let's imagine we want to build a system to manage different kinds of troopers for the Galactic Empire.

We'll start with an interface:

interface Trooper {
    fun move(x: Long, y: Long)
    fun attackRebel(x: Long, y: Long)
}

And we'll create multiple implementations for different types of troopers:

class StormTrooper : Trooper {
    override fun move(x: Long, y: Long) {
        // Move at normal speed
    }
 
    override fun attackRebel(x: Long, y: Long) {
        // Missed most of the time 
    }
}
 
class ShockTrooper : Trooper {
    override fun move(x...

Composite

This chapter is dedicated to composing objects within one another, so it may look strange to have a separate section for the Composite design pattern. As a result, this raises a question:

Shouldn't this design pattern encompass all of the others?

As in the case of the Bridge design pattern, the name may not reflect its true uses and benefits.

Let's continue with our StormTrooper example from before. Lieutenants of the Empire quickly discover that no matter how well equipped, stormtroopers cannot hold their ground against the rebels because they are uncoordinated.

To provide better coordination, the Empire decides to introduce the concept of a squad for the stormtroopers. A squad should contain one or more stormtrooper of any kind, and when given commands, it should behave exactly as if it was a single unit.

Squad, clearly, consists of a collection of stormtroopers:

class Squad(val units: List<Trooper>)

Let's add a couple of them to...

Facade

The use of facade as a term to refer to a design pattern comes directly from building architecture. That is, a facade is the face of a building that is normally made to look more appealing than the rest of it. In programming, facades can help to hide the ugly details of an implementation.

The Facade design pattern itself aims to provide a nicer, simpler way to work with a family of classes or interfaces. We previously discussed the idea of a family of classes when covering the Abstract Factory design pattern. The Abstract Factory design pattern focuses on creating related classes, while the Facade design pattern focuses on working with them once they have been created.

To better understand this, let's go back to the example we used for the Abstract Factory design pattern. In order to be able to start our server from a configuration using our Abstract Factory, we could provide users of our library with a set of instructions:

  • Check if the given file is .json...

Flyweight

Flyweight is an object without any state. The name comes from it being very light. If you've been reading either one of the two previous chapters, you might already be thinking of a type of object that should be very light: a data class. But a data class is all about state.

So, is the data class related to the Flyweight design pattern at all?

To understand this design pattern better, we need to jump back in time some twenty years. Back in 1994, when the original Design Patterns book was published, your regular PC had 4 MB of RAM. During this period, one of the main goals of any process was to save that precious RAM, as you could fit only so much into it.

Nowadays, some cellphones have 8 GB of RAM. Bear that in mind when we discuss what the Flyweight design pattern is all about in this section.

Having said that, let's see how we can use our resources more efficiently, as this is always important!

Being conservative

Imagine we're building a...

Proxy

Much like the Decorator design pattern, the Proxy design pattern extends an object's functionality. However, unlike a decorator, which always does what it's told, having a proxy may mean that when asked to do something, the object does something totally different.

When we discussed Creational Patterns in Chapter 2, Working with Creational Patterns, we already touched on the idea of expensive objects. For example, an object that accesses network resources or takes a lot of time to create.

We at the Funny Cat App provide our users with funny cat images on a daily basis. On our homepage and mobile application, each user sees a lot of pictures of funny cats. When they click or touch any of those images, it expands to its full-screen glory.

Fetching cat images over the network is very expensive, and it consumes a lot of memory, especially if those are images of cats that tend to indulge in a second dessert after dinner. What we want to do is fetch the full-sized...

Summary

In this chapter, we have learned how structural design patterns can help us to create more flexible code that can adapt to changes with ease, sometimes even at runtime. We've covered how we can add functionality to an existing class with the Decorator design pattern, and we've explored how operator overloading can allow us to provide more intuitive syntax to common operations.

We then learned how to adapt one interface to another interface using extension methods, and we also learned how to create anonymous objects to implement an interface only once. Next, we discussed how to simplify class hierarchies using the Bridge design pattern. You should now know how to create a shortcut for a type name with typealias and also how to define efficient constants with const.

Moving on, we looked at the Composite design pattern, and we considered how it could help you to design a system that needs to treat groups of objects and regular objects in the same way. We also learned...

Questions

  1. What differences are there between the implementations of the Decorator and Proxy design patterns?
  2. What is the main goal of the Flyweight design pattern?
  3. What is the difference between the Facade and Adapter design patterns?
lock icon
The rest of the chapter is locked
You have been reading a chapter from
Kotlin Design Patterns and Best Practices - Second Edition
Published in: Jan 2022Publisher: PacktISBN-13: 9781801815727
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Alexey Soshin

Alexey Soshin is a software architect with 15 years of experience in the industry. He started exploring Kotlin when Kotlin was still in beta, and since then has been a big enthusiast of the language. He's a conference speaker, published writer, and the author of a video course titled Pragmatic System Design.
Read more about Alexey Soshin