Senior Java Software Engineer Interview Notes – Part 3

Introduction

These are the interview questions that were asked for the Software Engineer role in Bally’s Interactive. This post mentions Java, Design Patterns, REST, and Testing.

Java

What is immutability?

An object is considered immutable if its state cannot change after it is constructed. They are helpful when writing secure code as you don’t need to worry about the values changing. Since we cannot change the state, they are thread-safe, meaning that immutable objects can be easily shared between multiple threads.

How to create a class that is immutable?

The following rules define a simple strategy for creating immutable objects.

  1. Use a constructor to set all properties of the object
  2. Mark all of the instance variables private and final
  3. Don’t define any setter methods
  4. If the instance fields include references to mutable objects, don’t allow those objects to be changed
  5. Prevent methods from being overridden

Imagine that one of the member variables of an immutable class is a list. How would you make this list immutable so that you cannot change any item on the list?

There are two approaches: (1) Using wrapper methods to read the data and (2) Making a copy of the mutable object any time it is required. Consider the immutable class below.

public final class WorkoutMutable {
    private final List<String> exercises;

    public WorkoutMutable(String type, List<String> exercises) {
        this.type = type;
        this.exercises = new ArrayList<String>();
    }     

    //rule 4 failed
    public List<String> getExercises() {
        return exercises;
    }
}

Approach 1: Using wrapper methods to read the data

In this version, the data is still available. However, it is an immutable object as the mutable variable cannot be modified by the caller.

public final class WorkoutMutable {
    private final List<String> exercises;

    public WorkoutMutable(String type, List<String> exercises) {
        this.type = type;
        this.exercises = new ArrayList<String>();
    }     
    
    public int getExercisesCount() {
        return exercises.size();
    }

    public String getExercisesItem(int index) {
        return exercises.get(index);
    }
}

Approach 2: Making a copy of the mutable object any time it is required

In this approach, changes in copy will not be reflected in the original excercises object, but at least the original cannot be modified by the caller. However, this is an expensive operation if called frequently by the caller.

public final class WorkoutMutable {
    private final List<String> exercises;

    public WorkoutMutable(String type, List<String> exercises) {
        this.type = type;
        this.exercises = new ArrayList<String>();
    }     
    
    public int getExercisesCount() {
        return exercises.size();
    }

    public ArrayList<String> getExercises() {
        return new ArrayList<String>(this.exercises);
    }
}

Design Patterns

What is Dependency Injection?

In software engineeringdependency injection is a design pattern in which an object receives other objects that it depends on. A form of inversion of control, dependency injection aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs.[1][2][3] 

The pattern ensures that an object which wants to use a given service should not have to know how to construct those services. Instead, the receiving ‘client‘ (object) is provided with its dependencies by external code (an ‘injector’), which it is not aware of [wiki].

What is the best practice for dependency injection? For instance, if you need to inject a dependency, do you inject an interface or the concrete class?

There are three main ways in which a client can receive injected services:

  • Constructor injection, where dependencies are provided through a client’s class constructor.
  • Setter injection, where the client exposes a setter method which accepts the dependency.
  • Interface injection, where the dependency’s interface provides an injector method that will inject the dependency into any client passed to it.

What is a Singleton?

A singleton is simply a class that is instantiated exactly once. We want a single instance of a particular object in the memory. Read more here: https://suleymanyildirim.org/java/singleton-design-pattern-for-interviews.

When Spring framework creates a dependency, is it a Singleton or not?

It is a Singleton, only one instance of the spring bean will be created for the spring container. This is the default spring bean scope. While using this scope, make sure bean doesn’t have shared instance variables otherwise it might lead to data inconsistency issues.

What is the value of using design patterns?

Design patterns are typical solutions to common problems in software design. Here is a nice explanation in Refactoring Guru.

  • Design patterns are a toolkit of tried and tested solutions to common problems in software design. Even if you never encounter these problems, knowing patterns is still useful because it teaches you how to solve all sorts of problems using principles of object-oriented design.
  • Design patterns define a common language that you and your teammates can use to communicate more efficiently. You can say, “Oh, just use a Singleton for that,” and everyone will understand the idea behind your suggestion. No need to explain what a singleton is if you know the pattern and its name.

REST

What is REST?

REST is an architectural style for providing standards between computer systems on the web, making it easier for systems to communicate with each other. RESTful systems are characterized by how they are stateless and separate the concerns of the client and server [what-is-rest]

https://www.codecademy.com/article/what-is-rest

Separation of Client and Server

In the REST architectural style, the implementation of the client and the implementation of the server can be done independently without each knowing about the other. This means that the code on the client side can be changed at any time without affecting the operation of the server, and the code on the server side can be changed without affecting the operation of the client.

Statelessness

Systems that follow the REST paradigm are stateless, meaning that the server does not need to know anything about what state the client is in and vice versa. In this way, both the server and the client can understand any message received, even without seeing previous messages. This constraint of statelessness is enforced through the use of resources, rather than commands. Resources are the nouns of the Web.

What is idempotency?

An HTTP method is idempotent if an identical request can be made once or several times in a row with the same effect while leaving the server in the same state. In other words, calling the same PUT request multiple times will always produce the same result [1]. In contrast, calling a POST request repeatedly have the side effects of creating the same resource multiple times[2].

GET /pageX HTTP/1.1 is idempotent. Called several times in a row, the client gets the same results:

GET /pageX HTTP/1.1
GET /pageX HTTP/1.1
GET /pageX HTTP/1.1
GET /pageX HTTP/1.1

POST /add_row HTTP/1.1 is not idempotent; if it is called several times, it adds several rows:

POST /add_row HTTP/1.1
POST /add_row HTTP/1.1   -> Adds a 2nd row
POST /add_row HTTP/1.1   -> Adds a 3rd row

Which HTTP Verbs are idempotent?

Implemented correctly, the GETHEADPUT, and DELETE methods are idempotent, but not the POST method.

What is the difference between @PathParam and @RequestParam?

@RequestParam extracts values from the query string, while @PathVariable extracts values from the URI path. Read more here: https://www.baeldung.com/spring-requestparam-vs-pathvariable

TESTING

What are the TDD steps?

Red, Green, and Refactor are the three-phase of Test Driven Development. When followed, this order of steps helps ensure that you have tests for the code you are writing and you are writing only the code that you have to test for.

  • The red phase indicates that the code does not work.
  • The green phase indicates that everything is working, but not necessarily in the most optimal way.
  • The blue phase indicates that the tester is refactoring the code, but is confident their code is covered with tests that give the tester confidence to change and improve our code.

What is the value of Refactor step in TDD?

Before the refactoring phase, you are not concerned with non-functional requirements, such as code maintainability, quality, and readability. The focus of this phase is to look at these because you have covered functional (business) requirements in the form of unit tests.

Which layer of the Test Pyramid should have more tests?

According to Matrin Fowler, the number of unit tests in your test suite will largely outnumber any other type of test. The foundation of your test suite will be made up of unit tests. Your unit tests make sure that a certain unit (your subject under test) of your codebase works as intended. Unit tests have the narrowest scope of all the tests in your test suite.

Test Pyramid: https://martinfowler.com/articles/practical-test-pyramid.html

There is also another good explanation in AWS Whitepaper. The following testing pyramid is a concept provided by Mike Cohn in Succeeding with Agile. It shows the various software tests in relation to their cost and speed at which they run.

Unit tests are at the bottom of the pyramid. They are both the fastest to run and the least expensive. Therefore, unit tests should make up the bulk of your testing strategy. A good rule of thumb is about 70 percent. Unit tests should have near-complete code coverage because bugs caught in this phase can be fixed quickly and cheaply.

Service, component, and integration tests are above unit tests on the pyramid. These tests require detailed environments and therefore, are more costly in infrastructure requirements and slower to run. Performance and compliance tests are the next level. They require production-quality environments and are more expensive yet. UI and user acceptance tests are at the top of the pyramid and require production-quality environments as well.

AWS Whitepaper: Testing stages in continuous integration and continuous delivery

What is a “unit” in testing?

Nice definition on Martin Fowler’s website:

If you ask three different people what “unit” means in the context of unit tests, you’ll probably receive four different, slightly nuanced answers. If you’re working in a functional language a unit will most likely be a single function. Your unit tests will call a function with different parameters and ensure that it returns the expected values. In an object-oriented language, a unit can range from a single method to an entire class.

What’s a Unit?

Leave a Reply

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