替换硬编码的类实现 [英] Replace hardcoded class implementations

查看:43
本文介绍了替换硬编码的类实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在处理一个新项目时,遇到了以下熟悉的案例.

While working on a new project a came across with the following familiar case.

private static Hashtable<String, Class<? extends MyExample>> MyExampleClassCollection = new Hashtable<String, Class<? extends MyExample>>();

static {
    MyExampleClassCollection.put("example1", MyExampleImplementation1.class);
    MyExampleClassCollection.put("example2", MyExampleImplementation2.class);
    MyExampleClassCollection.put("example3", MyExampleImplementation3.class);
}

public static MyExample getMyExample(String myExampleType){

    Class<? extends MyExample> templateClass = MyExampleClassCollection.get(myExampleType);

    try {
        Constructor ctor = templateClass.getConstructor(Connection.class);
        return (MyExample)ctor.newInstance();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

在这种情况下,您试图创建更通用的代码,但最终对通用类的不同实现进行了硬编码.不利的一面是,您必须为每个新实现在 Hashtable 上创建一个新条目.

It's the case where you are trying to create a more generic code and you end up hardcode the different implementations of your generic class. Having the down side that you have to create a new entry on the Hashtable for every new implementation.

我在之前参与的一个项目中看到的一个解决方案是将具体类的几个名称存储在数据库中.有一个字段,其中包含将要使用的具体类的名称.并使用反射再次创建它,例如 MyExampleImplementation1.获得你不使用哈希表.这个解决方案是相同的,唯一的区别是我从数据库中检索了 myExampleType.

A solution I saw on a previous project I worked on, was to store the several names of the concrete classes in the DB. Having a field with the name of the concrete class that is going to be used. And create it again with reflection, for example MyExampleImplementation1. Gain that you don't use a HashTable. This solution is identical, the only difference is I retrieve the myExampleType from the DB.

有没有更优雅的方式?我更喜欢使用配置文件.我以为我可以使用依赖注入(我对此还不是很有经验),但这是一个很好的例子吗?

Is there a more elegant way? I would prefer to use a configuration file. I thought I could use dependency injection (I'm not very experienced with this yet), but is this a good case for that?

有没有办法将此功能与我的应用程序服务器集成?

Is there a way to integrate this functionality with my application server ?

推荐答案

总结: 您无需自己枚举和存储应用程序所需的组件.容器会为你做这件事;您需要做的就是在需要组件时查找它.

Summary : You don't need to enumerate and store components that are required across an application, by yourself. The container will do that for you; all you need to do is look it up whenever you need the components.

应该在这里做.您将得到的是 Service Locator 模式的一种变体.此解决方案假定您不打算重写大量代码或 getMyExample 的任何客户端(如果是,则有更好的方法来实现).

cdi or jndi ought to do here. What you're going to wind up with is a variation of the Service Locator pattern. This solution presumes you're not looking to rewrite a lot of code or any of the clients of getMyExample (if you are, there are better ways to do it).

这里的 JNDI 方法是快速、严肃的,但也 (IMO) 不优雅和丑陋.很简单:

A JNDI approach here is quick, no-nonsense, but also (IMO) inelegant and ugly. It's simple:

  1. 在您的 web.xml 中为每个类声明一个条目(如果您在 Web 应用程序中)

  1. Declare an entry per class in your web.xml (if you're in a web application)

<env-entry>
   <env-entry-name>example1</env-entry-name>
   <env-entry-type>java.lang.Class</env-entry-type>
   <env-entry-value>my.example.MyExampleImplementation1</env-entry-value>
</env-entry>

  • 需要时随时查看

  • Look it up whenever you need it

    这里至少有 2 个选项 - 使用 @Resource 注释或使用老式的 InitialContextSessionContext JNDI 查找(@Resource>SessionContext 更快).由于您在这里的目标是避免硬编码,因此上下文查找是要走的路:

    You have at least 2 options here - using the @Resource annotation or using old-fashioned InitialContext or SessionContext JNDI lookups (SessionContext is faster). Since your objective here is to avoid hardcoding, the context lookup is the way to go:

    @Resource
    private SessionContext sessionContext;
    
    public static MyExample getMyExample(String myExampleType){
    
        Class<? extends MyExample> theClass =  (Class<? extends MyExample>) sessionContext.lookup(myExampleType);
        //instantiate
    }
    

  • 这是纯粹的配置,但同样,不是很优雅.此外,从 JavaEE 6 开始支持 类.

    It's pure configuration, but again, not very elegant. Also, support for classes as <env-entry> started with JavaEE 6.

    CDI 的众多好处之一是它默认为 Java EE 空间中的许多人工制品提供的 bean.让我们从这里开始

    One of the many benefits of CDI is the beanification that it provides by default to many artefacts in the Java EE space. Let's start here

    • 您不想对需要实例化的类型进行硬编码
    • 您正在努力编写更简洁的代码

    以下 CDI 结构的组合应该能让您找到答案:

    A combination of the following CDI constructs should sort you out:

    • CDI Bean 管理器
    • 命名

    首先,创建一个beans.xml文件在您的 WEB-INF 或 META-INF 文件夹中.这是在您的 Web 应用程序中启用 CDI 的先决条件.

    To start, create a beans.xml file in your WEB-INF or META-INF folders. This is a prerequisite to enabling CDI in your web application.

    命名:

    在上面的 Web 应用程序中启用 CDI 后,您的应用程序中几乎所有的类都可供 CDI 引擎检查,即根据某些资格规则,它们变成 bean.您现在希望能够通过您作为类的作者指定的名称来引用您的实现类.在 bean 上使用 CDI @Named.

    After enabling CDI in your web application above, almost all your classes in your application are available to the CDI engine to inspect, i.e. they become beans, based on some eligibility rules. You now want to be able to refer to your implementation classes by names that you as the author of the class specify. Use the CDI @Named on the beans.

    @Named("example1")
    public class MyExampleImplementation1
    

    在应用程序的任何部分,您现在都可以通过该名称引用该类.

    In any part of your application, you can now refer to that class, by that name.

    BeanManager

    BeanManager 是 CDI 上下文中 bean 的霸主 - 它是您进入 CDI 引擎的所有 bean 和其他内部工作的网关.您将使用它来按名称检索所需类的实例

    The BeanManager is the overlord of the beans in the CDI context - it's your gateway into the all the beans and other inner workings of the CDI engine. You'll use this to retrieve an instance of the desired class by name

    @Inject BeanManager manager; //inject the bean manager into your class
    
    
    public static MyExample getMyExample(String myExampleType){
        Set<Bean<?>> beans = manager.getBeans("example1"); //retrieve the bean by name. This returns a set, but you're ideally interested in only one bean
        Bean bean = beans.iterator().next(); //get the first (and ideally the only bean in the set)
        CreationalContext ctx = manager.createCreationalContext(bean); //CDI stipulation: there has to be a context associated with everything.
        MyExample theExample = bean.create(ctx); //create an instance of the bean and return
    
        return theExample;
    
    }
    

    使用这种方法,您无需在每次添加对新 MyExample 实现的支持时继续更新配置文件

    With this approach, there's no need to continue updating a configuration file everytime you add support for a new MyExample implementation

    提示:缓存创建的对象以减少两种方法中的查找和实例化开销

    Tip : Cache the objects that are created to reduce lookup and instantiation overhead in both approaches

    这篇关于替换硬编码的类实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆