Reader small image

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

Product typeBook
Published inApr 2024
PublisherPackt
ISBN-139781805127765
Edition3rd Edition
Right arrow
Author (1)
Alexey Soshin
Alexey Soshin
author image
Alexey Soshin

Alexey Soshin is a software architect with 18 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

Understanding Structural Patterns

This chapter examines structural patterns in Kotlin, focusing on how objects are composed and arranged within larger structures. These patterns aim to simplify the interactions and relationships between different components of our system.

We will discuss how to enhance the functionality of our objects without creating complex class hierarchies. Additionally, we’ll explore methods to adapt to future changes and rectify design decisions made in the past. Furthermore, we’ll examine techniques for reducing the memory footprint of our programs.

Throughout this chapter, we will cover the following patterns:

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

By the end of this chapter, you will have a better understanding of how to compose your objects in a way that enables simpler extension and adaptation to various types of changes.

As we have observed in the previous...

Technical requirements

The requirements for this chapter are the same as for 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_Third-Edition/tree/main/Chapter03.

Decorator

In the last chapter, we covered a range of design patterns, including the Prototype design pattern. The Prototype pattern is particularly useful for creating class instances with varying data attributes. But what if our goal extends to developing a collection of classes, each with its own unique behavior?

This section introduces an alternative method for a similar objective. It’s important to remember that design patterns offer a variety of solutions to common software development problems.

By implementing the Decorator design pattern, we empower users of our code to specify the abilities they want to add to their objects. This approach offers a flexible way to extend the behavior of classes without modifying their structure directly.

Having understood the purpose of the Decorator pattern, let’s now explore how it can be applied to augment a class.

Enhancing a class

Let’s consider a straightforward class that registers all the captains...

Adapter

The Adapter design pattern aims to convert one interface into another, much like the physical world examples of electrical plug adapters or USB adapters we encounter in daily life.

Imagine finding yourself in a hotel room late at night, with only 7% battery left on your phone. Your phone charger is in your office at the other end of the city, and all you have is an EU plug charger with a Mini USB cable. However, your phone requires a USB-C connection since you had to upgrade. To add to the challenge, the outlets in New York are USB-A. In this desperate situation, you urgently search for a Mini USB-to-USB-C adapter and hope you also brought your EU-to-US plug adapter. Time is running out as your battery reaches a mere 5%.

Just as adapters help us in the physical world, we can apply the same principle in code to enable compatibility between different interfaces.

Let’s explore how we can achieve this through interfaces. We start with the USPlug interface, which...

Bridge

The Bridge design pattern is a great tool for avoiding the pitfalls of overusing inheritance and creating more flexible, maintainable code. It helps in separating abstractions from their implementations, allowing for better extensibility.

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

We’ll begin by defining an interface to represent the basic behavior of a trooper:

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

With this interface, we establish the fundamental behavior that all troopers should have. Next, we’ll move on to implementing different types of troopers:

open 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
    }
}
open class ShockTrooper : Trooper {
    override fun move(x: Long,...

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 other structural patterns?

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 stormtroopers of any kind, and when given commands, it should behave exactly as if it were a single unit.

Squad, clearly, consists of a collection of stormtroopers:

class Squad(val units: List<Trooper>)
...

Facade

The term “facade” used in the context of a design pattern takes inspiration from building architecture. In architecture, a facade refers to the appealing front of a building, which often looks more attractive than the rest of the structure. In programming, facades are like helpers that hide the complex inner workings of an implementation.

The Facade design pattern itself aims to offer a more user-friendly and straightforward way of working with a group of related classes or interfaces. This idea of related classes was discussed when we covered the Abstract Factory design pattern. While the Abstract Factory pattern is about creating related classes, the Facade pattern focuses on simplifying their usage once they’ve been created.

To better grasp this concept, let’s revisit the example we used for the Abstract Factory pattern. To allow users of our library to start a server based on a configuration using our Abstract Factory, we could provide...

Flyweight

Flyweight is a term used to describe an object that lacks inherent state. The term “flyweight” implies its lightweight nature. If you’ve been following the content in the preceding chapters, you might already be envisioning a certain type of object that should be lightweight: a data class. However, a data class primarily revolves around maintaining state.

So, can we draw any connections between the data class concept and the Flyweight design pattern?

To gain a deeper comprehension of this design pattern, let’s rewind the clock by about two decades. In 1994, when the original “Design Patterns” book was published, a standard personal computer boasted a mere 4 MB of RAM. During that era, conserving RAM was a paramount objective of any process, given the limited capacity for storage.

Fast forward to today, where even some smartphones now boast an impressive 8 GB of RAM. It’s important to keep this evolution in mind...

Proxy

Similar to the Decorator design pattern, the Proxy design pattern enhances an object’s capabilities. However, unlike a decorator that consistently follows instructions, a proxy might take a different route and behave differently when given a task.

In our earlier discussions about creational patterns in Chapter 2, where we explored working with such patterns, we already touched upon the concept of costly objects. These could be objects that rely on network resources or demand substantial time for creation.

Consider our scenario with the Funny Cat App, where we offer users daily doses of humorous cat images. On both our homepage and mobile app, users encounter an array of funny cat pictures. When they interact with these images, they’re presented in their full-screen glory.

The challenge lies in fetching these cat images from the network, a resource-intensive process, particularly if we’re dealing with images of cats that have a penchant for post...

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

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?

Learn more on Discord

Join our community’s Discord space for discussions with the author and other readers:

https://discord.com/invite/xQ7vVN4XSc

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Kotlin Design Patterns and Best Practices - Third Edition
Published in: Apr 2024Publisher: PacktISBN-13: 9781805127765
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 €14.99/month. Cancel anytime

Author (1)

author image
Alexey Soshin

Alexey Soshin is a software architect with 18 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