【问题标题】:How can I represent key with list of values for each environment type in an Enum?如何用枚举中每种环境类型的值列表表示键?
【发布时间】:2018-06-05 23:18:21
【问题描述】:

我有两个环境PRODSTAGING。在生产环境中,我们有三个数据中心ABCDEFPQR,并且暂存有一个数据中心CORP。每个数据中心都有几台机器,我为它们定义了常量,如下所示:

// NOTE: I can have more machines in each dc in future
public static final ImmutableList<String> ABC_SERVERS = ImmutableList.of("tcp://machineA:8081", "tcp://machineA:8082");
public static final ImmutableList<String> DEF_SERVERS = ImmutableList.of("tcp://machineB:8081", "tcp://machineB:8082");
public static final ImmutableList<String> PQR_SERVERS = ImmutableList.of("tcp://machineC:8081", "tcp://machineC:8082");

public static final ImmutableList<String> STAGING_SERVERS = ImmutableList.of("tcp://machineJ:8087","tcp://machineJ:8088");

现在我在同一个类中定义了另一个常量,它按 DC 分组到每种环境类型的机器列表。

public static final ImmutableMap<Datacenter, ImmutableList<String>> PROD_SERVERS_BY_DC =
  ImmutableMap.<Datacenter, ImmutableList<String>>builder()
      .put(Datacenter.ABC, ABC_SERVERS).put(Datacenter.DEF, DEF_SERVERS)
      .put(Datacenter.PQR, PQR_SERVERS).build();

public static final ImmutableMap<Datacenter, ImmutableList<String>> STAGING_SERVERS_BY_DC =
  ImmutableMap.<Datacenter, ImmutableList<String>>builder()
      .put(Datacenter.CORP, STAGING_SERVERS).build();

现在在其他班级,根据我在(Utils.isProd()) 的环境,我得到PROD_SERVERS_BY_DCSTAGING_SERVERS_BY_DC

Map<Datacenter, ImmutableList<String>> machinesByDC = Utils.isProd() ? Utils.PROD_SERVERS_BY_DC : Utils.STAGING_SERVERS_BY_DC;

现在我认为这可以用某种 Enum 以更好的方式表示,而不是像上面那样定义常量,但我无法弄清楚我该怎么做?我从这个开始,但对如何为每个 DC 拥有单个键,然后将多个值作为该 DC 的机器列表,然后我还需要按环境对它们进行分组感到困惑。

// got confuse on how can I make key (DC) and list of values (machines) for each environment type.
public enum DCToMachines {
  abc(tcp://machineA:8081", "tcp://machineA:8082"), 

  private final List<String> machines;
  private final Datacenter datacenter;
  ...


}

【问题讨论】:

    标签: java oop design-patterns data-structures enums


    【解决方案1】:

    我从这个开始,但对如何拥有单键感到困惑 对于每个 DC,然后将多个值作为该 DC 的机器列表 然后我还需要按环境对它们进行分组。

    您忘记了一个重要的问题:枚举可以存储特定于每个枚举值的成员(方法和字段),但如果需要,它也可以存储 static 成员。
    您需要的是直接在枚举类中移动按环境分组的地图:

    private static final ImmutableMap<Datacenter, ImmutableList<String>> PROD_SERVERS_BY_DC;
    private static final ImmutableMap<Datacenter, ImmutableList<String>> STAGING_SERVERS_BY_DC;
    

    您可以使用内部地图根据用户请求返回预期的地图:

    private static final Map<Env, ImmutableMap<Datacenter, ImmutableList<String>>> SERVERS_BY_ENV;
    

    Env 是另一个枚举:

    public static enum Env {
        PROD, STAGING
    }
    

    在我将展示的代码中,我添加了 Datacenter 的封闭类型,但在它自己的文件中可能会更好,因为环境可能不是数据中心专用的概念。

    最后,您通过环境检索服务器的方式将相关职责分为 2 个类(Datacenter 枚举和当前类):

    Map<Datacenter, ImmutableList<String>> machinesByDC = Utils.isProd() ? Utils.PROD_SERVERS_BY_DC : Utils.STAGING_SERVERS_BY_DC;
    

    在枚举中引入执行此操作的方法会更好:

    public static ImmutableMap<Datacenter, ImmutableList<String>> getServers(Env env){...}
    

    这会增加枚举类的凝聚力。

    这是更新的枚举:

    import java.util.HashMap;
    import java.util.Map;
    
    import com.google.common.collect.ImmutableList;
    import com.google.common.collect.ImmutableMap;
    import com.google.common.collect.ImmutableMap.Builder;
    
    public enum Datacenter {
    
       ABC(Env.PROD, "tcp://machineA:8081", "tcp://machineA:8082"), 
       DEF(Env.PROD, "tcp://machineB:8081", "tcp://machineB:8082"), 
       PQR(Env.PROD, "tcp://machineA:8081", "tcp://machineA:8082"), 
       CORP(Env.STAGING, "tcp://machineC:8081", "tcp://machineC:8082");
    
        public static enum Env {
            PROD, STAGING
        }
    
        private Env env;
        private String[] url;
    
        private static final ImmutableMap<Datacenter, ImmutableList<String>> PROD_SERVERS_BY_DC;
        private static final ImmutableMap<Datacenter, ImmutableList<String>> STAGING_SERVERS_BY_DC;
        private static final Map<Env, ImmutableMap<Datacenter, ImmutableList<String>>> SERVERS_BY_ENV;
    
        static {
    
            Builder<Datacenter, ImmutableList<String>> prodDataCenterBuilder = ImmutableMap.<Datacenter, ImmutableList<String>>builder();
            Builder<Datacenter, ImmutableList<String>> stagingDataCenterBuilder = ImmutableMap.<Datacenter, ImmutableList<String>>builder();
    
            for (Datacenter datacenter : Datacenter.values()) {
                if (datacenter.env == Env.PROD) {
                    prodDataCenterBuilder.put(datacenter, ImmutableList.of(datacenter.url));
                } else if (datacenter.env == Env.STAGING) {
                    stagingDataCenterBuilder.put(datacenter, ImmutableList.of(datacenter.url));
                }
    
                else {
                    throw new IllegalArgumentException("not exepected env " + datacenter.env);
                }
            }
    
            PROD_SERVERS_BY_DC = prodDataCenterBuilder.build();
            STAGING_SERVERS_BY_DC = stagingDataCenterBuilder.build();
    
            SERVERS_BY_ENV = new HashMap<>();
            SERVERS_BY_ENV.put(Env.PROD, PROD_SERVERS_BY_DC);
            SERVERS_BY_ENV.put(Env.STAGING, STAGING_SERVERS_BY_DC);
    
        }
    
        Datacenter(Env env, String... url) {
            this.env = env;
            this.url = url;
        }    
    
        public static ImmutableMap<Datacenter, ImmutableList<String>> getServers(Env env) {
            ImmutableMap<Datacenter, ImmutableList<String>> map = SERVERS_BY_ENV.get(env);
            if (map == null) {
                throw new IllegalArgumentException("not exepected env " + env);
            }
            return map;
        }
    
    }
    

    以及如何使用它:

    public static void main(String[] args) {        
        ImmutableMap<Datacenter, ImmutableList<String>> servers = Datacenter.getServers(Env.PROD);
        servers.forEach((k, v) -> System.out.println("prod datacenter=" + k + ", urls=" + v));
    
        servers = Datacenter.getServers(Env.STAGING);
        servers.forEach((k, v) -> System.out.println("staging datacenter=" + k + ", urls=" + v));
    }
    

    输出

    prod datacenter=ABC, urls=[tcp://machineA:8081, tcp://machineA:8082]

    prod datacenter=DEF, urls=[tcp://machineB:8081, tcp://machineB:8082]

    prod datacenter=PQR, urls=[tcp://machineA:8081, tcp://machineA:8082]

    staging datacenter=CORP, urls=[tcp://machineC:8081, tcp://machineC:8082]

    【讨论】:

    • 谢谢@davidxxx。现在说得通了。我还有一个与设计模式here 相关的问题。想看看你能不能帮帮我?
    【解决方案2】:

    虽然在不了解完整实现的情况下这有点棘手,但我也许可以帮助您入门。

    枚举的有趣之处在于它们是不可变的,它解决了声明常量字段和变量的问题。这是我尝试解决您正在寻找的问题。

    public enum Server {
        // Constant style naming because Enums are Immutable objects
        ABC_SERVERS("tcp://machineA:8081", "tcp://machineA:8082"),
        DEF_SERVERS("tcp://machineB:8081", "tcp://machineB:8082"),
        PQR_SERVERS("tcp://machineC:8081", "tcp://machineC:8082"),
        CORP("tcp://machineZ:8081");
    
        /* After the name declaration you can essentially do normal class 
           development
        */ Fields
        private List<String> servers = new ArrayList<>();
        public boolean isProd; // you could use something as simple as a 
        //boolean to determine the environment
    
        // Enums use a private constructor because they are never 
        //instantiated outside of creation of this Enum class
        private Server(String... dataCenter){
    
        // because of the varargs parameter there is a potential for 
        //multiple Strings to be passed
            for (String tcp :
                    dataCenter) {
                this.servers.add(tcp);
            }
    
            // You can access the name property of the Enum that is being created
            if (this.name() == "CORP")
                this.isProd = false;
            else
                this.isProd = true;
    
        }
    
        // You could make the List public but this demonstrates some encapsulation
        public List<String> getServers() {
            return servers;
        }
    }
    

    然后我检查了每个“机器”是否被添加到另一个类中

        // these are what I used to check to make sure that each string is being "mapped" correctly
        List<String> prodServer = new ArrayList<>();
        List<String> stagingServer = new ArrayList<>();
    
        for (Server server :
                Server.values()) {
            if (server.isProd) {
                for (String machine :
                        server.getServers()) {
                    prodServer.add(machine);
                }
            }
            else {
                for (String machine:
                        server.getServers()) {
                    stagingServer.add(machine);
                }
            }
        }
    
        System.out.println(prodServer);
    
        System.out.println(stagingServer);
    

    另请注意,要获取实际的枚举名称,请使用 Enum 类的 .values() 函数。

    【讨论】:

      猜你喜欢
      • 2011-04-15
      • 1970-01-01
      • 2023-04-04
      • 2012-03-19
      • 1970-01-01
      • 2016-08-15
      • 1970-01-01
      • 2022-06-11
      相关资源
      最近更新 更多