Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Practical Design Patterns for Java Developers
Practical Design Patterns for Java Developers

Practical Design Patterns for Java Developers: Hone your software design skills by implementing popular design patterns in Java

eBook
£17.99 £25.99
Paperback
£24.99 £31.99
Subscription
Free Trial
Renews at £16.99p/m

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
Table of content icon View table of contents Preview book icon Preview Book

Practical Design Patterns for Java Developers

Getting into Software Design Patterns

Every software architect or developer often faces the challenges of structuring code – how to develop a code structure that remains sustainable, just as an artist draws their painting. This chapter will take us on a journey into writing program code. You will explore the challenges behind the structure of code and its organization. Together, we will approach the topic from an early stage described by the pillars of object-oriented programming, known as APIE. We will also review the principles of SOLID to gain clarity in understanding design patterns.

In this chapter, we will cover the following topics:

  • Code – from symbols to program
  • Examining OOP and APIE
  • Understanding the SOLID design principles
  • The significance of design patterns
  • Reviewing what challenges design patterns solve

By the end of this chapter, you will have reviewed the basic programming concepts, which will form the basis of the rest of the book.

Technical requirements

Code – from symbols to program

Human speech is fruitful, rich, colorful, and way beyond what the words themselves may express. Nouns, verbs, and adjectives for precisely expressing a moment or action can be used. In contrast, machines do not understand the complex constructions or expressions that humans are able to create.

Machine language is limited, well-defined, extremely specific, and simplified. Its goal is to provide the precise expression of intent for which it is designed. This contrasts with human language whose purpose is just communication and not necessarily with specifics.

A machine’s intent can be expressed as a defined instruction or a set of them. This means that machines understand the instructions. These instructions must be available to the machine in some form at the time of execution. Each machine normally has a set of instructions. Based on this kind of instruction set, machines can perform the required instructions, as shown here:

Figure 1.1 – A simplified instruction cycle inside the CPU (instruction is taken from memory and the result is stored)

Figure 1.1 – A simplified instruction cycle inside the CPU (instruction is taken from memory and the result is stored)

Let us explore one individual instruction. The instruction can be understood as a command given to the processor. The processor is the heart of the machine, or the center of the ordering and executing of processes. The machine may contain one or more of them. It depends on its design, but in any case, there is always one that takes the lead. For further simplification, we will only consider one – that is, consider a system that only has one central processing unit (CPU) dedicated to executing a program.

A CPU is a device that executes instructions containing a computer program. The CPU must contain such an instruction set, as shown in the previous diagram, to process the requested action.

Because instructions can take completely different forms depending on the CPU, there is no defined standard. This promotes different CPU platforms, which is not necessarily a bad thing and contributes to evolution. However, the fact remains that the instructions are not easy for people to read.

We have stated that machines can perform instruction collection, ideally as a continuous flow. The flow of instructions can be simplified as a queue in memory, where one instruction goes in and the other leaves. The CPU plays the role of an interpreter who works with this memory cyclically (as we saw in Figure 1.1). Okay, so the CPU interprets, but as the instructions are added to the memory, where do they come from, and how can such a stream be created?

Let us gather some thoughts. Machine instructions, in most cases, originate from a compiler.

What is a compiler? The compiler can be viewed as a CPU or a platform-specific program that translates text into target actions. The text we use to call the program and the result could be named machine code. The following diagram illustrates this:

Figure 1.2 – A simplified platform-specific flow from the source code through the compiler program to its resultant action

Figure 1.2 – A simplified platform-specific flow from the source code through the compiler program to its resultant action

Machine code is a low-level language that the machine understands and consists of language instructions that are processed sequentially (see Figure 1.1); the program was compiled, executed, and run.

In the case of Java, there is no machine code:

Figure 1.3 – A simplified flow for the Java program through the compiler to its platform execution

Figure 1.3 – A simplified flow for the Java program through the compiler to its platform execution

The source code is compiled by the Java compiler into bytecode. The bytecode is running a Java virtual machine (JVM) (see Figure 1.3). In this situation, the JVM plays the role of the interface between the bytecode and the actual instructions that are executed on the CPU. The JVM emulates a bytecode instruction. It does this using the just-in-time (JIT) compiler that is part of the JVM. The JIT compiler translates bytecode instructions into native processor instructions. The JVM is a platform-specific interpreter, analogous to directly compiled code (see Figure 1.2). The JVM also provides additional features such as memory management and garbage collection, which is what makes the Java platform so powerful. All these features allow developers to write code once, compile it into bytecode, and run a supported platform – known as write once, run anywhere (WORA).

In the context of the previous exploration, Java is a high-level language that is translated to a low level. Java provides a strong abstraction from the details of computer functionality. It allows programmers to create simpler programs for complex challenges.

At this point, we begin our journey of jointly exploring standardized solutions. Later in the book, we will review how to create code that is maintainable and extensible with fewer memory requirements. Together, we will discuss different types of design patterns that can help us to make our daily work understandable, transparent, and more fun.

Examining OOP and APIE

In the previous section, we learned how a program written in one of the high-level languages is converted into machine instructions that are processed by the CPU. The high-level language provides a framework for expressing the desired ideas by following the details of the language implementation. Such languages commonly provide many neat constructions or statements that do not limit the imagination. In object-oriented programming (OOP) language, the representation of the core carrier is presented by the concept of the object. This book focuses on the Java language. Java is a fully object-oriented language with additional features. What does object-oriented language mean exactly? In computer science, this means that the program focuses on the concept of classes, where instances of these classes represent an object. Next, we will repeat the importance of the OOP paradigm and deal with some basic concepts.

These terms can be expressed by the abbreviation of abstraction, polymorphism, inheritance, and encapsulation (APIE). The letters APIE indicate the four basic pillars of OOP languages. Let’s examine each word in a separate section in reverse order – so, EIPA. The motivation is to bring more clarity to our understanding of the concept of OOP.

Only exposing what’s required – encapsulation

The first in reverse order is encapsulation – let’s start with it. OOP languages, including Java, work with the concept of classes. Imagine that a class is a vehicle. The class provides all the fields that can be statically typed or object-specific – that is, initiated after an object is instantiated in the allocated memory. The concept is similar with respect to class or object methods. The method may belong to a class or its instance – in the considered example, to a vehicle. Any method can work over an object or class field and change the internal state of the vehicle or the field values (see Example 1.1):

public class Vehicle {
    private boolean moving;
    public void move(){
        this.moving = true;
        System.out.println("moving...");
    }
    public void stop(){
        this.moving = false;
        System.out.println("stopped...");
    }
}

We can apply encapsulation to the example of a vehicle. We imagine a real vehicle – only one. In such an imaginary vehicle, all internal elements and internal functions remain hidden from the driver. It only exposes the functionality it serves, such as the steering wheel, which the driver can control. This is the general principle of encapsulation. The state of an instance can be changed or updated through exposed methods or fields; everything else is hidden from the outside world. It is quite a good practice to use methods to modify the inner array or arrays of an instance. But we will repeat that later in this book. So far, it’s just a good hint.

Inevitable evolution – inheritance

In the previous section, an instance of an imaginary vehicle class was created. We encapsulated all the functions that should not be exposed to the driver. This means that the driver may not know how the engine works, only how to use it.

This section is devoted to the property of inheritance, which we will demonstrate in the following example. Assume that the vehicle’s engine is broken. How can we replace it? The goal is to replace the current one with a functional one. An engine that works this way may not necessarily be the same, especially if the vehicle model already has old parts that are not available on the market.

What we do is derived from all the attributes and functions needed to create a new engine. Concerning the class, the new replacement module will be a child in the class hierarchy.

Although the engine will not be a perfect replica and does not have the same unique object identifier, it will match all the parent properties.

With that, we have described the second pillar of inheritance in OOP – the ability to create a new class above the existing subclass. However, software designers should be wary of the fourth pillar, encapsulation, and any violations caused by a subclass depending on the implementation details of its superclass.

Behavior on demand – polymorphism

The third concept is polymorphism. With a little imagination, this can be understood as “many forms.” So, what does that mean here?

Given the vehicle described previously, it could be defined as the ability to perform a particular action in many ways. This would mean, in the context of a vehicle, that the movement of the other method, move, could happen differently based on the inputs or the state of the instance.

Java allows for two types of polymorphism, both of which differ in their runtime behavior. We will discuss both in detail.

Method overloading

This type is known as static polymorphism. This means that the correct method is resolved during program compilation – so, at compile time. Java provides two types of method overloads:

  • Changing the input argument type:
Figure 1.4 – Overloading the method of the Vehicle class by changing the input types

Figure 1.4 – Overloading the method of the Vehicle class by changing the input types

  • Changing the number of method arguments:
Figure 1.5 – Overloading the method of the Vehicle class by changing the number of arguments

Figure 1.5 – Overloading the method of the Vehicle class by changing the number of arguments

Now, let’s look at the second type of polymorphism.

Method overriding

This is sometimes called dynamic polymorphism. This means that the method performed is known at runtime. The overridden method is called through reference to the object instance of belongingness. Let us examine a simple example to illustrate this. Consider the Vehicle class a parent class (see Figure 1.6 and Example 1.2) with a method called move:

Figure 1.6 – The relation between the overridden move methods for the parent and child classes

Figure 1.6 – The relation between the overridden move methods for the parent and child classes

We intend to create a child class, Car, with a similar method named move. The child provides slightly different functions because the Car instance moves faster than the parent instance, Vehicle:

public class Vehicle {
    public void move(){
        System.out.println("moving...");
    }
}
public class Car extends Vehicle {
    @Override
    public void move(){
        System.out.println("moving faster.");
    }
}
Vehicle vehicle = new Car();
vehicle.move();
output: moving faster...

We will touch on this topic in more detail in Chapter 3, Working with Creational Design Patterns.

Standard features – abstraction

The last letter to cover (but the first letter in the abbreviation APIE) leads us to the hitherto unspecified pillar of abstraction. The key to this concept is the constant removal of specifics or individual details to achieve the generalization of the purpose of the object.

To get the best experience with this concept, let us get into the context with the vehicle example. We do not intend to describe a specific car model that belongs to a group of vehicles. Our goal is to define a common functionality that all types of vehicles under consideration can include in the context of our efforts. With such knowledge, we create a suitable abstraction, an abstract class that can be inherited later when constructing a particular model class (see Example 1.3).

This approach allows us to focus our efforts on generalizing and abstracting vehicle characteristics. This can have a positive impact on code reduction and reusability.

The abstraction in Java can be achieved in two ways:

  • Abstract classes with abstract methods (see Example 1.3 and Figure 1.7):
Figure 1.7 – The AbstractVehicle class with its CommonCar realizations and SportCar classes

Figure 1.7 – The AbstractVehicle class with its CommonCar realizations and SportCar classes

public abstract class AbstractVehicle {
    abstract public void move();
    public void stop(){
        System.out.println("stopped...");
    }
}
public class CommonCar extends AbstractVehicle{
    @Override
    public void move() {
        System.out.println("move slow...");
    }
}
public class SportCar extends AbstractVehicle{
    @Override
    public void move() {
        System.out.println("move fast...");
    }
}
  • Using interfaces (see Example 1.4 and Figure 1.8) with a generic abstract method:
Figure 1.8 – The abstraction concept achieved by using interfaces

Figure 1.8 – The abstraction concept achieved by using interfaces

public interface VehicleInterface {
    void move();
}
public class Truck implements VehicleInterface{
    @Override
    public void move() {
        System.out.println("truck moves...");
    }
}
public class Bus implements VehicleInterface{
    @Override
    public void move() {
        System.out.println("bus moves...");
    }
}

Both concepts of abstraction can be combined (see Figure 1.9):

Figure 1.9 – A combination of both abstraction concepts

Figure 1.9 – A combination of both abstraction concepts

Abstract classes and interfaces have their place in the design of code structure. Their use depends on demand, but both have a very positive impact on code maintainability and help in the use of design patterns.

Gluing parts to APIE

The motivation for each of the pillars mentioned in the previous sections is to introduce structure into the code through a given set of concepts. The pillars are defined and complementary. Let’s just examine one unit, the Vehicle class, and its instance. Instance logic and data are encapsulated and exposed through methods to the outside world. Vehicle characteristics can be inherited so that a new vehicle design, such as a new model, can be specified. Exposed methods can provide model-based behavior and incoming arguments with internal instance state changes. When crystalizing thoughts about a new vehicle, we can always generalize its behavior and extract it using an abstract class or interface.

Let us examine the generalization process over the Vehicle class development. When preparing to define a new vehicle model, we can always generalize its characteristics and extract it using an abstract class or interface. Let’s look at the following diagram:

Figure 1.10 – APIE viewed as a continual improvement process

Figure 1.10 – APIE viewed as a continual improvement process

Although these four pillars seem trivial, it is incredibly difficult to follow them, as we will continue to show in the following sections and chapters.

So far in this section, we learned about the four basic pillars of OOP and examined how these principles affect code design. Next, we will learn more about sustainable code design concepts. Let us roll on to the following section.

Understanding the SOLID design principles

In the previous sections, the idea of structured work was introduced. The development pillars of APIE were elaborated on in detail using examples. You have gained a foundational understanding of the concept of class instances in terms of object-oriented principles and how we can create different types of specific objects:

Figure 1.11 – Vehicle N, where N is a positive integer number, represents an instance of the Vehicle class

Figure 1.11 – Vehicle N, where N is a positive integer number, represents an instance of the Vehicle class

Classes can be instantiated so that an instance becomes an object. The object must fit into free memory. We say that the object allocates memory space. When Java is considered, allocated memory is virtual space inside the physical system’s memory.

Just a small note – we previously discussed the existence of the JVM, an interpreter of compiled bytecode for the required platform (see Figure 1.3). We mentioned other JVM features, one of which is memory management. In other words, the JVM assumes responsibility for allocating virtual memory space. This virtual memory space can be used to allocate an instance of a class. This virtual memory and its fragmentation are taken care of by the JVM and an unused object cleans up the selected garbage collection algorithm, but this is beyond the scope of this book and would be the subject of further study (see Reference 1).

Every programmer, although it may not be obvious at first glance, plays the role of a software designer. The programmer creates the code by writing it. The code carries an idea that is semantically transformed into action depending on the text entered.

Over time, software development has gone through many phases and many articles have been written and published on software maintenance and reusability. One of the milestones in software development may be considered the year 2000 when Robert C. Martin published his paper on Design Principles and Design Patterns (see Reference 2). The paper reviews and examines techniques in the design and implementation of software development. These techniques were later simplified in 2004 into the mnemonic acronym SOLID.

The goal of the SOLID principles is to help software designers make software and its structure more sustainable, reusable, and extensible. In the following sections, we will examine each of the individual terms hidden after the initial letter in the abbreviation SOLID.

The single-responsibility principle (SRP) – the engine is just an engine

The first principle is a well-defined class goal. We can say that each class should have only one reason to exist. As in, it has the intention and responsibility for only one part of the functionality. The class should encapsulate this part of the program. Let’s put this in the context of an example. Imagine the previous example of a vehicle and its abstraction. We are now extending this class with the Engine and VehicleComputer classes, as shown:

Figure 1.12 – The Vehicle class instance using Engine and VehicleComputer realization but an engine functionality does not interfere with the lights

Figure 1.12 – The Vehicle class instance using Engine and VehicleComputer realization but an engine functionality does not interfere with the lights

The engine can start and stop, but the instance of the Engine class cannot control vehicle lights, for example. The light control is the responsibility of the vehicle computer class instance.

The open-closed principle (OCP)

This principle states that the class or entity under consideration should be open to extension but closed to modifications. It goes hand in hand with the concepts already mentioned. Let’s put this in the context of an example where we consider the Car and Truck classes. Both classes inherit the Vehicle interface. Both believe that vehicle entities have a move method.

By not thinking about proper abstraction and without respecting the OCP, code can easily bear unexpected difficulties when classes are not easy to reuse or cannot be handled (see Example 1.5):

public interface Vehicle {}
public class Car implements Vehicle{
    public void move(){}
}
public class Truck implements Vehicle {
    public void move(){}
}
-- usage --
List<Vehicle> vehicles = Arrays.asList(new Truck(), new 
    Car());
vehicles.get(0).move() // ERROR, NOT POSISBLE!

The correction of the example at hand is very trivial in this case (see Example 1.6):

public interface Vehicle {
    void move();    // CORRECTION!
}
--- usage ---
List<Vehicle> vehicles = Arrays.asList(new Truck(), new 
    Car());
vehicles.get(0).move() // CONGRATULATION, ALL WORKS!

Obviously, as code evolves, non-compliance leads to unexpected challenges.

The Liskov Substitution Principle (LSP) – substitutability of classes

The previous sections dealt with inheritance and abstraction as two of the key pillars of OOP. It will come as no surprise to those of you who have read carefully that, given the class hierarchy of parent-child relationships, a child may be replaced or represented by its parent and vice versa (see Example 1.7). Let us look at the example of CarWash, where you can wash any vehicle:

public interface Vehicle {
    void move();
}
public class CarWash {
    public void wash(Vehicle vehicle){}      
}
public class Car implements Vehicle{
    public void move(){}
}
public class SportCar extends Car {}
--- usage ---
CarWash carWash = new CarWash();
carWash.wash(new Car());
carWash.wash(new SportCar());

This means that classes of a similar type can act analogously and replace the original class. This statement was first mentioned during a keynote address by Barbara Liskov in 1988 (see Reference 3). The conference focused on data abstraction and hierarchy. The statement was based on the idea of substitutability of class instances and interface segregation. Let’s look at interface segregation next.

The interface segregation principle (ISP)

This principle states that no instance of a class should be forced to depend on methods that are not used or in their abstractions. It also provides instructions on how to structure interfaces or abstract classes. In other words, it controls how to divide the intended methods into smaller, more specific entities. The client could use these entities transparently. To point out a malicious implementation, consider Car and Bike as children of the Vehicle interface, which shares all the abstract methods (see Example 1.8):

public interface Vehicle {
    void setMove(boolean moving);
    boolean engineOn();
    boolean pedalsMove();
}
public class Bike implements Vehicle{
    ...
    public boolean engineOn() {
        throw new IllegalStateException("not supported");
    }
    ...
}
public class Car implements Vehicle {
    ...
    public boolean pedalsMove() {
        throw new IllegalStateException("not supported");
    }
}
--- usage ---
private static void printIsMoving(Vehicle v) {
    if (v instanceof Car) { 
        System.out.println(v.engineOn());}
    if(v instanceof Bike) 
        {System.out.println(v.pedalsMove());}
}

Some of you with a keen eye will already notice that such a software design direction negatively involves software flexibility through unnecessary actions that need to be considered (such as exceptions). The remedy is based on compliance with the ISP in a very transparent way. Consider two additional interfaces, HasEngine and HasPedals, with their respective functions (see Example 1.9). This step forces the printIsMoving method to overload. The entire code becomes transparent to the client and does not require any special treatment to ensure code stability, with exceptions as an example (as seen in Example 1.8):

public interface Vehicle {
    void setMove(boolean moving);
}
public interface HasEngine {
    boolean engineOn();
}
public interface HasPedals {
    boolean pedalsMove();
}
public class Bike implements HasPedals, Vehicle {...}
public class Car implements HasEngine, Vehicle {...}
--- usage --- 
private static void printIsMoving(Vehicle v){
    // no access to internal state
}
private static void printIsMoving(Car c) {
    System.out.println(c.engineOn());
}
private static void printIsMoving(Bike b) {
    System.out.println(b.pedalsMove());
}

Two interfaces, HasEngine and HasPedals, are introduced, which enforce method code overload and transparency.

The dependency inversion principle (DIP)

Every programmer, or rather software designer, will face the challenge of hierarchical class composition throughout their careers. The following DIP is a remarkably simple guide on how to approach it.

The principle suggests that a low-level class should not know about high-level classes. In the opposite direction, this means that the high-level classes, the classes that are above, should have no information about the basic classes at lower levels (see Example 1.10, with the SportCar class):

public interface Vehicle {}
public class Car implements Vehicle{}
public class SportCar extends Car {}
public class Truck implements Vehicle {}
public class Bus implements Vehicle {}
public class Garage {
    private List<Vehicle> parkingSpots = new ArrayList<>();
    public void park(Vehicle vehicle){
        parkingSpots.add(vehicle);
    }
}

It also means that the implementation of a particular functionality should not depend on specific classes, but rather on their abstractions (see Example 1.10, with the Garage class).

Significance of design patterns

The previous sections introduced two complementary approaches to software design – APIE and SOLID concepts. It has begun to crystallize that having code in a transparent form can be beneficial for a variety of reasons, because every programmer often, if not always, faces the challenge of designing a piece of code that extends or modifies existing ones.

One wise man once said, “The way to Hell is the path of continual technical debt ignorance....” Anything that slows down or prevents the development of applications can be considered a technical debt. Translated into a programming language, this would mean that even a small part matters, if not now, then later. It also follows that code readability and purpose are crucial to application logic, as it is possible to verify various hypotheses (for example, application operation).

The inability to perform business-oriented application testing can be considered the first sign of incorrect development trends. It may appear to require the use of different mock-up techniques during verification. This approach can easily turn into providing false-positive results. This can usually be caused by the clutter of the code structure, which forces programmers to use mocks.

Although the SOLID and APIE concepts suggest several principles, they still do not guarantee that the project code base will not start to rot. Adherence to these principles makes it difficult, but there is still room because not all concepts provide the required framework for dealing with rot.

There may be long stories of how software can rot over time, but one fact that remains is that there is a cure for avoiding it or letting it go. The cure is covered by an idea called design patterns. The idea of a design pattern not only covers the readability of the code base and its purpose but also advances the ability to verify required business hypotheses.

What are the ideas behind defining it to get more clarity? The design pattern idea can be described as a set of reusable coding approaches that solve the most common problems encountered during application development. These approaches are in line with the previously mentioned APIE or SOLID concepts and have an incredibly positive impact on bringing transparency, readability, and testability to the development path. Simply put, the idea of design patterns provides a framework for accessing common challenges in software design.

Reviewing what challenges design patterns solve

Take a deep breath and think about the motivation for writing the program. The program is written in a programming language, in our case, Java, and is a human-readable form to address a specific challenge. Let’s look at it from a different perspective.

We can state that writing a program is considered a goal. The goal has its reason defined by known needs or requirements in most cases. Expectations and limitations are defined. When the goal is known, each action is chosen with the aim of achieving it. The goal is evaluated, organized, and placed in the context of the destination, where the destination means a work program addressing the required challenge. Imagine all the difficulties mentioned in the previous sections.

Day after day, a new solution is posed, instead of a transparent solution. Every day, another local success keeps the project afloat, despite everything looking good on the surface.

Currently, most teams follow the SCRUM framework. Imagine a situation where the team follows the SCRUM framework (see Reference 4) and application development begins to deviate from the goal. Daily standup meetings run smoothly from time to time: it is mentioned that a fundamental error has been found. A few days later, the bug is successfully fixed with great applause. Interestingly, the frequency of such notifications is growing – more corrections, more applause. But does this really mean that the project is moving towards its goal? Does this mean that the application works? Let’s look at the answer.

There is a darker side – the backlog is growing with features and technical debt. Technical debt is not necessarily a terrible thing. Technical debt can stimulate the project and can be especially useful in the concept validation phase. The problem with technical debt occurs when it is not recognized, ignored, and poorly evaluated – even worse when technical debt starts being labeled as new features.

Although the product backlog should be one entity, it begins to consist of two different and unfortunately incompatible parts – the business and the sprint backlog (mostly technical debt). Of course, the team is working on a sprint backlog that comes from planning meetings, but with increasing technical debt, there is less and less room for the relevant business functions of the product. The trends observed in this way can result in extremely tricky situations during each new sprint planning session, where the development resources should be allocated. Let’s stop for a moment and recall this situation where the team cannot move the product forward due to technical debt.

The values of the SCRUM methodology can be simplified to courage, concentration, determination, respect, and openness. These values are not specific to the SCRUM framework. Because the team’s motivation is to deliver the product, they all sound very logical and fair.

We will now refresh our memory of the state the team has achieved. A state where it cannot move the project forward and struggles with the definition and proper consolidation of technical departments. This means that the team is doing its job, but may deviate from achieving its ultimate goal. Every discussion is extremely difficult because it is difficult to solve and describe the problem correctly for many different reasons. It may seem that developers may lose their language of communication and begin to misunderstand each other. We can see that the entropy of the software has increased because the coherence is not maintained. The project is beginning to rot and convergence to the inevitable wasted development time increases.

Let us take another deep breath and think together about how to prevent such a situation. It must be possible to identify these tendencies. Usually, each team has some commonality: the team is not always homogeneous in terms of knowledge, but this should not prevent us from identifying the degradation of the learning curve.

The project learning curve can help us identify a rotting project. Instead of gradual improvements towards the goal, the team experiences local successes full of technical repairs and solutions. Such successes do not even correspond to the values of SCRUM and gradual improvement seems unlikely. The solution may not be considered an improvement because it is specific to a particular movement and may violate the specifications of the technology used. During the solution period, the team may not acquire any useful knowledge applicable to the future. This can soon be considered a missing business opportunity due to the inability to supply business elements or only parts of them.

In addition to the degradation of the learning curve, other symptoms can be identified. This can be described as an inability to test a business function. Project code is proving sticky, dependencies are out of control, which can also harm code readability, testability, and, of course, programmer discipline. The daily goal of the software designer can be reduced to closing a ticket.

To avoid getting to this state, this book will provide some guidelines for solving the most common problems in the following chapters by introducing and questioning different types of design patterns. The design patterns are in line with the aforementioned basic pillars of OOP and APIE and promote the principles of SOLID.

What’s more, design patterns can highlight any misunderstood directions and enforce the don’t repeat yourself (DRY) principle. As a result, there is much less duplication, code testability, and more fun on the project.

That brings us to the end of this chapter.

Summary

Before we embark on the journey of researching design patterns, let us quickly summarize. This chapter has expanded or improved our understanding of various areas. Each of these areas affects program code from different perspectives:

  • Code transparency and readability
  • The ability to solve complex challenges
  • Following SOLID and OOP principles
  • Code testability (it’s possible to verify the purpose of the code)
  • Easy to extend and modify
  • Supporting continual refactoring
  • Code is self-explanatory

The program code is written – well done. The next chapter will take us through a survey of the implementation platform – in our case, the Java platform. We will learn in more detail how and what it means to run a program.

Questions

  1. What interprets the Java code to the platform and how?
  2. What does the acronym APIE represent?
  3. What types of polymorphism does the Java language allow?
  4. What principle helps software designers to produce maintainable code?
  5. What does the OCP mean?
  6. What should be considered about design patterns?

Further reading

  • The Garbage Collection Handbook: The Art of Automatic Memory Management, Anthony Hosking, J. Eliot B. Moss, and Richard Jones, CRC Press, ISBN-13: 978-1420082791, ISBN-10: 9781420082791, 1996.
  • Design Principles and Design Patterns, Robert C. Martin, Object Mentor, 2000.
  • Keynote address - data abstraction and hierarchy, Barbara Liskov, https://dl.acm.org/doi/10.1145/62139.62141, 1988.
  • The SCRUM framework, https://www.scrum.org/, 2022.
Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Decouple logic across objects with dependency injection by creating various vehicles with features
  • Finalize vehicle construction by chaining handlers using the Chain of Responsibility Pattern
  • Plan and execute an advanced vehicle sensor initiation with the Scheduler Pattern

Description

Design patterns are proven solutions to standard problems in software design and development, allowing you to create reusable, flexible, and maintainable code. This book enables you to upskill by understanding popular patterns to evolve into a proficient software developer. You’ll start by exploring the Java platform to understand and implement design patterns. Then, using various examples, you’ll create different types of vehicles or their parts to enable clarity in design pattern thinking, along with developing new vehicle instances using dedicated design patterns to make the process consistent. As you progress, you’ll find out how to extend vehicle functionalities and keep the code base structure and behavior clean and shiny. Concurrency plays an important role in application design, and you'll learn how to employ a such design patterns with the visualization of thread interaction. The concluding chapters will help you identify and understand anti-pattern utilization in the early stages of development to address refactoring smoothly. The book covers the use of Java 17+ features such as pattern matching, switch cases, and instances of enhancements to enable productivity. By the end of this book, you’ll have gained practical knowledge of design patterns in Java and be able to apply them to address common design problems.

Who is this book for?

If you are an intermediate-level Java developer or software architect looking to learn the practical implementation of software design patterns in Java, then this book is for you. No prior knowledge of design patterns is required, but an understanding of Java programming is necessary.

What you will learn

  • Understand the most common problems that can be solved using Java design patterns
  • Uncover Java building elements, their usages, and concurrency possibilities
  • Optimize a vehicle memory footprint with the Flyweight Pattern
  • Explore one-to-many relations between instances with the observer pattern
  • Discover how to route vehicle messages by using the visitor pattern
  • Utilize and control vehicle resources with the thread-pool pattern
  • Understand the penalties caused by anti-patterns in software design

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Feb 03, 2023
Length: 266 pages
Edition : 1st
Language : English
ISBN-13 : 9781804613320
Category :

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning

Product Details

Publication date : Feb 03, 2023
Length: 266 pages
Edition : 1st
Language : English
ISBN-13 : 9781804613320
Category :

Packt Subscriptions

See our plans and pricing
Modal Close icon
£16.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
£169.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just £5 each
Feature tick icon Exclusive print discounts
£234.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just £5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total £ 98.97 105.97 7.00 saved
Test-Driven Development with Java
£35.99
Practical Design Patterns for Java Developers
£24.99 £31.99
50 Algorithms Every Programmer Should Know
£37.99
Total £ 98.97 105.97 7.00 saved Stars icon

Table of Contents

13 Chapters
Part 1: Design Patterns and Java Platform Functionalities Chevron down icon Chevron up icon
Chapter 1: Getting into Software Design Patterns Chevron down icon Chevron up icon
Chapter 2: Discovering the Java Platform for Design Patterns Chevron down icon Chevron up icon
Part 2: Implementing Standard Design Patterns Using Java Programming Chevron down icon Chevron up icon
Chapter 3: Working with Creational Design Patterns Chevron down icon Chevron up icon
Chapter 4: Applying Structural Design Patterns Chevron down icon Chevron up icon
Chapter 5: Behavioral Design Patterns Chevron down icon Chevron up icon
Part 3: Other Essential Patterns and Anti-Patterns Chevron down icon Chevron up icon
Chapter 6: Concurrency Design Patterns Chevron down icon Chevron up icon
Chapter 7: Understanding Common Anti-Patterns Chevron down icon Chevron up icon
Assessments Chevron down icon Chevron up icon
Index Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Full star icon Half star icon 4.7
(20 Ratings)
5 star 90%
4 star 0%
3 star 0%
2 star 5%
1 star 5%
Filter icon Filter
Top Reviews

Filter reviews by




Vessi Mar 10, 2023
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Practical Design Patterns for Java is not just a book. It is a gift you can give to your future self or someone you believe in. It is the perfect companion for people new to Java and the JVM, as well as seasoned Java engineers. Unlike most programming language books, this one mixes in the topic of Design Patterns in the context of the Java ecosystem. You can find Java books or ones that teach software design, but having both uniquely intertwined makes this one so unique.What is more, it brought me back to my memories. I first encountered the topic of Design Patterns as a CS student back in the early 2000s. I was at a crossroads, wondering what to do with my life when a Software Engineering course changed my future trajectory forever. While I could write code before that, Patterns made things click on a fundamentally different level. I was now not only seeing code as such but as building blocks that could help me communicate the client's requirements and my solution.So many years after, I am holding Practical Design Patterns for Java Developers and feeling as excited as I was back in that first Software Engineering lecture. Over the years as a Java developer, I have collected a set of techniques I'd rely on but never formally documented them. I believe the same applies to most of the other developers I know of. Thanks to Miroslav's book, we now have a fantastic reference and a teaching tool we can use when onboarding Java engineers in our teams.The Java ecosystem evolves fast, and we may eventually need Part 2 of this book to cover the new additions. In particular, I can see how the recent moves towards better immutability (records, value classes, pattern matching) and concurrency based on Project Loom's green threads will bring new exciting challenges and opportunities. But as we stand right now, Design Patterns for Java Developers is up to the most recent developments in Java.
Amazon Verified review Amazon
Johannes Bechberger Mar 16, 2023
Full star icon Full star icon Full star icon Full star icon Full star icon 5
When I first read the book’s title, I thought, "another book on the good old design patterns,” and wondered why I should bother reading it. But I was wrong; this book gives you far more than a short descriptive compilation of design patterns. It gives you a great introduction to modern Java features and common pitfalls, too; this book is valuable even if you are not interested in design patterns. The author did an excellent job introducing the Java Runtime Environment, with many pointers to other superior reading materials.The chapters on design patterns are also significant: They give a (slightly opinionated) view of all the major design patterns, complete with small examples and examples from the JDK. These chapters feel like an encyclopedia, which I’ll probably revisit occasionally whenever I need to do a more complex architecture.
Amazon Verified review Amazon
Carl Dea Apr 09, 2023
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I’ve been a software developer for most of my adult life. Early in my career, I was in an interview, being asked about design pattern questions. I thought, how hard could that be? Boy, was I in for a surprise; while I’ve coded common design patterns without realizing it, I didn’t know each design pattern had a name.At the time, a mentor showed me the (Gang of Four) Design Patterns book, which helped me re-think the best practices in software architecture and design. However, the book was not written from a Java developer’s perspective. This is precisely why the Practical Design Patterns for Java Developers book resonated with me.Miroslav Wengner (Java Champion) explains how to code design patterns in the Java language properly. For example, the Singleton pattern may sound basic; however, the author explains the importance of the double-checked locking pattern in Java. As many seasoned developers will encounter these pitfalls, this book will help you avoid them.Chapter 1 begins by providing the reader with a great foundation covering object-oriented programming and design principles.Chapter 2 covers much material on the OpenJDK platform (11-17+), many Java language features, and deep knowledge of Java internals.Chapters 3-6 represent the main categories of design patterns such as Creational, Behavioral, Concurrency, etc.Each chapter begins with a URL to the chapter’s source code. I like how Miroslav uses a common theme of automobiles (cars) when showing the code samples. This helps me remember and conceptualize the design patterns tangibly.Each chapter has three main sections Motivation, Finding it in the JDK, and Sample code. The author carefully explains the motivation to help the reader understand the what, where, when & how to apply design patterns in the real world. Next, the author describes where you can find the design pattern in the Java language, such as existing Java types (classes, interfaces, etc.). Thirdly the author presents a UML diagram with sample code. At the end of each chapter, the reader is presented with questions to assess their knowledge.Chapter 7 is my favorite chapter. I love how Miroslav talks about scenarios related to current technology stacks of today, such as microservices, concurrency programming, etc., unlike other pattern books that are too cut and dry for my taste. In practice, we often learn how to do things correctly but don’t realize we’ve been doing some things incorrectly for so long. This chapter will teach you how to recognize Anti-patterns. In other words, how to avoid bad coding (code smell) practices.The Assessments chapter (like at the end of each chapter) will provide many questions to help challenge the reader. If only I could turn back time and use this book during those intense interviews.Pros:- UML diagrams- Easy examples to follow- Explains Anti-Patterns- Assessments in each chapter to challenge the reader- References to further reading sections- Code is open-sourced on GitHub- Practical examplesCons:- Not for the Java beginnerIf you are an impatient mid to senior-level Java developer, you can just go jump to Chapter 3.- The title font of each design pattern name isn’t prominent at first glance.ConclusionThe book is a breath of fresh air for many Java developers who care about maintaining, developing, and designing software. You can immediately tell Miroslav has a deep knowledge of the Java Platform internals. Many examples are easy to follow and well-explained. This is the kind of book you can read during your travels without needing your computer and compiler. It may not be as thrilling as a mystery novel, but you never know; it could land you your next dream job.
Amazon Verified review Amazon
Brian Benz Mar 29, 2023
Full star icon Full star icon Full star icon Full star icon Full star icon 5
As a fellow Java Champion, I can wholeheartedly recommend Practical Design Patterns for Java Developers. This book offers a fresh perspective on design patterns and provides valuable insights for both beginners and professionals with years of experience.I liked that the book is organized into three main parts: An into to Design Patterns and Java Platform Functionalities, followed by tips for Implementing Standard Design Patterns, then covers additional Essential Patterns and Anti-Patterns.After a great intro, the second part covers several types of design patterns, each illustrated with clear, practical usage examples, and instances found in the JDK. This approach allows developers to quickly grasp the real-world applications and advantages of utilizing design patterns effectively.The third part of the book focuses on concurrency design patterns and common anti-patterns. I especially appreciated the coverage of anti-patterns, particularly relevant in the context of today's multi-core and multi-threaded computing environments. Miroslav's in-depth exploration of these patterns demonstrates how they contribute to more efficient and robust applications.What sets this book apart is the personal touch Miroslav brings to the material. His clear and concise writing style, combined with his expertise and passion for Java an patterns makes the book enjoyable to read. It's evident that he has put a lot of thought into organizing the content and providing useful code samples, further reading suggestions, and assessments to reinforce the concepts covered.Practical Design Patterns for Java Developers is a must-read for experienced Java developers who want to sharpen their software design skills and stay up-to-date with the latest trends in the field. Miroslav's expertise, combined with his unique perspective, makes this book an invaluable resource for creating more efficient, maintainable, and scalable Java applications.
Amazon Verified review Amazon
Maryna Savchenko May 17, 2023
Full star icon Full star icon Full star icon Full star icon Full star icon 5
It is remarkable book with clear explanations of design patterns and many useful examples including references to JDK.One of the standout features is chapter about concurrency design patterns, which helps to understand Java concurrency functionality better.I also appreciated the author's attention to best practices and common anti-patterns.Overall it is easy to read, cohesive guide to design patterns from the latest Java version prospective.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

How do I buy and download an eBook? Chevron down icon Chevron up icon

Where there is an eBook version of a title available, you can buy it from the book details for that title. Add either the standalone eBook or the eBook and print book bundle to your shopping cart. Your eBook will show in your cart as a product on its own. After completing checkout and payment in the normal way, you will receive your receipt on the screen containing a link to a personalised PDF download file. This link will remain active for 30 days. You can download backup copies of the file by logging in to your account at any time.

If you already have Adobe reader installed, then clicking on the link will download and open the PDF file directly. If you don't, then save the PDF file on your machine and download the Reader to view it.

Please Note: Packt eBooks are non-returnable and non-refundable.

Packt eBook and Licensing When you buy an eBook from Packt Publishing, completing your purchase means you accept the terms of our licence agreement. Please read the full text of the agreement. In it we have tried to balance the need for the ebook to be usable for you the reader with our needs to protect the rights of us as Publishers and of our authors. In summary, the agreement says:

  • You may make copies of your eBook for your own use onto any machine
  • You may not pass copies of the eBook on to anyone else
How can I make a purchase on your website? Chevron down icon Chevron up icon

If you want to purchase a video course, eBook or Bundle (Print+eBook) please follow below steps:

  1. Register on our website using your email address and the password.
  2. Search for the title by name or ISBN using the search option.
  3. Select the title you want to purchase.
  4. Choose the format you wish to purchase the title in; if you order the Print Book, you get a free eBook copy of the same title. 
  5. Proceed with the checkout process (payment to be made using Credit Card, Debit Cart, or PayPal)
Where can I access support around an eBook? Chevron down icon Chevron up icon
  • If you experience a problem with using or installing Adobe Reader, the contact Adobe directly.
  • To view the errata for the book, see www.packtpub.com/support and view the pages for the title you have.
  • To view your account details or to download a new copy of the book go to www.packtpub.com/account
  • To contact us directly if a problem is not resolved, use www.packtpub.com/contact-us
What eBook formats do Packt support? Chevron down icon Chevron up icon

Our eBooks are currently available in a variety of formats such as PDF and ePubs. In the future, this may well change with trends and development in technology, but please note that our PDFs are not Adobe eBook Reader format, which has greater restrictions on security.

You will need to use Adobe Reader v9 or later in order to read Packt's PDF eBooks.

What are the benefits of eBooks? Chevron down icon Chevron up icon
  • You can get the information you need immediately
  • You can easily take them with you on a laptop
  • You can download them an unlimited number of times
  • You can print them out
  • They are copy-paste enabled
  • They are searchable
  • There is no password protection
  • They are lower price than print
  • They save resources and space
What is an eBook? Chevron down icon Chevron up icon

Packt eBooks are a complete electronic version of the print edition, available in PDF and ePub formats. Every piece of content down to the page numbering is the same. Because we save the costs of printing and shipping the book to you, we are able to offer eBooks at a lower cost than print editions.

When you have purchased an eBook, simply login to your account and click on the link in Your Download Area. We recommend you saving the file to your hard drive before opening it.

For optimal viewing of our eBooks, we recommend you download and install the free Adobe Reader version 9.