Cet article est l'article du 11ème jour du Calendrier de l'Avent FizzBuzz 2017.
Tout le monde aime Fizz Buzz.
Il est également utilisé dans notre entreprise comme première question de formation avant de rejoindre l'entreprise.
Quel que soit le langage, il devrait s'agir d'une ligne de base, donc il n'y a pas de problème, mais je me demandais ce qui se passerait si je l'écrivais en orienté objet (POO), alors je l'ai écrit en Java
.
Jetons à nouveau un coup d'œil à FizzBuzz
.
Comptez les nombres de 1 à 100,
--Fizz pour les multiples de 3 --Buzz pour les multiples de 5 --FizzBuzz pour les multiples de 3 et 5 --Autre que ça, ce numéro
Est sortie.
C'est juste une réponse "Java" normale.
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);
}
}
}
}
Afin de créer ce code orienté objet, nous allons d'abord concevoir la classe.
Suivez les étapes ci-dessous pour concevoir une classe.
Tout d'abord, identifiez les objets qui apparaissent dans le problème.
Il n'y a pas de problème particulier car il est simplement résumé par rôle.
Considérez le modèle requis parmi les objets donnés ci-dessus.
Tout d'abord, "comptez les nombres de 1 à 100 et sortez le résultat selon les conditions". Il a une fonction d'entrée / sortie "d'obtenir une plage de valeurs numériques et de conditions, et de sortir une chaîne de caractères selon les conditions".
Ensuite, concernant 2. ~ 4, on peut dire qu'ils ont la même fonction à l'exception des valeurs numériques et des chaînes de caractères spécifiques. En tant que fonction, il semble qu'il puisse être décomposé en deux fonctions, "vérifier s'il est divisible par le nombre spécifié (vérifier avec et s'il y en a plusieurs)" et "tenir la chaîne de caractères correspondant à la condition".
En résumé, ce serait bien d'avoir les trois modèles suivants.
Citons les trois modèles mentionnés ci-dessus. Utilisez le nom tel quel comme nom de classe.
Nom | une fonction |
---|---|
Operator | Obtenez une plage de nombres et de conditions, et affichez une chaîne de caractères en fonction des conditions |
Specification | Vérifiez s'il est divisible par le nombre spécifié |
Operation | Contient la chaîne correspondant à la condition |
Des modèles tels que Specification
sont introduits dans Domain Driven Development (DDD) dans un modèle appelé ** Specification Pattern **.
Maintenant que nous avons les classes nécessaires, nous allons les implémenter.
Sur la base du modèle conçu, nous le déposerons dans le code Java
. (Puisqu'il est placé du côté à utiliser, il est différent de l'ordre du modèle ci-dessus.)
Stream
est utilisé, un environnement de Java8
ou supérieur est requis.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));
}
}
Il y a quatre points principaux dans l'implémentation de Specification.java
:
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);
});
}
}
La partie pour vérifier la condition est écrite dans une doublure en utilisant Stream
.
La partie de .findFirst (). OrElse ()
doit être remplie pour voir s'il existe une meilleure façon de l'écrire.
J'écrirai également le code pour exécuter FizzBuzz
en utilisant la classe ci-dessus.
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;
}
}
Si vous regardez FizzBuzz.java
, vous pouvez voir que le rôle de chaque classe est clair et que le style d'écriture est lié à l'énoncé du problème.
Même s'il existe une spécification supplémentaire telle que "Bar si c'est un multiple de 7", vous pouvez facilement imaginer où éditer.
C'est le bon point de la programmation orientée objet.
Cette fois, j'ai écrit FizzBuzz
d'une manière orientée objet.
Même avec un problème aussi simple, il était assez intéressant de réfléchir sérieusement à l'orientation des objets.
La prochaine fois, j'écrirai un test.
Le code publié ici est résumé sur Github, donc si vous le souhaitez, essayez de l'exécuter à portée de main et essayez de l'améliorer à nouveau. (Et veuillez tirer la demande.) https://github.com/totto357/OOP-FizzBuzz
Le 12ème jour de demain sera le "FizzBuzz de @ aimof composé uniquement de définitions de fonctions sur une ligne, à l'exclusion des instructions d'exécution"!
Il s'agit d'une implémentation dans le modèle Builder de la classe Operation rejetée.
Cela semblait bien de pouvoir exprimer "quand ~~, afficher xx" dans le code, mais je l'ai rejeté car il ne ressemblait pas à Java
.
Cependant, je vais le laisser car c'est un gros problème.
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);
}
}
}
Lors de l'utilisation
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