> For the complete documentation index, see [llms.txt](https://gpcoder.gitbook.io/clean-code/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gpcoder.gitbook.io/clean-code/the-key-principles-of-clean-code/functions.md).

# Functions

## Small

The first rule of functions is that they should be small. The second rule of functions is that *they should be smaller than that*.

{% hint style="danger" %}

<pre class="language-java"><code class="lang-java"><strong>public static List&#x3C;UserReportDto> getUserReportDtos() {
</strong>        List&#x3C;User> users = CsvUtils.read(new File("user.csv"), User.class);
        List&#x3C;UserReportDto> userReportDtos = new ArrayList&#x3C;>();
        if (users != null &#x26;&#x26; !users.isEmpty()) {
            for (User user : users) {
                UserReportDto reportDto = UserReportDto.builder()
                        .withName(user.getName())
                        .withEmail(user.getEmail())
                        .build;
                userReportDtos.add(reportDto);
            }
        }
        return userReportDtos;
}
</code></pre>

{% endhint %}

{% hint style="success" %}

```java
// First level of abstraction
public static List<UserReportDto> getUserReportDtos() {
    List<User> users = readUsersFromCsvFile("user.csv");
    return mapToUserReportDtos(users);
}

// Second level of abstraction
private List<User> readUsersFromCsvFile(String csvFileName) {
    return CsvUtils.read(new File(csvFileName), User.class);
}

// Second level of abstraction
private List<UserReportDto> mapToUserReportDtos(List<User> users) {
    List<UserReportDto> userReportDtos = new ArrayList<>();
    if (!isValid(users)) {
        return userReportDtos;
    }
    for (User user : users) {
        UserReportDto reportDto = mapToUserReportDto(user);
        userReportDtos.add(reportDto);
    }
    return userReportDtos;
}

// Third level of abstraction
private boolean isValid(List<User> users) {
    return users != null && !users.isEmpty();
}

// Third level of abstraction
private UserReportDto mapToUserReportDto(User user) {
    return UserReportDto.builder()
                .withName(user.getName())
                .withEmail(user.getEmail())
                .build();
}
```

{% endhint %}

Refer to [Write Short Units of Code](/clean-code/building-maintainable-software/write-short-units-of-code.md)

## Do One Thing

One way to know that a function is doing more than “one thing” is if you can extract another function from it with a name that is not merely a restatement of its implementation.

{% hint style="info" %}
See examples in the "[Small](#small)" section.
{% endhint %}

## One Level of Abstraction per Function

In order to make sure our functions are doing “one thing,” we need to make sure that the statements within our function are all at the same level of abstraction.

Abstraction is a fundamental concept in OOPS. It talks about hiding the “how” part and only exposing “what” to the outer world.

According to SLAP (Single Level of Abstraction Principle), source code inside each method should refer to concepts and mechanisms relevant to just one level of “operational complexity” of your application.

Apply refactoring technique "Extract Method".

{% hint style="info" %}
See examples in the "[Small](#small)" section.
{% endhint %}

## Reading Code from Top to Bottom: The Stepdown Rule

We want every function to be followed by those at the next level of abstraction so that we can read code from top to bottom.

{% hint style="info" %}
See examples in the "[Small](#small)" section.
{% endhint %}

## Avoid nested structures

{% hint style="danger" %}

```java
public boolean isUserValid(UserDto userDto) {
    if (userDto != null) {
        if (userDto.getPassword() != null) {
            if (userDto.getEmail() != null) {
                return !userService.isEmailExists(userDto.getEmail());
            }
        }
    }
    return false;
}
```

{% endhint %}

{% hint style="success" %}

```java
public boolean isUserValid(UserDto userDto) {
    if (userDto == null) {
        return false;
    }
    if (userDto.getPassword() == null) {
        return false;
    }
    if (userDto.getEmail() == null) {
        return false;
    }
    return !userService.isEmailExists(userDto.getEmail());
}
```

{% endhint %}

## Avoid negative conditionals

Negatives are just a bit harder to understand than positives. So, when possible, conditionals should be expressed as positives.

{% hint style="danger" %}

```java
if(!isNotEmpty(users)) {}
```

{% endhint %}

{% hint style="success" %}

```
if(isEmpty(users)) {}
```

{% endhint %}

## Switch Statements <a href="#ch3lev1sec4" id="ch3lev1sec4"></a>

It’s hard to make a small `switch` statement. By their nature, `switch` statements always do *N* things. Unfortunately, we can’t always avoid `switch` statements, but we *can* make sure that each `switch` statement is buried in a low-level class and is never repeated. We do this, of course, with **polymorphism**.

There are several problems with the switch statement.&#x20;

* First, it’s large, and when new enum types are added, it will grow.&#x20;
* Second, it very clearly does more than one thing.&#x20;
* Third, it violates the Single Responsibility Principle (SRP) because there is more than one reason for it to change.&#x20;
* Fourth, it violates the Open Closed Principle (OCP) because it must change whenever new types are added.

{% hint style="danger" %}

```java
enum VehicleType {
    BICYCLE,
    CAR,
    AIRPLANE
}

class Vehicle {
    String name;
    VehicleType type;
    void ride() {}
    void fly() {}
}

void perform(Vehicle vehicle) {
    switch (vehicleType) {
        case BICYCLE:
            vehicle.ride();
            break;
        case CAR:
            vehicle.drive();
            break;
        case AIRPLANE:
            vehicle.fly();
            break;
        default:
            break;
    }
}
```

{% endhint %}

{% hint style="success" %}

```java
interface VehicleAction {
    void perform();
}

abstract class Vehicle implements VehicleAction {
    String name;
}

class Bicycle extends Vehicle {

    void ride() {}

    @Override
    public void perform() {
        ride();
    }
}

class Car extends Vehicle {
    void drive() {}

    @Override
    public void perform()
        drive();
    }
}

// App
void perform(VehicleAction vehicleAction) {
    vehicleAction.perform();
}
```

{% endhint %}

## **Use Descriptive Names** <a href="#ch3lev1sec5" id="ch3lev1sec5"></a>

Don’t be afraid to make a name long. A long descriptive name is better than a short enigmatic name. A long descriptive name is better than a long descriptive comment.

{% hint style="danger" %}

```
public void createPrd(); // Create a product
```

{% endhint %}

{% hint style="success" %}

```
public void createProduct();
```

{% endhint %}

Be consistent in your names. Use the same phrases, nouns, and verbs in the function names you choose for your modules.

{% hint style="success" %}
beforeEach();

afterEach();

beforeAll();

afterAll();
{% endhint %}

## Prefer fewer arguments

The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification—and then shouldn’t be used anyway.

{% hint style="danger" %}
void createUser(String username, String password, Gender gender, int roleId);
{% endhint %}

Apply refactoring technique "**Replace Method Parameters with Method Object**": When a function seems to need more than two or three arguments, it is likely that some of those arguments ought to be wrapped into a class of their own.

{% hint style="success" %}

```java
void createUser(UserDto userDto) {
}

class UserDto {
    private String username;
    private String password;
    private Gender gender;
    private int roleId;
}
```

{% endhint %}

{% hint style="info" %}
Functions that take variable arguments can be monads, dyads, or even triads. But it would be a mistake to give them more arguments than that.

Arguments are even harder from a testing point of view. Imagine the difficulty of writing all the test cases to ensure that all the various combinations of arguments work properly.
{% endhint %}

Sometimes we want to pass a variable number of arguments into a function. Consider to use Argument Lists

{% hint style="success" %}
public String format(String format, Object… args);

String.format(”%s worked %.2f hours.”, name, hours);
{% endhint %}

Read more:  [Keep Unit Interfaces Small](/clean-code/building-maintainable-software/keep-unit-interfaces-small.md)

## Avoid flag arguments

Function with flag argument does more than one thing. It does one thing if the flag is true and another if the flag is false!

{% hint style="danger" %}

```java
public execute(boolean isTestSuite) {
    if (isSuite) {
        // do something for suite
    } else {
        // do something for single test
    }
}
```

{% endhint %}

{% hint style="success" %}

```java
public void executeForTestSuite() {
    // do something for suite
}

public void executeForSingleTest() {
    // do something for single test
}
```

{% endhint %}

{% hint style="info" %}
Split the method into several independent methods that can be called from the client without the flag.
{% endhint %}

## Have No Side Effects

Side effects are lies. Your function promises to do one thing, but it also does other hidden things.

The side effect is the call to update(). The get user function, by its name, says that it gets the user by email. The name does not imply that it updates the last access of the user.

{% hint style="danger" %}

```java
public User getUserByEmail(String email) {
        User user = userRepository.getUserByEmail(email);
        user.setLastAccess(now());
        userRepository.update(user);
        return user;
    }
```

{% endhint %}

## Output Arguments

Anything that gets modified or mutated but is not returned by the function. An array/object passed to a function, which changes the body of the array or changes the data of the object but does not return it, then it is the output argument.

{% hint style="danger" %}

```java
public User createUser(UserDto userDto) {
    userDto.setRole("ADMIN");
    return userRepository.createUser(userDto);
}

class UserDto {
    private String email;
    private String password;
    private String role;
}
```

{% endhint %}

Output arguments should be avoided. If your function must change the state of something, have it change the state of its owning object.

{% hint style="danger" %}
public void appendFooter(StringBuffer report);

appendFooter(report);
{% endhint %}

{% hint style="success" %}
report.appendFooter();
{% endhint %}

## Separate command from query

Functions should either do something or answer something, but not both.

{% hint style="danger" %}
public boolean set(String attribute, String value);

if (set("username", "gpcoder")) {}
{% endhint %}

{% hint style="info" %}
if (attributeExists("username")) {&#x20;

&#x20; setAttribute("username", "gpcoder");

}
{% endhint %}

## Prefer exceptions to returning error codes

Returning error codes from command functions lead to deeply nested structures and maybe a violation of command query separation.

{% hint style="danger" %}

```java
public void sendShutDown() {
    DeviceHandle handle = getHandle(DEV1);

    // Check the state of the device
    if (handle != DeviceHandle.INVALID) {

        // Save the device status to the record field
        retrieveDeviceRecord(handle);

        // If not suspended, shut down
        if (record.getStatus() != DEVICE_SUSPENDED) {
            pauseDevice(handle);
            clearDeviceWorkQueue(handle);
            closeDevice(handle);
        } else {
            logger.log("Device suspended.  Unable to shut down");
        }
    } else {
        logger.log("Invalid handle for: " + DEV1.toString());
    }
}
```

{% endhint %}

{% hint style="success" %}

```java
public void sendShutDown() {
    try {
        tryToShutDown();
    } catch (DeviceShutDownError e) {
        logger.log(e);
    }
}

private void tryToShutDown() throws DeviceShutDownError {
    DeviceHandle handle = getHandle(DEV1);
    
    DeviceRecord record = retrieveDeviceRecord(handle);

    pauseDevice(handle);
    clearDeviceWorkQueue(handle);
    closeDevice(handle);
}

private DeviceHandle getHandle(DeviceID deviceID) {
    throw new DeviceShutDownError("Invalid handle for: " + deviceID);
}
```

{% endhint %}

Another exmaple:

{% hint style="danger" %}

```java
public void performLogin(UserDto userDto) {
    ResponseCode status = login(userDto);
    switch (status) {
        case BAD_REQUEST:
            log.error("Bad request");
            break;
        case USER_NOT_FOUND:
            log.error("Invalid username or password");
            break;
        case WRONG_PASSWORD:
            log.error("Invalid username or password");
            break;
        case SUCCESS:
            log.error("Login success");
            redirect();
            break;
        default:
            log.error("Unknown response");
            break;
    }
}

public ResponseCode login(UserDto userDto) {
    if (userDto.getEmail() == null || userDto.getPassword() == null) {
        return ResponseCode.BAD_REQUEST;
    }
    User user = userRepository.get(userDto);
    if (user == null) {
        return ResponseCode.USER_NOT_FOUND;
    }
    if (notMatchPassword(user.getPassword, userDto.getPassword())) {
        return ResponseCode.WRONG_PASSWORD;
    }
    setUserSession(user);
    return SUCCESS;
}
```

{% endhint %}

{% hint style="success" %}

```java
public void performLogin(UserDto userDto) {
    try {
        login(userDto);
    } catch(Exception ex) {
        log.error(ex.getMessage(), ex);
    }
    redirect();
}

public void login(UserDto userDto) {
    if (userDto.getEmail() == null || userDto.getPassword() == null) {
        throw new BadRequestException("Bad request");
    }
    User user = userRepository.get(userDto);
    if (user == null) {
        throw new UserNotFoundException("Invalid username or password");
    }
    if (notMatchPassword(user.getPassword, userDto.getPassword())) {
        throw new UserNotFoundException("Invalid username or password");
    }
    setUserSession(user);
}
```

{% endhint %}

{% hint style="info" %}
When you use exceptions rather than error codes, then new exceptions are *derivatives* of the exception class. They can be added without forcing any recompilation or redeployment.

It is better to extract the bodies of the `try` and `catch` blocks out into functions of their own.
{% endhint %}

## Put all code inside a try block

If the keyword try exists in a function, it should be the very first word in the function and there should be nothing after the catch/finally blocks.

{% hint style="danger" %}

<pre class="language-java"><code class="lang-java"><strong>public void performLogin(UserDto userDto) {
</strong>    try {
        login(userDto);
    } catch(Exception ex) {
        log.error(ex.getMessage(), ex);
        throw new BadCredentialExceptoin();
    }
    redirect(); // Should move to try block
}
</code></pre>

{% endhint %}

{% hint style="success" %}

```java
public void performLogin(UserDto userDto) {
    try {
        login(userDto);
        redirect();
    } catch(Exception ex) {
        log.error(ex.getMessage(), ex);
        throw new BadCredentialExceptoin();
    }
}
```

{% endhint %}

## The DRY principle

Refer to [DRY Principle](/clean-code/bonus/dry-principle.md)

## Structured Programming

Every block within a function should have one entry and one exit. Following these rules means that there should only be one `return` statement in a function, no `break` or `continue` statements in a loop, and never, *ever,* any `goto` statements.

If you keep your functions small, then the occasional multiple `return`, `break`, or `continue` the statement does no harm and can sometimes even be more expressive than the single-entry, single-exit rule. On the other hand, `goto` only makes sense in large functions, so it should be avoided.

## Write, test, and refine

> When I write functions, they come out long and complicated. But I also have unit tests that cover every one of those clumsy lines of code. So then I massage and refine that code, splitting out functions, changing names, eliminating duplication.
>
> — Robert C. Martin
