C'est la structure de la série de modèles de conception du GoF du point de vue des problèmes.
before
FooAPI fooAPI = new FooAPI(lat, lng);
FooPlace place = new FooPlace();
place.setAddress(fooAPI.getPostalCode() + " " + fooAPI.getAddress()); // 〒012-3456 Hoge City, Préfecture de Hoge
place.setStation(
new StringJoiner(",")
.add(fooAPI.getStation1())
.add(fooAPI.getStation2())
.add(fooAPI.getStation3())
.toString()); //Cette station,Gare de Kakiku,Gare de Sashisu
C'est un cas où le résultat de l'API est formaté et utilisé. C'est le seul endroit où le FooAPI est utilisé, et si la méthode de mise en forme ne change pas à l'avenir, c'est bien, mais compte tenu de la maintenabilité, ce n'est pas une bonne conception.
after :bulb: Adapter
public class FooAPIAdapter {
private final FooAPI fooAPI;
public FooAPIAdapter(double latitude, double longitude) {
this.fooAPI = new FooAPI(latitude, longitude);
}
public String getAddress() {
return fooAPI.getPostalCode() + fooAPI.getAddress();
}
public String getStation() {
return new StringJoiner(",")
.add(fooAPI.getStation1())
.add(fooAPI.getStation2())
.add(fooAPI.getStation3())
.toString();
}
}
FooAPIAdapter fooAPI = new FooAPIAdapter(lat, lng);
FooPlace place = new FooPlace();
place.setAddress(fooAPI.getAddress());
place.setStation(fooAPI.getStation());
Nous avons préparé un adaptateur pour le formatage des réponses API. En cas de modification de la méthode de formatage, il vous suffit de modifier cet adaptateur et cela n'affectera pas l'utilisateur.
before
public class FooSorter {
private List<FooStudent> students = new List<>();
public void add(FooStudent student) {
students.add(student);
}
public void sort() {
students.sort(
Comparator.comparingInt(
student -> student.getJapaneseScore()
+ student.getMathScore()
+ student.getEnglishScore())
.reversed());
}
public List<FooStudent> getResult() {
return students;
}
}
FooSorter sorter = new FooSorter();
sorter.add(student1);
sorter.add(student2);
sorter.add(student3);
sorter.sort();
sorter.getResult();
Le mauvais point est que l'utilisateur doit connaître la procédure de création d'une instance → add () → sort () → getResult ().
after :bulb: Facade
public class FooSorter {
private List<FooStudent> students = new List<>();
private FooSorter() {}
public static List<FooStudent> sort(FooStudent... students) {
for (FooStudent student : students)
add(student);
sort();
return getResult();
}
private void add(FooStudent student) {
students.add(student);
}
private void sort() {
students.sort(
Comparator.comparingInt(
student -> student.getJapaneseScore()
+ student.getMathScore()
+ student.getEnglishScore())
.reversed());
}
private List<FooStudent> getResult() {
return students;
}
}
FooSorter.sort(student1, student2, student3);
L'utilisateur n'a pas à penser à la commande. À cette échelle, vous pouvez écrire sur une seule ligne, vous n'avez donc pas à séparer les méthodes en Facade, mais c'est un exemple, alors veuillez bien regarder. Facade est principalement utile pour regrouper des processus complexes qui utilisent plusieurs classes en séquence.
before
public class FooPosition {
private int x;
private int y;
// x,y accesseur
public void moveAs(FooMove move) {
move.move(this);
}
}
public abstract class FooMove {
private final int addition;
public FooMove(int addition) {
this.addition = addition;
}
public abstract void move(FooPosition position);
}
public class FooMoveHorizontal extends FooMove {
public FooMoveHorizontal(int addition) {
super(addition);
}
@Override
public void move(FooPosition position) {
position.setX(position.getX() + addition);
}
}
public class FooMoveVertical extends FooMove {
public FooMoveVertical(int addition) {
super(addition);
}
@Override
public void move(FooPosition position) {
position.setY(position.getY() + addition);
}
}
FooMove moveHorizontal = new FooMoveHorizontal(x);
FooMove moveVertical = new FooMoveVertical(y);
FooPosition position = new FooPosition();
position.moveAs(moveHorizontal);
position.moveAs(moveVertical);
C'est une conception simple et bonne en soi, mais supposons que vous souhaitiez créer une classe qui gère les mouvements dans les directions x et y en même temps. (À propos, la conception qui divise l'opération de la valeur de FooPosition en FooMove et l'accepte avec moveAs est le modèle de visiteur)
after :bulb: Decorator
public class FooPosition {
private int x;
private int y;
// x,y accesseur
public void moveAs(FooMove move) {
move.move(this);
}
}
public abstract class FooMove {
private final int addition;
private final FooMove move;
public FooMove(int addition) {
this.addition = addition;
}
public FooMove(int addition, FooMove move) {
this.addition = addition;
this.move = move;
}
public abstract void move(FooPosition position);
}
public class FooMoveHorizontal extends FooMove {
public FooMoveHorizontal(int addition) {
super(addition);
}
public FooMoveHorizontal(int addition, FooMove move) {
super(addition, move);
}
@Override
public void move(FooPosition position) {
if (move != null)
move.move(position);
position.setX(position.getX() + addition);
}
}
public class FooMoveVertical extends FooMove {
public FooMoveVertical(int addition) {
super(addition);
}
public FooMoveVertical(int addition, FooMove move) {
super(addition, move);
}
@Override
public void move(FooPosition position) {
if (move != null)
move.move(position);
position.setY(position.getY() + addition);
}
}
FooMove move = new FooMoveHorizontal(x, new FooMoveVertical(y));
FooPosition position = new FooPosition();
position.moveAs(move);
Les mouvements de x et y ont été résumés.
De plus, avec cette conception, il vous suffit d'utiliser new FooMoveHorizontal (x, move)
pour ajouter plus de mouvements. (Appelez position.moveAs
une seule fois)
À cette échelle, il est plus facile de comprendre si vous créez une classe qui gère x et y en même temps, mais puisqu'il s'agit d'un exemple (omis)
before
public class FooTestUser extends FooUser {
@Override
public void foo1() {
//Utilisateur normal foo1
}
@Override
public void foo2() {
throw new RuntimeException("this operation is not permitted.");
}
@Override
public void foo3() {
throw new RuntimeException("this operation is not permitted.");
}
}
public class FooNormalUser extends FooUser {
@Override
public void foo1() {
//Utilisateur normal foo1
}
@Override
public void foo2() {
//Utilisateur normal foo2
}
@Override
public void foo3() {
throw new RuntimeException("this operation is not permitted.");
}
}
public class FooSuperUser extends FooUser {
@Override
public void foo1() {
//SuperUser foo1
}
@Override
public void foo2() {
//SuperUser foo2
}
@Override
public void foo3() {
//Traitement de foo3
}
}
TestUser veut limiter ses fonctionnalités afin que seul NormalUser foo1 puisse être exécuté. À ce stade, je veux gérer que l'implémentation de foo1 de TestUser et NormalUser devienne une copie.
after :bulb: Proxy
public class FooTestUser extends FooUser {
private FooUser normalUser = new FooNormalUser();
@Override
public void foo1() {
normalUser.foo1();
}
@Override
public void foo2() {
throw new RuntimeException("this operation is not permitted.");
}
@Override
public void foo3() {
throw new RuntimeException("this operation is not permitted.");
}
}
//L'utilisateur normal et le super utilisateur sont les mêmes qu'avant
Il n'y a pas de copier-coller de l'implémentation, et si les exigences de foo1 changent, il vous suffit de changer NormalUser. Cependant, comme Java 8 a ajouté une implémentation par défaut de l'interface, il peut être préférable que vous souhaitiez simplement partager l'implémentation. (Idem pour le modèle de pont) Si vous n'avez pas SuperUser, je pense qu'il est plus simple d'implémenter foo1 dans la classe parent (FooUser).
Recommended Posts