Object-oriented FizzBuzz (Java)

Introduction

This article is the 11th day article of FizzBuzz Advent Calender 2017.

Everyone loves FizzBuzz. It is also used at our company as the first question of training before joining the company. No matter what language you use, it's supposed to be a basic one-liner, so there's no problem, but I was wondering what would happen if I wrote it in object-oriented (OOP), so I wrote it in Java.

A review of FizzBuzz

Let's take a quick look at FizzBuzz again.

Problem statement

Count the numbers from 1 to 100 and

--Fizz for multiples of 3 --Buzz for multiples of 5 --FizzBuzz for multiples of 3 and 5 --Other than that, that number

Is output.

Ordinary answer

It's just a normal Java answer.

public class FizzBuzz {
    public static void main(String[] args) {
        for (int i = 1; i <= 100; i++) {
            if (i % 3 == 0 && i % 5 == 0) {
                System.out.println("FizzBuzz");
            } else if (i % 3 == 0) {
                System.out.println("Fizz");
            } else if (i % 5 == 0) {
                System.out.println("Buzz");
            } else {
                System.out.println(i);
            }
        }
    }
}

In order to make this object-oriented code, we will first design the class.

Class design

Follow the steps below to design a class.

  1. Identify objects
  2. Examining the model
  3. Selection of class name

1. Identify objects

First, identify the objects that appear in the problem.

  1. An object that "counts numbers from 1 to 100 and outputs the result according to the conditions"
  2. An object that "checks for multiples of 3 and returns Fizz"
  3. An object that "checks for multiples of 5 and returns Buzz"
  4. An object that "checks multiples of 3 and 5 and returns FizzBuzz"

There is no particular problem because it is just summarized by role.

2. Model review

Consider the required model from the objects given above.

First of all, "count the numbers from 1 to 100 and output the result according to the conditions". It has an input / output function of "getting a range of numerical values and conditions, and outputting a character string according to the conditions".

Next, regarding 2. ~ 4, it can be said that they have the same function except for the specific numerical values and character strings. It seems that it can be decomposed into two functions, "check if it is divisible by the specified number (check with and if there are multiple)" and "hold the character string corresponding to the condition".

In summary, it would be nice to have the following three models.

--Get a range of numbers and conditions, and output a character string according to the conditions --Check if it is divisible by the specified number (check with and if there are multiple) --Hold the string corresponding to the condition

3. Selection of class name

Let's name the three models mentioned above. The name is used as it is as the class name.

name function
Operator Get a range of numbers and conditions, and output a character string according to the conditions
Specification Check if it is divisible by the specified number
Operation Holds the string corresponding to the condition

Models like Specification are introduced in Domain Driven Development (DDD) in a pattern called ** Specification Pattern **. Now that we have the necessary classes, we will implement them.

Implementation

Based on the designed model, we will drop it into the Java code. (Since it is placed from the side to be used, it is different from the model order above.)

Specification.java


import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class Specification {

    private List<Predicate<Integer>> predicateList = new ArrayList<>();

    public Specification(Predicate<Integer> predicate) {
        this.predicateList.add(predicate);
    }

    private Specification(List<Predicate<Integer>> predicateList) {
        this.predicateList = predicateList;
    }

    public Specification and(Predicate<Integer> predicate) {
        List<Predicate<Integer>> results = new ArrayList<>(this.predicateList);
        results.add(predicate);
        return new Specification(results);
    }

    public boolean isSatisfiedBy(Integer number) {
        return this.predicateList.stream().allMatch(p -> p.test(number));
    }

}

There are four main points in implementing Specification.java:

--Implemented as a DDD specification pattern --Use Java's Predicate for the part that expresses the condition --Implemented to receive at least one condition in the constructor and chain it with ʻand` if there are multiple conditions --Implemented as a first class collection (immutable)

Operation.java


public class Operation {

    private Specification specification;
    private String message;

    Operation(Specification specification, String message) {
        this.specification = specification;
        this.message = message;
    }

    public Specification getSpecification() {
        return this.specification;
    }

    public String getMessage() {
        return message;
    }

}

Operator.java


import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

public class Operator {

    private List<Operation> operationList = new ArrayList<>();

    public void addOperation(Operation operation) {
        this.operationList.add(operation);
    }

    public void run(IntStream range) {
        range.forEach(number -> {
            String message = this.operationList.stream()
                    .filter(operation -> operation.getSpecification().isSatisfiedBy(number))
                    .map(Operation::getMessage)
                    .findFirst()
                    .orElse(String.valueOf(number));
            System.out.println(message);
        });
    }

}

The part to check the condition is written in one liner using Stream. The part of .findFirst (). OrElse () needs to be filled in to see if there is a better way to write it.

I will also write the code to run FizzBuzz using the above class.

FizzBuzz.java


import java.util.function.Predicate;
import java.util.stream.IntStream;

public class FizzBuzz {

    public static void main(String[] args) {
        Operator operator = new Operator();

        Predicate<Integer> divisibleBy3 = DivisiblePredicateFactory.divisibleBy(3);
        Predicate<Integer> divisibleBy5 = DivisiblePredicateFactory.divisibleBy(5);

        Operation fizzbuzz = new Operation(new Specification(divisibleBy3).and(divisibleBy5), "FizzBuzz");
        Operation fizz = new Operation(new Specification(divisibleBy3), "Fizz");
        Operation buzz = new Operation(new Specification(divisibleBy5), "Buzz");

        operator.addOperation(fizzbuzz);
        operator.addOperation(fizz);
        operator.addOperation(buzz);

        operator.run(IntStream.rangeClosed(1, 100));
    }

}

DivisiblePredicateFactory.java


import java.util.function.Predicate;

public class DivisiblePredicateFactory {

    public static Predicate<Integer> divisibleBy(Integer divisor) {
        return n -> n % divisor == 0;
    }

}

If you look at FizzBuzz.java, you can see that the role of each class is clear and that the writing style is linked to the problem statement. Even if you have an additional spec like "Bar if it's a multiple of 7," you can easily imagine where to edit it. This is the good thing about object-oriented programming.

Summary

This time I wrote FizzBuzz in an object-oriented manner. Even such a simple problem was quite interesting when seriously considering object orientation. Next time I will write a test.

The code posted here is summarized on Github, so if you like, please try running it at hand and improve it again. (And please pull request.) https://github.com/totto357/OOP-FizzBuzz

Tomorrow's 12th day will be @ aimof's "FizzBuzz consisting only of one-line function definitions, excluding executable statements"!

bonus

It is an implementation with the builder pattern of the rejected Operation class. It seemed good to be able to express "when ~~, display xx" in the code, but I rejected it because it didn't look like Java. However, I will leave it because it is a big deal.

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class OperationWithBuilder {

    private Specification specification;
    private String message;

    private Operation(Builder builder) {
        this.specification = builder.getSpecification();
        this.message = builder.getMessage();
    }
    
    public static Builder when(Predicate<Integer> predicate) {
        return new Builder(predicate);
    }
    
    @Getter
    static class Builder {
    
        private Specification specification;
        private String message;
        
        public Builder(Predicate<Integer> predicate) {
            this.specification = new Specification(predicate);
        }
        
        public Builder and(Predicate<Integer> predicate) {
            this.specification = this.specification.and(predicate);
            return this;
        }
        
        public Operation print(String message) {
            this.message = message;
            return new Operation(this);
        }
    
    }

}

When using

Predicate<Integer> divisibleBy3 = DivisiblePredicateFactory.divisibleBy(3);
Predicate<Integer> divisibleBy5 = DivisiblePredicateFactory.divisibleBy(5);

Operation fizz = Operation.when(divisibleBy3).print("Fizz");
Operation fizzbuzz = Operation.when(divisibleBy3).and(divisibleBy5).print("FizzBuzz");

Recommended Posts

Object-oriented FizzBuzz (Java)
[Java] Object-oriented
[Java] Object-oriented summary_Part 1
[Java] Object-oriented syntax-Constructor
Object-oriented (Java) basics
[Java] Object-oriented summary_Part 2
FizzBuzz in Java
[Java] Object-oriented syntax-Package
Object-oriented (java) with Strike Gundam
Object-oriented summary by beginners (Java)
Java
Java 3 major elements (object-oriented) memorandum
Java
[Java] Object-oriented syntax --class method / argument
[Java] Object-oriented syntax-class / field / method / scope
Summary of object-oriented programming using Java
I compared Java and Ruby's FizzBuzz.
Java learning (0)
Studying Java ―― 3
[Java] array
Java protected
[Java] Annotation
[Java] Module
Studying Java ―― 9
Java scratch scratch
Java tips, tips
Java methods
Java method
java (constructor)
Java array
[Java] ArrayDeque
java (override)
java (method)
Object-oriented summary
Java Day 2018
Java string
java (array)
Object-oriented programming
Java static
Java serialization
java beginner 4
JAVA paid
Studying Java ―― 4
Java (set)
java shellsort
[Java] compareTo
Studying Java -5
java reflexes
java (interface)
Java memorandum
☾ Java / Collection
Java array
Studying Java ―― 1
[Java] Array
[Java] Polymorphism
Studying Java # 0
Java review
java framework
Java features
[Java] Inheritance
FastScanner Java