Maison> Java> javaDidacticiel> le corps du texte

Comment implémenter la commande Set du client Tile intégré Springboot

王林
Libérer: 2023-05-19 13:37:10
avant
1287 Les gens l'ont consulté

    set command syntax

    SET key id [FIELD name value ...] [EX seconds] [NX|XX] (OBJECT geojson)|(POINT lat lon z)|(BOUNDS minlat minlon maxlat maxlon) |(HASH geohash)|(STRING value)

    La commandesetest équivalente à l'utilisation de la commandehashdans redis, et est également une cléetid, mais la différence est que la commandesetde Tile38 peut également contenir davantage d'autres attributs, tels que la personnalisation du champFIELD, vous pouvez également définir la période de validitéEX, etc., nous devons alors concevoir uneAPI Javautile pour cette syntaxe afin que les développeurs puissent mieux utiliser Tile38.set命令就相当于redis中的hash命令的使用,也是一个keyid的组合,但是不同的是,Tile38的set命令还可以携带更多的其他属性,比如可以自定义FIELD字段,还可以设置EX有效期等等,那么我们需要给这个语法设计一套好用的java api,以便开发人员可以更好地使用Tile38。

    语法分析

    首先,根据上面提供的语法,我们可以分为三部分:

    1.第一部分就是命令的启示关键字SET,我们把这个关键字单独作为一部分;

    2.第二部分就是key id [FIELD name value ...] [EX seconds] [NX|XX],我们把这些都作为参数;

    3.第三部分就是最后的目标数据对象:

    (OBJECT geojson)|(POINT lat lon z)|(BOUNDS minlat minlon maxlat maxlon)|(HASH geohash)|(STRING value)

    代码设计

    1.我们把第一部分的命令关键字通过枚举的方式来管理:

    enum Tile38Command implements ProtocolKeyword { SET; public final byte[] bytes; static final String UNDERSCORE = "_"; static final String SPACE = " "; Tile38Command() { String name = StringUtils.replace(this.name(), UNDERSCORE, SPACE); this.bytes = name.getBytes(StandardCharsets.US_ASCII); } @Override public byte[] getBytes() { return this.bytes; } }
    Copier après la connexion

    因为redis客户端工具在发送命令前需要对所有命令进行编码,所以要求所有的命令都必须实现ProtocolKeyword接口。如果命令的起始关键字是两个或多个单词,那么我们会使用下划线连接,转换成bytes的时候我们可以使用空格把下划线替换。

    2.我们把命令的第二部分抽象成一个具体的class,通过相关的字段来进行描述:

    public class SetOpts { private String key; private String id; //字段值必须是双精度浮点型 private Map fields; // 单位秒 private int ex; // 创建方式: // NX 不存在的时候创建 // XX 存在的时候更新 private NxXx nxXx; private SetOpts(Builder builder) { this.key = builder.key; this.id = builder.id; this.fields = builder.fields; this.ex = builder.ex; this.nxXx = builder.nxXx; } // 把所有的参数按顺序放到列表中 public List commandLine() { List result = new LinkedList<>(); result.add(this.key); result.add(this.id); // 添加所有的FIELD if (MapUtils.isNotEmpty(this.fields)) { for (Map.Entry entry : this.fields.entrySet()) { result.add("FIELD"); result.add(entry.getKey()); result.add(entry.getValue().toString()); } } // 添加`EX` if (this.ex >= 0) { result.add("EX"); result.add(String.valueOf(this.ex)); } // 添加NX或XX if (Objects.nonNull(this.nxXx)) { result.add(this.nxXx.name()); } // 返回结果 return result; } public enum NxXx { NX, XX } // 建造者模式 public static class Builder { private String key; private String id; //字段值必须是双精度浮点型 private Map fields; // 单位秒 private int ex = -1; // 创建方式: // NX 不存在的时候创建 // XX 存在的时候更新 private NxXx nxXx; public Builder key(String key) { this.key = key; return this; } public Builder id(String id) { this.id = id; return this; } public Builder field(String field, double value) { if (Objects.isNull(this.fields)) { this.fields = new LinkedHashMap<>(); } this.fields.put(field, value); return this; } public Builder ex(int seconds) { this.ex = seconds; return this; } public Builder nxXx(NxXx nxXx) { this.nxXx = nxXx; return this; } public SetOpts build() throws AwesomeException { if (StringUtils.isEmpty(this.key)) { throw new AwesomeException(500, "key is empty"); } if (StringUtils.isEmpty(this.id)) { throw new AwesomeException(500, "id is empty"); } // 创建SetOpts对象 return new SetOpts(this); } } }
    Copier après la connexion

    我们上面通过建造者的设计模式,把所有的参数都转换成了SetOpts这个类当中,开发人员就可以通过SetOpts对象的构建来灵活地控制命令中的参数了。

    3.我们需要把第三部分当中的不同数据对象转换成不同的类型:

    POINT数据类型

    Point关键的字段就是经纬度,除此之外,还有一个额外的字段z

    Analyse grammaticale

    Tout d'abord, selon la syntaxe fournie ci-dessus, on peut la diviser en trois parties :

    1 La première partie est le mot-clé de révélation de la commande SET, on prend. ce mot-clé en tant que partie distincte ;

    2. La deuxième partie est key id [FIELD name value ...] [EX seconds] [NX|XX], nous les utilisons comme paramètres ;

    3. La troisième partie C'est l'objet de données cible final :

    (OBJECT geojson)|(POINT lat lon z)|(BOUNDS minlat minlon maxlat maxlon)|(HASH geohash)|(STRING value)

    Conception du code

    1. Nous mettons Les mots-clés de commande dans la première partie sont gérés par énumération :

    public class Point extends Element implements Serializable { // 经度 private double lng; // 维度 private double lat; // 额外的数据 private double z; public Point(double lng, double lat, double z) { this.lat = lat; this.lng = lng; this.z = z; } public Point(double lng, double lat) { this(lng, lat, Integer.MIN_VALUE); } @Override public List commandArgs() { List result = new LinkedList<>(); result.add("POINT"); result.add(String.valueOf(this.lng)); result.add(String.valueOf(this.lat)); if (this.z != Integer.MIN_VALUE) { result.add(String.valueOf(this.z)); } return result; } }
    Copier après la connexion

    Parce que l'outil client Redis doit encoder toutes les commandes avant de les envoyer, toutes les commandes doivent implémenter l'interface ProtocolKeyword. Si le mot-clé de départ de la commande est composé de deux mots ou plus, nous utiliserons des traits de soulignement pour les connecter. Lors de la conversion en octets, nous pouvons utiliser des espaces pour remplacer les traits de soulignement.

    2. Nous résumons la deuxième partie de la commande dans une classe spécifique et la décrivons à travers les champs associés :

    @AllArgsConstructor public class Bounds extends Element { private double[] coordinate1; private double[] coordinate2; @Override public List commandArgs() { List result = new LinkedList<>(); result.add("BOUNDS"); result.add(String.valueOf(coordinate1[0])); result.add(String.valueOf(coordinate1[1])); result.add(String.valueOf(coordinate2[0])); result.add(String.valueOf(coordinate2[1])); return result; } }
    Copier après la connexion

    Nous avons utilisé le modèle de conception du constructeur pour convertir tous les paramètres dans la classe SetOpts, les développeurs peuvent contrôler les paramètres de manière flexible grâce à la construction d’objets SetOpts.

    3. Nous devons convertir les différents objets de données de la troisième partie en différents types : Type de données POINT Le champ clé de Point est la latitude et la longitude. De plus, il y a un champ supplémentaire z., utilisé pour stocker des paramètres métier supplémentaires, peut être vide.
    @AllArgsConstructor public class Geohash extends Element { private String hash; @Override public List commandArgs() { List result = new LinkedList<>(); result.add("HASH"); result.add(this.hash); return result; } } @AllArgsConstructor public class RawString extends Element { private String raw; @Override public List commandArgs() { List result = new LinkedList<>(); result.add("STRING"); result.add(this.raw); return result; } }
    Copier après la connexion
    Type de données BOUNDSBOUNDS est un rectangle, ses champs clés sont le coin inférieur gauche et le coin supérieur droit. Nous utilisons coordinate1 et coordinate2 pour représenter le coin inférieur gauche et le coin supérieur droit
    Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon
    Copier après la connexion
    Les types de données HASH et STRING ; HASH et STRING sont en fait une chaîne distincte, mais nous l'encapsulons toujours pour que les développeurs puissent l'utiliser ;
    @Data public class GeoJson { public static class Builder { public Point.Builder point() { return new Point.Builder(); } public MultiPoint.Builder multPoint() { return new MultiPoint.Builder(); } public LineString.Builder lineString() { return new LineString.Builder(); } public MultiLineString.Builder multiLineString() { return new MultiLineString.Builder(); } public Polygon.Builder polygon() { return new Polygon.Builder(); } public MultiPolygon.Builder multiPolygon() { return new MultiPolygon.Builder(); } } }
    Copier après la connexion
    Le type de données OBJECTOBJECT est en fait des données GeoJSON. Ce type de données est plus compliqué, et il y en a six au total. ceux qui veulent en savoir plus peuvent voir ici geojson.org/
    // Point类型 public static class Point extends BaseGeoJson { // 坐标点 private double[] coordinates; Point(Builder builder) { super(builder); this.type = GeoJsonType.Point; this.coordinates = builder.coordinates; } @Override protected Object coordinates() { return this.coordinates; } public static class Builder extends BaseGeoJson.Builder { private double[] coordinates; public Builder coordinate(double lon, double lat) { coordinates = new double[]{lat, lon}; return this; } public Point build() { return new Point(this); } } } // MultiPoint类型 public static class MultiPoint extends BaseGeoJson { private double[][] coordinates; MultiPoint(Builder builder) { super(builder); this.type = GeoJsonType.MultiPoint; this.coordinates = builder.convert2Array(); } @Override protected Object coordinates() { return this.coordinates; } public static class Builder extends BaseGeoJson.Builder { private List coordinates; public Builder coordinate(double lon, double lat) { if (CollectionUtils.isEmpty(this.coordinates)) { this.coordinates = new LinkedList<>(); } this.coordinates.add(new Coordinate(lat, lon)); return this; } protected double[][] convert2Array() { int length = this.coordinates.size(); double[][] result = new double[length][]; for (int i = 0; i < length; i++) { result[i] = this.coordinates.get(i).convertToArray(); } return result; } @Override public MultiPoint build() { return new MultiPoint(this); } } } // LineString类型 public static class LineString extends MultiPoint { private double[][] coordinates; LineString(Builder builder) { super(builder); this.type = GeoJsonType.LineString; } public static class Builder extends MultiPoint.Builder { @Override public LineString build() { return new LineString(this); } } } // MultiLineString类型 public static class MultiLineString extends BaseGeoJson { private double[][][] coordinates; MultiLineString(Builder builder) { super(builder); this.type = GeoJsonType.MultiLineString; this.coordinates = builder.convertToArray(); } @Override protected Object coordinates() { return this.coordinates; } public static class Builder extends BaseGeoJson.Builder { private List lines = new LinkedList<>(); public Line line() { return new Line(this); } void addLine(Line line) { lines.add(line); } double[][][] convertToArray() { int length = this.lines.size(); double[][][] result = new double[length][][]; for (int i = 0; i < length; i++) { Line line = this.lines.get(i); result[i] = line.convert2Array(); } return result; } @Override public BaseGeoJson build() { return new MultiLineString(this); } } static class Line { private List coordinates; private Builder builder; Line(Builder builder) { this.builder = builder; this.builder.addLine(this); } private double[][] convert2Array() { int length = this.coordinates.size(); double[][] result = new double[length][]; for (int i = 0; i < length; i++) { result[i] = this.coordinates.get(i).convertToArray(); } return result; } public Line coordinate(double lon, double lat) { if (CollectionUtils.isEmpty(this.coordinates)) { this.coordinates = new LinkedList<>(); } this.coordinates.add(new Coordinate(lat, lon)); return this; } public Line nextLine() { return new Line(this.builder); } public Builder end() { return this.builder; } } } // Polygon类型 public static class Polygon extends MultiPoint { private double[][][] coordinates; Polygon(Builder builder) { super(builder); this.type = GeoJsonType.Polygon; this.coordinates = new double[][][]{builder.convert2Array()}; } public static class Builder extends MultiPoint.Builder { @Override public Polygon build() { return new Polygon(this); } } } // MultiPolygon类型 public static class MultiPolygon extends BaseGeoJson { private double[][][][] coordinates; MultiPolygon(Builder builder) { super(builder); this.type = GeoJsonType.MultiPolygon; this.coordinates = new double[][][][]{builder.convert2Array()}; } @Override protected Object coordinates() { return this.coordinates; } public static class Builder extends BaseGeoJson.Builder { private List polygons = new LinkedList<>(); @Override public BaseGeoJson build() { return new MultiPolygon(this); } void addPolygon(Polygon polygon) { polygons.add(polygon); } private double[][][] convert2Array() { int length = this.polygons.size(); double[][][] result = new double[length][][]; for (int i = 0; i < length; i++) { result[i] = this.polygons.get(i).convert2Array(); } return result; } } static class Polygon { private List coordinates; private Builder builder; Polygon(Builder builder) { this.builder = builder; this.builder.addPolygon(this); } private double[][] convert2Array() { int length = this.coordinates.size(); double[][] result = new double[length][]; for (int i = 0; i < length; i++) { result[i] = this.coordinates.get(i).convertToArray(); } return result; } public Polygon coordinate(double lon, double lat) { if (CollectionUtils.isEmpty(this.coordinates)) { this.coordinates = new LinkedList<>(); } this.coordinates.add(new Coordinate(lat, lon)); return this; } public Polygon nextLine() { return new Polygon(this.builder); } public Builder end() { return this.builder; } } } // 基类BaseGeoJson public abstract static class BaseGeoJson extends Element { // 公共字段type protected GeoJsonType type; // 公共字段metadata private Map metadata; BaseGeoJson(Builder builder) { this.metadata = builder.metadata; } protected abstract Object coordinates(); // 转换成命令参数 @Override public List commandArgs() { List result = new LinkedList<>(); result.add("OBJECT"); result.add(toJson()); return result; } // 提供统一的转json方法 protected String toJson() { Map map = new LinkedHashMap<>(); map.put("type", this.type); map.put("coordinates", coordinates()); if (!CollectionUtils.isEmpty(this.metadata)) { for (Map.Entry entry : this.metadata.entrySet()) { map.put(entry.getKey(), entry.getValue()); } } return JsonUtil.obj2String(map); } abstract static class Builder { private Map metadata; public Builder meta(String key, String value) { if (MapUtils.isEmpty(this.metadata)) { this.metadata = new LinkedHashMap<>(); } this.metadata.put(key, value); return this; } public abstract BaseGeoJson build(); } static class Coordinate { private double lat; private double lon; Coordinate(double lat, double lon) { this.lat = lat; this.lon = lon; } public double[] convertToArray() { return new double[]{this.lat, this.lon}; } } // GeoJSON所有的数据类型 enum GeoJsonType { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } }
    Copier après la connexion
    Afin que les développeurs puissent mieux utiliser ces six types, nous utilisons également le modèle de générateur pour concevoir le type de données GeoJSON :
    public abstract class Element implements Serializable { public abstract List commandArgs(); }
    Copier après la connexion
    Nous sommes maintenant un créateur de méthodes multiples dans une grande classe, et chaque méthode crée un constructeur du type correspondant. Dans ce cas, cela équivaut à avoir six façons de créer des objets dans cette classe, et chaque constructeur est uniquement responsable de la construction de l'objet correspondant. Voici les codes des six constructeurs. Chaque objet est construit sur la base du BaseGeoJson le plus basique. Le type de champ public et les champs méta supplémentaires sont extraits de BaseGeoJson. La différence entre chaque type réside dans le nombre et le nombre de points de coordonnées. . Les niveaux sont différents, donc selon les caractéristiques de chaque type, la conception du code est la suivante :
    private String setElement(SetOpts setOpts, Element element) { List args1 = setOpts.commandLine(); List commandArgs = element.commandArgs(); return execute(Tile38Command.SET, args1, commandArgs); } /** * 设置点位 * * @param setOpts * @param point * @return */ public String setPoint(SetOpts setOpts, Point point) { return setElement(setOpts, point); } /** * 设置对象 * * @param setOpts * @param geoJson * @return */ public String setObject(SetOpts setOpts, GeoJson.BaseGeoJson geoJson) { return setElement(setOpts, geoJson); } /** * 设置矩形边界 * * @param setOpts * @param bounds * @return */ public String setBounds(SetOpts setOpts, Bounds bounds) { return setElement(setOpts, bounds); } /** * 设置geohash * * @param setOpts * @param geohash * @return */ public String setGeohash(SetOpts setOpts, Geohash geohash) { return setElement(setOpts, geohash); } /** * 设置String * * @param setOpts * @param string * @return */ public String setString(SetOpts setOpts, RawString string) { return setElement(setOpts, string); }
    Copier après la connexion
    Enfin, ajoutez un élément de classe de base : rrreeeComment utiliserNous convertissons tous les types de données en conceptions de code spécifiques. , voyons ci-dessous. Voir comment utiliser : rrreee

    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

    Étiquettes associées:
    source:yisu.com
    Déclaration de ce site Web
    Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
    Derniers téléchargements
    Plus>
    effets Web
    Code source du site Web
    Matériel du site Web
    Modèle frontal
    À propos de nous Clause de non-responsabilité Sitemap
    Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!