Cette fois, j'ai eu l'occasion de mettre en œuvre les spécifications organisées dans la table de décision, je voudrais donc expliquer comment implémenter un programme qui fonctionne selon la définition.
Une table de décision est une table qui résume les conditions et les actions possibles pour un certain problème. Pour plus de détails, veuillez consulter le site "Explication de la table de décision", qui est facile à comprendre. Le tableau de décision (calcul de la remise des frais de stationnement) et le chiffre suivant, qui font l'objet de ce temps, sont également cités à partir de ce site.
Partie description de la condition (talon de condition) | Entrée de condition |
---|---|
Partie de description de l'action (talon d'action) | Entrée d'action |
Le contenu expliqué dans cet article est le suivant.
Il est unique que chacune des conditions soit définie sur Règle, mais j'ai essayé de concevoir la classe pour qu'elle soit aussi cohérente que l'apparence de la table de décision. En conséquence, nous avons pu le réaliser avec 6 classes (3 interfaces et 1 classe abstraite). Comme vous pouvez le voir dans le contenu, il est implémenté uniquement avec des fonctions Java standard et sans branchement conditionnel.
2.1. DecisionAction
Il s'agit d'une interface pour définir des actions qui sont les résultats de jugement de la table de décision. Le contenu peut être librement défini en fonction de la table de décision que vous souhaitez réaliser.
DecisionAction.java
public interface DecisionAction {
}
2.2. RuleInput
Une interface pour définir les données d'entrée de Rule.
RuleInput.java
public interface RuleInput {
}
2.3. Rule
Une interface pour implémenter une logique de jugement conditionnel. ʻImplémenter le processus de jugement d'une condition dans la méthode Object evaluer (entrée RuleInput) `. La valeur de retour sera la valeur utilisée dans le jugement de la partie de spécification de condition. Tout type de données Java standard (valeur valide, type de chaîne, type numérique) fera l'affaire.
Rule.java
public interface Rule<I extends RuleInput> {
Object evaluate(RuleInput input);
@SuppressWarnings("unchecked")
default I getInput(RuleInput input) {
return (I) input;
};
}
2.4. ConditionStub
Cette classe est un argument lors de l'exécution d'un jugement conditionnel. Comme vous pouvez le voir, il s'agit simplement d'un support pour la saisie de règles et de règles.
ConditionStub.java
public class ConditionStub {
private Map<Rule<? extends RuleInput>, RuleInput> rules = new HashMap<>();
public void when(Rule<? extends RuleInput> rule, RuleInput input) {
rules.put(rule, input);
}
public Map<Rule<? extends RuleInput>, RuleInput> getRules() {
return rules;
}
}
2.5. ConditionEntry
Cette classe définit la partie description des conditions de la table de décision et l'opération déterminée à ce moment.
En tant qu'élément de conservation des données, l'un des modèles de données définis dans les numéros 1 à 8 est conservé.
Le but est d'utiliser le hashCode
de Set
comme id pour l'identifier de manière unique.
ConditionEntry.java
public class ConditionEntry<A extends DecisionAction> {
private Set<String> resultMap = new HashSet<>();
private A action;
public void when(@SuppressWarnings("rawtypes") Class ruleClass, Object entry) {
resultMap.add(ruleClass.getSimpleName() + "/" + entry);
}
public void then(A action) {
this.action = action;
}
public int getId() {
return resultMap.hashCode();
}
public A getAction() {
return action;
}
}
2.6. DecisionTable
Une classe qui définit une table de décision.
En tant que données, il ne contient que ConditionEntry
dans Map. S'il y a 8 modèles de données, # 1 à # 8
, 8ConditionEntry
s sont stockés dans Map.
Lors de son utilisation, définissez les données de la table de décision avec ʻinitDecisionTable () `.
Le processus de détermination de l'action de la table de décision est la méthode «résoudre».
Comme vous pouvez le voir dans le contenu, tout ce que vous avez à faire est d'exécuter la règle séquentiellement pour créer une instance de ConditionEntry
et vérifier si l'id correspondant est enregistré dans la table de décision.
DecisionTable.java
public abstract class DecisionTable<A extends DecisionAction> {
private Map<Integer, ConditionEntry<A>> decisionTable;
public DecisionTable() {
decisionTable = initDecisionTable();
}
protected abstract Map<Integer, ConditionEntry<A>> initDecisionTable();
public A resolve(ConditionStub conditionStub) {
// execute rule
ConditionEntry<A> result = new ConditionEntry<>();
conditionStub.getRules().forEach((R, I) -> {
result.when(R.getClass(), R.evaluate(I));
});
// search action
Integer key = result.getId();
if (decisionTable.containsKey(key)) {
return decisionTable.get(key).getAction();
}
return null;
}
}
Implémentons le tableau de décision "Calcul de la remise des frais de stationnement". Il sera mis en œuvre tel qu'il en a vraiment l'air. Si vous souhaitez l'implémenter plus simplement, reportez-vous à "[4. Mise en œuvre du tableau de décision de calcul de la remise des frais de stationnement (version simple)](# 4-Mise en œuvre du tableau de décision de calcul de la remise des frais de stationnement, version simple)" S'il te plait donne moi.
ParkingDiscountAction.java
public class ParkingDiscountAction implements DecisionAction {
private final boolean discount30Minute;
private final boolean discount1Hour;
private final boolean discount2Hour30Minute;
private final boolean discount3Hour30Minute;
//Le constructeur est omis. Préparez un constructeur qui prend un champ comme argument.
//getter est omis. Aucun setter n'est nécessaire car il ne contient que les résultats de la table de décision.
//ToString pour vérifier le contenu()Il est pratique de générer automatiquement la méthode avec eclipse.
}
PriceRuleInput.java
public class PriceRuleInput implements RuleInput {
private final int paymentPrice;
//Le constructeur est omis. Préparez un constructeur qui prend un champ comme argument.
//getter est omis. La valeur d'entrée étant immuable, aucun setter n'est requis.
}
PriceRule1.java
public class PriceRule1 implements Rule<PriceRuleInput> {
//Logique de traitement pour juger "3000 yens ou plus et moins de 10000 yens"
@Override
public Object evaluate(RuleInput input) {
PriceRuleInput priceRuleInput = getInput(input);
int paymentPrice = priceRuleInput.getPaymentPrice();
if (paymentPrice >= 3000 && paymentPrice < 10000) {
return true;
}
return false;
}
}
PriceRule2.java
public class PriceRule2 implements Rule<PriceRuleInput> {
//Logique de traitement pour juger "10 000 yens ou plus et moins de 30 000 yens"
@Override
public Object evaluate(RuleInput input) {
PriceRuleInput priceRuleInput = getInput(input);
int paymentPrice = priceRuleInput.getPaymentPrice();
if (paymentPrice >= 10000 && paymentPrice < 30000) {
return true;
}
return false;
}
}
PriceRule3.java
public class PriceRule3 implements Rule<PriceRuleInput> {
//Logique de traitement pour juger "30 000 yens ou plus"
@Override
public Object evaluate(RuleInput input) {
PriceRuleInput priceRuleInput = getInput(input);
int paymentPrice = priceRuleInput.getPaymentPrice();
if (paymentPrice >= 30000) {
return true;
}
return false;
}
}
CinemaRuleInput.java
public class CinemaRuleInput implements RuleInput {
private final boolean watchCinema;
//Le constructeur est omis. Préparez un constructeur qui prend un champ comme argument.
//getter est omis. La valeur d'entrée étant immuable, aucun setter n'est requis.
}
CinemaRule.java
public class CinemaRule implements Rule<CinemaRuleInput> {
@Override
public Object evaluate(RuleInput input) {
CinemaRuleInput cinemaRuleInput = getInput(input);
return cinemaRuleInput.isWatchCinema();
}
}
Définissez comme «ConditionEntry» dans le cadre rouge de la table de décision.
La condition est ajoutée par la méthode when ()
, et l'opération à ce moment-là est définie par la méthode then ()
.
Après avoir défini les huit modèles de données # 1 à # 8 dans la table de décision, ajoutez chacun à la carte. La clé à ce moment-là est l'id obtenu par getId ()
de ConditionEntry
.
ParkingDiscountDecisionTable.java
public class ParkingDiscountDecisionTable extends DecisionTable<ParkingDiscountAction> {
@Override
protected Map<Integer, ConditionEntry<ParkingDiscountAction>> initDecisionTable() {
// #1
ConditionEntry<ParkingDiscountAction> pattern01 = new ConditionEntry<>();
pattern01.when(PriceRule1.class, false);
pattern01.when(PriceRule2.class, false);
pattern01.when(PriceRule3.class, false);
pattern01.when(CinemaRule.class, false);
pattern01.then(new ParkingDiscountAction(true, false, false, false));
// #2
ConditionEntry<ParkingDiscountAction> pattern02 = new ConditionEntry<>();
pattern02.when(PriceRule1.class, true);
pattern02.when(PriceRule2.class, false);
pattern02.when(PriceRule3.class, false);
pattern02.when(CinemaRule.class, false);
pattern02.then(new ParkingDiscountAction(false, true, false, false));
// #3
ConditionEntry<ParkingDiscountAction> pattern03 = new ConditionEntry<>();
pattern03.when(PriceRule1.class, false);
pattern03.when(PriceRule2.class, true);
pattern03.when(PriceRule3.class, false);
pattern03.when(CinemaRule.class, false);
pattern03.then(new ParkingDiscountAction(false, false, true, false));
// #4
ConditionEntry<ParkingDiscountAction> pattern04 = new ConditionEntry<>();
pattern04.when(PriceRule1.class, false);
pattern04.when(PriceRule2.class, false);
pattern04.when(PriceRule3.class, true);
pattern04.when(CinemaRule.class, false);
pattern04.then(new ParkingDiscountAction(false, false, false, true));
// #5
ConditionEntry<ParkingDiscountAction> pattern05 = new ConditionEntry<>();
pattern05.when(PriceRule1.class, false);
pattern05.when(PriceRule2.class, false);
pattern05.when(PriceRule3.class, false);
pattern05.when(CinemaRule.class, true);
pattern05.then(new ParkingDiscountAction(false, false, true, false));
// #6
ConditionEntry<ParkingDiscountAction> pattern06 = new ConditionEntry<>();
pattern06.when(PriceRule1.class, true);
pattern06.when(PriceRule2.class, false);
pattern06.when(PriceRule3.class, false);
pattern06.when(CinemaRule.class, true);
pattern06.then(new ParkingDiscountAction(false, false, true, false));
// #7
ConditionEntry<ParkingDiscountAction> pattern07 = new ConditionEntry<>();
pattern07.when(PriceRule1.class, false);
pattern07.when(PriceRule2.class, true);
pattern07.when(PriceRule3.class, false);
pattern07.when(CinemaRule.class, true);
pattern07.then(new ParkingDiscountAction(false, false, true, false));
// #8
ConditionEntry<ParkingDiscountAction> pattern08 = new ConditionEntry<>();
pattern08.when(PriceRule1.class, false);
pattern08.when(PriceRule2.class, false);
pattern08.when(PriceRule3.class, true);
pattern08.when(CinemaRule.class, true);
pattern08.then(new ParkingDiscountAction(false, false, false, true));
// create map
Map<Integer, ConditionEntry<ParkingDiscountAction>> tables = new HashMap<>();
tables.put(pattern01.getId(), pattern01);
tables.put(pattern02.getId(), pattern02);
tables.put(pattern03.getId(), pattern03);
tables.put(pattern04.getId(), pattern04);
tables.put(pattern05.getId(), pattern05);
tables.put(pattern06.getId(), pattern06);
tables.put(pattern07.getId(), pattern07);
tables.put(pattern08.getId(), pattern08);
return tables;
}
}
Utiliser la table de décision est facile, il suffit de créer une instance de «ConditionStub», puis de la passer comme argument à la méthode «resolver ()» de «ParkingDiscountDecisionTable» et de l'exécuter.
Il est recommandé de réutiliser l'instance ParkingDiscountDecisionTable
au lieu de la créer à chaque fois, car la définition de la table de décision ne change pas pendant l'exécution.
ParkingDiscountService.java
public class ParkingDiscountService {
//Injection si vous utilisez un conteneur DI
PriceRule1 priceRule1;
PriceRule2 priceRule2;
PriceRule3 priceRule3;
CinemaRule cinemaRule;
ParkingDiscountDecisionTable parkingDiscountDecisionTable;
public ParkingDiscountService() {
//Instance au lieu d'injection
priceRule1 = new PriceRule1();
priceRule2 = new PriceRule2();
priceRule3 = new PriceRule3();
cinemaRule = new CinemaRule();
parkingDiscountDecisionTable = new ParkingDiscountDecisionTable();
}
public void business(int paymentPrice, boolean watchCinema) {
// create input data
PriceRuleInput priceRuleInput = new PriceRuleInput(paymentPrice);
CinemaRuleInput cinemaRuleInput = new CinemaRuleInput(watchCinema);
// create conditionStub
ConditionStub conditionStub = new ConditionStub();
conditionStub.when(priceRule1, priceRuleInput);
conditionStub.when(priceRule2, priceRuleInput);
conditionStub.when(priceRule3, priceRuleInput);
conditionStub.when(cinemaRule, cinemaRuleInput);
// resolve by decisionTable
ParkingDiscountAction parkingDiscountAction = parkingDiscountDecisionTable.resolve(conditionStub);
System.out.println("paymentPrice : " + paymentPrice + ", watchCinema : " + watchCinema);
System.out.println(parkingDiscountAction);
}
}
Demo.java
public class Demo {
public static void main(String[] args) {
//Si vous utilisez un conteneur DI, récupérez-le à partir de là
//Cette fois, créez une instance sur place
ParkingDiscountService service = new ParkingDiscountService();
// # 1
service.business(2000, false);
// # 2
service.business(5000, false);
// # 3
service.business(17000, false);
// # 4
service.business(45000, false);
// # 5
service.business(100, true);
// # 6
service.business(7000, true);
// # 7
service.business(20000, true);
// # 8
service.business(100000, true);
}
}
Résultat d'exécution
paymentPrice : 2000, watchCinema : false
ParkingDiscountAction [discount30Minute=true, discount1Hour=false, discount2Hour30Minute=false, discount3Hour30Minute=false]
paymentPrice : 5000, watchCinema : false
ParkingDiscountAction [discount30Minute=false, discount1Hour=true, discount2Hour30Minute=false, discount3Hour30Minute=false]
paymentPrice : 17000, watchCinema : false
ParkingDiscountAction [discount30Minute=false, discount1Hour=false, discount2Hour30Minute=true, discount3Hour30Minute=false]
paymentPrice : 45000, watchCinema : false
ParkingDiscountAction [discount30Minute=false, discount1Hour=false, discount2Hour30Minute=false, discount3Hour30Minute=true]
paymentPrice : 100, watchCinema : true
ParkingDiscountAction [discount30Minute=false, discount1Hour=false, discount2Hour30Minute=true, discount3Hour30Minute=false]
paymentPrice : 7000, watchCinema : true
ParkingDiscountAction [discount30Minute=false, discount1Hour=false, discount2Hour30Minute=true, discount3Hour30Minute=false]
paymentPrice : 20000, watchCinema : true
ParkingDiscountAction [discount30Minute=false, discount1Hour=false, discount2Hour30Minute=true, discount3Hour30Minute=false]
paymentPrice : 100000, watchCinema : true
ParkingDiscountAction [discount30Minute=false, discount1Hour=false, discount2Hour30Minute=false, discount3Hour30Minute=true]
Je ne sais pas si cela s'appelle une table de décision, mais les spécifications écrites sont les mêmes que la table de décision mentionnée ci-dessus. Si vous implémentez cela tel qu'il en a l'air, ce sera plus simple que le tableau de décision mentionné ci-dessus.
Tant que l'interface DecisionAction
est implémentée, il n'y a pas de limite au contenu, donc cette fois nous utiliserons un ENUM appelé Parking Discount.
ParkingDiscountAction.java
public class ParkingDiscountAction implements DecisionAction {
private final ParkingDiscount discount;
//Le constructeur est omis. Préparez un constructeur qui prend un champ comme argument.
//getter est omis. Aucun setter n'est nécessaire car il ne contient que les résultats de la table de décision.
//ToString pour vérifier le contenu()Il est pratique de générer automatiquement la méthode avec eclipse.
}
ParkingDiscount.java
public enum ParkingDiscount {
THIRTY_MINUTE(1),
ONE_HOUR(2),
TWO_HOUR_THIRTY_MINUTE(3),
THREE_HOUR_THIRTY_MINUTE(4);
private int code;
//Le constructeur est omis. Préparez un constructeur qui prend un champ comme argument.
//getter est omis.
}
Implémentez PriceRule1, PriceRule2 et PriceRule3 mentionnés ci-dessus ensemble. La valeur de retour cette fois est un type numérique (int).
PriceRule.java
public class PriceRule implements Rule<PriceRuleInput> {
@Override
public Object evaluate(RuleInput input) {
PriceRuleInput priceRuleInput = getInput(input);
int paymentPrice = priceRuleInput.getPaymentPrice();
if (paymentPrice >= 3000 && paymentPrice < 10000) {
return 1;
} else if (paymentPrice >= 10000 && paymentPrice < 30000) {
return 2;
} else if (paymentPrice >= 30000) {
return 3;
} else {
return 0;
}
}
}
Étant donné que la valeur de retour de «PriceRule» est un type numérique (int), la valeur est également définie en conséquence dans la méthode «when».
De même, la méthode then
a le constructeur de ParkingDiscountAction
changé en ENUM, définissez-le en conséquence.
ParkingDiscountDecisionTable.java
public class ParkingDiscountDecisionTable extends DecisionTable<ParkingDiscountAction> {
@Override
protected Map<Integer, ConditionEntry<ParkingDiscountAction>> initDecisionTable() {
// #1
ConditionEntry<ParkingDiscountAction> pattern01 = new ConditionEntry<>();
pattern01.when(PriceRule.class, 0);
pattern01.when(CinemaRule.class, false);
pattern01.then(new ParkingDiscountAction(ParkingDiscount.THIRTY_MINUTE));
// #2
ConditionEntry<ParkingDiscountAction> pattern02 = new ConditionEntry<>();
pattern02.when(PriceRule.class, 1);
pattern02.when(CinemaRule.class, false);
pattern02.then(new ParkingDiscountAction(ParkingDiscount.ONE_HOUR));
// #3
ConditionEntry<ParkingDiscountAction> pattern03 = new ConditionEntry<>();
pattern03.when(PriceRule.class, 2);
pattern03.when(CinemaRule.class, false);
pattern03.then(new ParkingDiscountAction(ParkingDiscount.TWO_HOUR_THIRTY_MINUTE));
// #4
ConditionEntry<ParkingDiscountAction> pattern04 = new ConditionEntry<>();
pattern04.when(PriceRule.class, 3);
pattern04.when(CinemaRule.class, false);
pattern04.then(new ParkingDiscountAction(ParkingDiscount.THREE_HOUR_THIRTY_MINUTE));
// #5
ConditionEntry<ParkingDiscountAction> pattern05 = new ConditionEntry<>();
pattern05.when(PriceRule.class, 0);
pattern05.when(CinemaRule.class, true);
pattern05.then(new ParkingDiscountAction(ParkingDiscount.TWO_HOUR_THIRTY_MINUTE));
// #6
ConditionEntry<ParkingDiscountAction> pattern06 = new ConditionEntry<>();
pattern06.when(PriceRule.class, 1);
pattern06.when(CinemaRule.class, true);
pattern06.then(new ParkingDiscountAction(ParkingDiscount.TWO_HOUR_THIRTY_MINUTE));
// #7
ConditionEntry<ParkingDiscountAction> pattern07 = new ConditionEntry<>();
pattern07.when(PriceRule.class, 2);
pattern07.when(CinemaRule.class, true);
pattern07.then(new ParkingDiscountAction(ParkingDiscount.TWO_HOUR_THIRTY_MINUTE));
// #8
ConditionEntry<ParkingDiscountAction> pattern08 = new ConditionEntry<>();
pattern08.when(PriceRule.class, 3);
pattern08.when(CinemaRule.class, true);
pattern08.then(new ParkingDiscountAction(ParkingDiscount.THREE_HOUR_THIRTY_MINUTE));
// create map
Map<Integer, ConditionEntry<ParkingDiscountAction>> tables = new HashMap<>();
tables.put(pattern01.getId(), pattern01);
tables.put(pattern02.getId(), pattern02);
tables.put(pattern03.getId(), pattern03);
tables.put(pattern04.getId(), pattern04);
tables.put(pattern05.getId(), pattern05);
tables.put(pattern06.getId(), pattern06);
tables.put(pattern07.getId(), pattern07);
tables.put(pattern08.getId(), pattern08);
return tables;
}
}
ParkingDiscountService.java
public class ParkingDiscountService {
//Injection si vous utilisez un conteneur DI
PriceRule priceRule;
CinemaRule cinemaRule;
ParkingDiscountDecisionTable parkingDiscountDecisionTable;
public ParkingDiscountService() {
//Instance au lieu d'injection
priceRule = new PriceRule();
cinemaRule = new CinemaRule();
parkingDiscountDecisionTable = new ParkingDiscountDecisionTable();
}
public void business(int paymentPrice, boolean watchCinema) {
// create input data
PriceRuleInput priceRuleInput = new PriceRuleInput(paymentPrice);
CinemaRuleInput cinemaRuleInput = new CinemaRuleInput(watchCinema);
// create conditionStub
ConditionStub conditionStub = new ConditionStub();
conditionStub.when(priceRule, priceRuleInput);
conditionStub.when(cinemaRule, cinemaRuleInput);
// resolve by decisionTable
ParkingDiscountAction parkingDiscountAction = parkingDiscountDecisionTable.resolve(conditionStub);
System.out.println("paymentPrice : " + paymentPrice + ", watchCinema : " + watchCinema);
System.out.println(parkingDiscountAction);
}
}
Résultat d'exécution
paymentPrice : 2000, watchCinema : false
ParkingDiscountAction [discount=THIRTY_MINUTE]
paymentPrice : 5000, watchCinema : false
ParkingDiscountAction [discount=ONE_HOUR]
paymentPrice : 17000, watchCinema : false
ParkingDiscountAction [discount=TWO_HOUR_THIRTY_MINUTE]
paymentPrice : 45000, watchCinema : false
ParkingDiscountAction [discount=THREE_HOUR_THIRTY_MINUTE]
paymentPrice : 100, watchCinema : true
ParkingDiscountAction [discount=TWO_HOUR_THIRTY_MINUTE]
paymentPrice : 7000, watchCinema : true
ParkingDiscountAction [discount=TWO_HOUR_THIRTY_MINUTE]
paymentPrice : 20000, watchCinema : true
ParkingDiscountAction [discount=TWO_HOUR_THIRTY_MINUTE]
paymentPrice : 100000, watchCinema : true
ParkingDiscountAction [discount=THREE_HOUR_THIRTY_MINUTE]
Cette fois, j'ai expliqué comment implémenter la table de décision, qui est implémentée telle qu'elle en a l'air.
En faisant bon usage des collections (Set, Map), nous avons pu l'implémenter sans utiliser de branchement conditionnel.
Il semble difficile d'implémenter la classe DecisionTable
qui définit le contenu de la table de décision, mais comme le contenu est standard, il semble préférable de le générer automatiquement à partir du document de conception dans un projet réel.
Recommended Posts