• Latest
Protect Your Invariants! – DZone Java

Protect Your Invariants! – DZone Java

January 7, 2022
PlayStation State Of Play Predictions – What’s Sony’s Plan For Summer ’22? | GI Show

PlayStation State Of Play Predictions: What’s Sony’s 2022 Plan? | GI Show

May 27, 2022
What Is Smoke Testing? – A Brief Guide

What Is Smoke Testing? – A Brief Guide

May 27, 2022
Kao the Kangaroo Review (Switch)

Kao the Kangaroo Review (Switch)

May 27, 2022
Mario Strikers: Battle League Preview – Just For Kicks

Mario Strikers: Battle League Preview – Just For Kicks

May 27, 2022
These are the latest Apple Arcade games for iPhone and more

These are the latest Apple Arcade games for iPhone and more

May 27, 2022
Mario Strikers: Battle League Makes A Furiously Good First Impression

Mario Strikers: Battle League Makes A Furiously Good First Impression

May 27, 2022
Steam Deck Update Reduces Fan Noise, Adds Refresh Rate Control, Remote Play Together Support, and More – TouchArcade

Steam Deck Update Reduces Fan Noise, Adds Refresh Rate Control, Remote Play Together Support, and More – TouchArcade

May 27, 2022
Microsoft is giving away free Xbox gift cards again

Microsoft is giving away free Xbox gift cards again

May 27, 2022
Learn how to access all your photos with the new Mylio Photos

Learn how to access all your photos with the new Mylio Photos

May 27, 2022
Microsoft union response in stark contrast to Apple; offers ‘full support’

Microsoft union response in stark contrast to Apple; offers ‘full support’

May 27, 2022
The Panda Cup, An Officially-Licensed Smash Bros. Circuit, Kicks Off In June

The Panda Cup, An Officially-Licensed Smash Bros. Circuit, Kicks Off In June

May 27, 2022
Ring out! SoulCalibur 2 HD falls off Xbox store

Ring out! SoulCalibur 2 HD falls off Xbox store

May 27, 2022
Advertise with us
Friday, May 27, 2022
Bookmarks
  • Login
  • Register
GetUpdated
  • Home
  • Game Updates
    • Mobile Gaming
    • Playstation News
    • Xbox News
    • Switch News
    • MMORPG
    • Game News
    • IGN
    • Retro Gaming
  • Tech News
    • Apple Updates
    • Jailbreak News
    • Mobile News
  • Software Development
  • Photography
  • Contact
    • Advertise With Us
    • About
No Result
View All Result
GetUpdated
No Result
View All Result
GetUpdated
No Result
View All Result
ADVERTISEMENT

Protect Your Invariants! – DZone Java

January 7, 2022
in Software Development
Reading Time:16 mins read
0 0
0
Share on FacebookShare on WhatsAppShare on Twitter


How do you handle constraints and validation inside your application? Most developers put this logic somewhere close to their application’s boundary. Actually, this prevents them from having a rich domain model that could ensure consistency.

Developers tend to get confused when they need to find a good place for their business logic. I suspect that most probably the reasons are related to all those bad examples circulating in the documentations of popular frameworks and bad habits from coding in old-fashioned enterprise platforms like J2EE. Most of the time, they’re afraid to keep these vital pieces of information in the language elements most relevant to the target domain: I’m talking about the simple classes reflecting the “nouns” of the business logic. Often, you see something like the example below:

@Entity 
public class ShoppingCart {

    @OneToMany
    private List<LineItems> lineItems;
    // getters and setters

}

@Component
public class ShoppingCartService {

    public void addToCart(ShoppingCart cart, LineItem item) {
        // Logic related to adding a line item to the shopping cart:
        // Preconditions to check, like availability of the item.
        // Operations, like really adding the item to the cart.
        // Postconditions and cleanup, like changing item's availabiltiy and transaction management.
    }

}

There’s an entity, which is just a placeholder for the database state. It’s also reflecting the object-database mapping with some sort of a meta-language, done by various annotations. There is nothing else here, just getters and setters. The real operations located in another class suffixed as a “Service” “Processor” or a “Manager” (these name suffixes are some kind of an anti-pattern by themselves according to the book “Clean Code”). But, from the framework’s point of view, the class lifecycle is often controlled by the framework itself. So, it’s preferable not to hold state in the instances of the class: Either because the framework is keeping a single instance per class in a managed bean (simply as a singleton) or because multiple threads can access the instance’s state at the same time, so thread safety is not guaranteed.

Note that I have not talked about anything related to the target domain. Nothing was mentioned about the cart: the capacity of it, the possibility of putting duplicated items in it, etc. But many things were explained about framework concepts, like thread management, ORM, bean lifecycles, and so on. So, it’s important to mention that I suspect the framework documentation is intentionally keeping their examples like the one above. They need to minimize the amount of code representing the “hard facts” of the domain in their examples because it’s irrelevant!

So, what’s wrong with all of this? Simply domain logic is not in focus, and it’s often scattered around in various places. Stateless operations are managing your domain by some sort of a “transaction script” leading us to an “anemic domain model.” There’s no place for the real power of Object-Oriented Programming in the form of polymorphism, nor for powerful design patterns, like composites, visitors, strategies, observers, and so on.

Sadly, this is especially true if we want to implement an invariant of the domain. These pieces of code are essential for keeping things in place: Essential for keeping our defect density low by catching programming errors early on. The invariants of our domain should be part of a domain layer, unit-tested, and easy to understand. Missing them from your domain will cause more complicated test cases as you try to tailor together all your business logic from different layers or modules of your application. These tests will either run slower or have a complex mocking mechanism and a lot of plumbing code to make the application run without all the clutter. This is not ideal if you aim to understand functional behavior by reading the test cases.

Should we put these invariants into our application boundaries in some kind of validation logic? Maybe we could use another framework or library for our aid? I say we should put these implementations very close to our domain instead. Capture them with simple language elements verifiable with a simple unit test.

Illustration.

Invariants VS. validation: Often, validation is placed on layer boundaries, while invariant located at the heart of your software

Defensive Programming; Design By Contract

To be honest, when I read about “Design by Contract” in the book “The Pragmatic Programmer,” I became a great fan of the subject. Unfortunately, I didn’t find good support for it in Java, so I just stopped experimenting with it. Later, I dropped the burden of finding a suitable library for all the work and started implementing invariants and preconditions with simple language elements. Sometimes I formulated these as assertions, and, sometimes, I just used some simplistic functions, but it always kept this kind of code close to the target domain. 

Later, I got familiar with Domain-Driven Design by reading Eric Evans’ book. Inside he talked about “Making Implicit Concepts Explicit.” That was the point when I realized that invariants related to the domain should be kept with the target domain and should be treated separately from possible programmer errors or misformatted input. For instance, a date format is something from the latter while handling what happens if duplicated line items are in a shopping cart, are the former.

Two Examples: Bowling Game and Knight in a Chessboard

Okay, now let’s look at two practical examples. In the first one, we will implement score evaluation software for a bowling game. The scoring rules can be explained in a few sentences, so I will copy them over to here:

Bowling Game Rules

The Game

The game consists of 10 frames. In each frame, the player has two rolls to knock down 10 pins.

The score for the frame is the total number of pins knocked down, plus bonuses for strikes and spares.

Spares

A spare is when the player knocks down all 10 pins in two rolls.

The bonus for that frame is the number of pins knocked down by the next roll.

Strikes

A strike is when the player knocks down all 10 pins on his first roll.

The frame is then completed with a single roll. The bonus for that frame is the value of the next two rolls.

Tenth frame

In the tenth frame, a player who rolls a spare or strike is allowed to roll the extra balls to complete the frame.

However, no more than three balls can be rolled in the tenth frame.

For those with poor imaginations, here’s a bowling scorecard. Spares are shown with black triangles and strikes with rectangles.

Spares.

The rules seem simplistic, so we can start with an int array counting the number of scores. We can factor in the rest of the mentioned rules later (at least we think we can do it relatively easily). 

public class Game {

    private int[] rolls = new int[21];
    private int turn = 0;

    public void roll(int pins) {
        rolls[turn++] = pins;
    }

    public int score() {
        return Arrays.stream(rolls).sum();
    }

}

Now, let’s collect the preconditions and invariants for the roll() method. 

  • We can’t roll more than 10 pins at once (physically impossible as there are only 10 pins at maximum on the field).
  • The sum of pins we hit can be only 10 in each frame.

There’s an implicit concept not mentioned in the code above, and it makes the implementation extremely hard to handle, and that’s the frame. Now let’s see preconditions and invariants related to frames in our business logic:

  • On the tenth frame, we can have either 2 or 3 rolls depending on the current score in that frame.
  • In the first nine frames, we can have 2 rolls at maximum.
  • All of the above mentioned for the roll() method is also true since we capture our current score in each frame in our scorecard.

The game should just ensure one thing:

  • A game consists of 10 frames. (this is captured implicitly with the magic number 21).

How should we represent frame in our code? We simply should make it explicit! This has the following positive effect on our implementation:

  • Eliminates the hidden “Single Responsibility Principle” violation starting to appear, as we try to do everything in a single Game class.
  • Immediately eliminates the magic number 21 in our code.
  • Helps readability by explicitly phrasing another “noun” of our domain, called a frame.
  • But most importantly it’s decomposing the problem into smaller subproblems, each one easier to solve!

Here’s a diagram that shows how our class hierarchy should look:

Hierarchy illustration.

So, how do we implement the Game class that encapsulates all its invariants? 

public class Game {

    private Frame[] frames = new Frame[10];
    private int turn = 0;

    public Game() {
        // ...
    }

    public void roll(int pins) throws NoMoreRollsException, IllegalRollException {
        frames[turn].roll(pins);
        if (frames[turn].noMoreRolls()) {
            turn++;
        }
    }

    public int score() {
        return Arrays.stream(frames).mapToInt(Frame::score).sum();
    }

}

The third line above is a pure representation of our first requirement: “The game consists of 10 frames.” The Game class does not need to know much more about anything else. The roll() and score() methods are just delegating functionalities to the appropriate Frame subclass.

Let’s see how the roll() method in the Frame implementation deals with preconditions and invariants:

public class IntermediateFrame extends BaseFrame {

    private static final int FIRST_TRY = 0;
    private static final int MAXIMUM_TRIES = 2;
    // ...
    @Override
    public void roll(int pins) throws NoMoreRollsException, IllegalRollException {
        verifyNumberOfTriesIsLessThanMaximum(tries, MAXIMUM_TRIES);
        verifyNumberOfPinsIsLessThanMaximum(getFirstRoll() + pins);
        if (tries == FIRST_TRY) {
            setFirstRoll(pins);
        } else {
            setSecondRoll(pins);
        }
        tries++;
    } 
    // ...
}

Just to recap, we have to ensure that:

  • We can’t roll more than 10 pins at once (physically impossible as there are only 10 pins at maximum on the field).
  • The sum of pins we hit can be only 10 in each frame.
  • In the first nine frames, we can have 2 rolls at maximum.

All of the above is encapsulated in just two lines.

Knight on a Chessboard

I once saw the following difficult interview assignment. If you get an exercise like that and you are inexperienced, you will probably block because of the overwhelming complexity of the problem (then you probably start right in the middle and end up troubleshooting all your bugs in an overcomplicated multi-nested loop in the last 20 minutes of the interview).

Object-Oriented thinking should be on our aid! The only thing that we have to do is to split the problem into feasible subproblems and protect our invariants (you can imagine it as some sort of a synonym to encapsulation). It ensures good OOP design from the bottom up and saves a lot of time.

Working With What We Know

Let’s brainstorm together a couple of invariants:

  • A chess piece should remain on the board after each step.
  • A knight is allowed to move two squares vertically and one square horizontally, or two squares horizontally and one square vertically (from Wikipedia).

These are still too high-level and not simplistic enough to work with. How should we express the first one with multiple invariants?

  • A position in the chessboard is formed of ranks and files.
  • A chess piece on the board occupies one single position.
public final class Position {

    private final Rank rank;
    private final File file;

    public Position(Rank rank, File file) {
        this.rank = rank;
        this.file = file;
    }
    // ...
}

public final class Knight {

    private final Position currentPosition;
    
    public Knight(Position position) {
        this.position = position;
    }

}

Now, for the sake of simplicity, let’s assume that we’re playing with a standard 8×8 chessboard. Each rank and file can be represented as an enum in this case: 

public enum Rank {
    A, B, C, D, E, F, G, H;
    // ...
}

public enum File {
    ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT;
    // ...
}

To guarantee the first invariant, all that we have to do is to implement a method, which will move our piece to a new location:

class Position {
    // ...
    public Position advance(int rankChange, int fileChange) throws IllegalMoveException {
        return new Position(getRank().advance(rankChange),
                getFile().advance(fileChange));
    }
    // ...
}

class Rank {
    // ..
    public Rank advance(int steps) throws IllegalMoveException {
        try {
            return Rank.values()[this.ordinal() + steps];
        } catch (ArrayIndexOutOfBoundsException ex) {
            throw new IllegalMoveException();
        }
    }
}

We’re changing the existing interface of the Java enum to align it with our domain. We have introduced the advance and the IllegalMoveException terms. 

Why Aren’t We Using Coordinates?

As a side note let’s discuss less powerful options. At least less powerful from the scope of forcing our invariants.

What if we use just two int inside our Knight class?

public class Knight {

    private final int file;
    private final int rank;

    public Knight(int file, int rank) {
        this.file = file;
        this.rank = rank;
    }
    // ...
}

In this case, we need to check if the file and rank fields are between 1 and 8 (or 0 – 7) after moving our piece. This logic can’t be inside the Knight class because it will break the “Single Responsibility Principle”: What if we need to extend our codebase with additional pieces? The invariant has to be enforced in each and every step for each and every piece. We can’t just copy over the validation code to other pieces, and neither can we extend int functionalities. So, there has to be a new class encapsulating this logic that’s associated with all the chess pieces somehow. Let’s put the coordinates into the Position class:

public class Position {
    private final int file;
    private final int rank;

    public Position(int file, int rank) {
        this.file = file;
        this.rank = rank;
    }
    // ...
}

This style is a bit better. We need to guarantee that each Position object is well constructed, meaning both file and rank values are in range. We could do something like the example below, but this would mean that every constructor call will possibly throw an exception:

public class Position {
    // ...
    public Position(int rank, int file) throws IllegalMoveException {
        assertWithingRange(rank);
        assertWithingRange(file);
        this.rank = rank;
        this.file = file;
    }
    // ...
}

Enums offer a possible range of values definable with a declarative style. It relieves us of the burden of exception handling in every object creation.

Coding the Algorithm

A Knight should tell the set of positions it can visit. We need to implement this method relying on the existing mechanism. Something like the snippet below should work:

public class Knight {
    // ...
    Set<Position> getPossibleMoves() {
        var result = new HashSet<Position>();
        addPositionIfPossible(result, 1, 2);
        addPositionIfPossible(result, 1, -2);
        addPositionIfPossible(result, 2, 1);
        addPositionIfPossible(result, 2, -1);
        addPositionIfPossible(result, -1, 2);
        addPositionIfPossible(result, -1, -2);
        addPositionIfPossible(result, -2, 1);
        addPositionIfPossible(result, -2, -1);
        return result;
    }

    private void addPositionIfPossible(HashSet<Position> result, int rankChange, int fileChange) {
        try {
            result.add(position.advance(rankChange, fileChange));
        } catch (IllegalMoveException e) {
        }
    }
    // ...
}

The method addPositionIfPossible is not quite nice, because it swallows an exception and modifies the passed parameter’s state. If you prefer to eliminate these issues in method implementations, you can refactor the snippet above after changing Position.advance()  to return an Optional<Position>.

public class Knight {
    // ...
    Set<Position> getPossibleMoves() {
        var result = new HashSet<Position>();
        position.advance(1, 2).ifPresent(result::add);
        position.advance(1, -2).ifPresent(result::add);
        position.advance(2, 1).ifPresent(result::add);
        position.advance(2, -1).ifPresent(result::add);
        position.advance(-1, 2).ifPresent(result::add);
        position.advance(-1, -2).ifPresent(result::add);
        position.advance(-2, 1).ifPresent(result::add);
        position.advance(-2, -1).ifPresent(result::add);
        return result;
    }
    // ...
}

OK, now let’s see the implementation of the algorithm after we’ve solved all the subproblems above. We need some Java collections to track our current progress of the traversal. Let’s try to nail them and implement them by doing the following:

  • We need to track the positions we’ve already traversed with our Knight (can be a set).
  • All possible moves should be enqueued to investigate them one by one (ideal candidate for a queue).
  • We track the steps required in a separate collection, which maps each position on the chessboard with an integer (a map, of course).
public class KnightSolver {
    private final Knight knight;
    private final Set<Position> seen = new HashSet<>();
    private final Queue<Position> candidates = new LinkedList<>();
    private final Map<Position, Integer> stepsNeeded = new HashMap<>();
    // ...
}

In each iteration, we’re visiting the first element of the candidates queue. Let’s list the steps we need to do:

  1. Get the possible moves from that position.
  2. Remove all the possible moves which we’ve already seen.
  3. Update our stepsNeeded collection with the newly discovered possible moves.
  4. Update the set of already seen positions.
  5. Update the traversable candidates with the newly discovered possible moves (for the subsequent iterations).
  6. Of course, return the result if we reached the end position.

And, this is how it should look:

public class KnightSolver {
    //...
    public int maxStepsFor(Position endPosition) {
        initialize();
        while (!candidates.isEmpty()) {
            var currentKnight = new Knight(candidates.poll());
            var positions = currentKnight.getPossibleMoves();
            positions.removeAll(seen);
            for (var position : positions) {
                stepsNeeded.put(position, stepsNeeded.get(currentKnight.getPosition()) + 1);
                if (endPosition.equals(position)) {
                    return stepsNeeded.get(endPosition);
                }
                seen.addAll(positions);
                candidates.add(position);
            }
        }
        throw new RuntimeException("Should not be possible");
    }
    //...
}

You can view the complete code example over here.

Conclusion

Domain-Driven Design has a great number of elements in its vocabulary to implement the concepts above. These include specification, validation, assertions, and constraints, to name a few. I encourage you to look at “Chapter 10: Supple Design” and “How to Model Less Obvious Kinds of Concepts” from the original book Domain-Driven Design if you’re interested in finding out more. It also implies that these concepts should be part of your domain layer and not sitting somewhere else, or even worse, scattered all over your code.

Flaws and Limitations of This Technique

Java is doing poorly in providing language support for designing by contract. Clojure, for instance, offers pre-conditions and post-conditions for their functions. In Java, we can somewhat replace the precondition of a method by simply implementing guard clauses or assertions inside the method body. The problem with this approach comes when we need to implement inheritance and begin to override methods. A few of the possible options to overcome this issue are the following:

  • Simplify the reusability of your preconditions: Extract them into their separated utility methods and make them final. Making them static will help these utility methods not to rely on the current object’s state.
  • * Implement a Template method design pattern. I encourage you to make all those methods public final that is not meant to be overridden by the subclasses.

Other Options

There are some cases where it might seem better not to aim for a robust design. Microservices with simple logic or with a shallow domain can be one area I can imagine. Would it sound good to have multiple layers and modularity in this case? It might look beneficial to simplify everything to the ground for the sake of reaching production early. But this is a slippery slope. Note that good design is really hard to factor in later, similarly to security or automated tests.

If you’re interested, you can watch me on YouTube doing the bowling game implementation step-by-step using TDD. 



Source link

ShareSendTweet
Previous Post

Inspecting Joins in PostgreSQL – DZone Database

Next Post

Nobody Saves the World Xbox achievement list has arrived

Related Posts

What Is Smoke Testing? – A Brief Guide

May 27, 2022
0
0
What Is Smoke Testing? – A Brief Guide
Software Development

Smoke testing is a method of determining whether a newly released build is stable. It assists a QA or testing...

Read more

The Top Security Strategies in Custom Software Development

May 27, 2022
0
0
The Top Security Strategies in Custom Software Development
Software Development

The global spending on enterprise software is $605 billion.  An increasing number of companies explore custom software development to digitize...

Read more
Next Post
Nobody Saves the World Xbox achievement list has arrived

Nobody Saves the World Xbox achievement list has arrived

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

© 2021 GetUpdated – MW.

  • About
  • Advertise
  • Privacy & Policy
  • Terms & Conditions
  • Contact

No Result
View All Result
  • Home
  • Game Updates
    • Mobile Gaming
    • Playstation News
    • Xbox News
    • Switch News
    • MMORPG
    • Game News
    • IGN
    • Retro Gaming
  • Tech News
    • Apple Updates
    • Jailbreak News
    • Mobile News
  • Software Development
  • Photography
  • Contact
    • Advertise With Us
    • About

Welcome Back!

Login to your account below

Forgotten Password? Sign Up

Create New Account!

Fill the forms bellow to register

All fields are required. Log In

Retrieve your password

Please enter your username or email address to reset your password.

Log In
Are you sure want to unlock this post?
Unlock left : 0
Are you sure want to cancel subscription?