• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

如何使用键建立一个Java工厂

java 来源:MadMan 6次浏览

我想构建一个命令解析器,它接收一个数据块并将该数据解析为特定命令的一个实例。这基本上是一个工厂,工厂返回的实例基于一个密钥。如何使用键建立一个Java工厂

所以更具体,说收到原始二进制数据块:

0×02 0×23 0×01 0×00 0×00 0x1F的

,以及该流的第三个字节定义的命令,我想创建的实例CommandOne。显然,根据命令将会有额外的方法来处理解析其余数据,但第一步是从命令编号中获取该命令的实例。我说这是一个使用密钥的工厂。

关于如何在java中建立工厂,有些非常直接,有些使用各种泛型和反射来讨论;但是我没有找到符合我想要实现的特定实现的运气。然而,我确实发现了与我正在努力完成的事情有关的点点滴滴,所以我将它们放在一个答案中,这是下面的第一个答案。这是一个很好的回应,还是我忽略了更简单的或更完整的东西?


===========解决方案如下:

以下是该解决方案的通用形式。这全部内置于包含所有命令和工厂的单个包中。请注意,添加新命令不需要对工厂本身进行任何更改。

这是所有命令必须实现的抽象类。它还包含工厂方法getInstance

public abstract class Commands 
{ 
    private static Map<Integer,Constructor<?>> commandConstructorMap = null; 

    private static void loadMap() 
    { 
    commandConstructorMap = new HashMap<Integer,Constructor<?>>(); 
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
    if(classLoader == null) return; 
    String myPathName = Commands.class.getName(); 
    String myPath[] = myPathName.split("\\."); 
    if(myPath == null || myPath.length <= 1) return; 
    int pkgLen = myPath.length - 1; 
    StringBuilder pkgNm = new StringBuilder(); 
    pkgNm.append(myPath[0]); 
    for(int i=1;i<pkgLen;i++) 
     pkgNm.append(".").append(myPath[i]); 
    String packageName = pkgNm.toString(); 
    String path = packageName.replace('.', '/'); 
    Enumeration<URL> resources; 
    try 
    { 
     resources = classLoader.getResources(path); 
    } 
    catch(IOException ioe) 
    { 
     return; // failure, just leave constructor empty 
    } 
    List<File> dirs = new ArrayList<File>(); 
    while(resources.hasMoreElements()) 
     dirs.add(new File(resources.nextElement().getFile())); 
    for(File dir:dirs) 
    { 
     if(!dir.exists() || !dir.isDirectory()) continue; 
     File[] files = dir.listFiles(); 
     for(File file:files) 
     { 
     if(!file.isFile()) continue; 
     String fileName = file.getName(); 
     if(!fileName.endsWith(".class")) continue; 
     try 
     { 
      String className = packageName+'.'+fileName.substring(0, fileName.length()-6); 
      Class<?> clazz = Class.forName(className); 
      Constructor<?> cons = clazz.getConstructor(); 
      Object instance = cons.newInstance(); 
      if(instance instanceof Commands) 
      { 
      commandConstructorMap.put(new Integer(((Commands) instance).getCommand()),cons); 
      } 
     } 
     catch(Exception e){} // do nothing special for exception, just don't add to map 
     }   
    } 
    } 


    public static Commands getInstance(Integer command) throws Exception 
    { 
    if(commandConstructorMap == null) 
    { 
     loadMap(); 
    } 
    if(commandConstructorMap.containsKey(command)) 
     return (Commands)commandConstructorMap.get(cmd).newInstance(); 
    throw new Exception(); 
    } 

    abstract Integer getCommand(); 
} 

注抽象getCommand的返回类型是相同的用作密钥的类型,这是关系conrtol和几乎可以是任何所需的对象类型。这种机制的一个警告是,这确实会创建并最终丢弃每个命令对象的一个​​实例,但它只会执行一次,未来的调用将只创建由该密钥指示的类的单个实例。

这是一个命令文件实现的例子,显然需要其他代码来构建该命令的所有细节,这里的目标只是演示如何构建命令类的一部分这是在工厂中使用的。

public class CommandOne 
    extends Commands 
{ 
    Integer getCommand() 
    { 
    return new Integer(1); 
    } 
} 

最后一个是如何工作的

public static void main(String[] args) 
{ 
    try 
    { 
    Commands cmd = Commands.getInstance(new Integer(1)); 
    if(cmd instanceof CommandOne) 
     System.out.println("Command using key 1 is: "+cmd.getClass().getSimpleName()); 
    cmd = Commands.getInstance(new Integer(2)); 
    if(cmd instanceof CommandTwo) 
     System.out.println("Command using key 2 is: "+cmd.getClass().getSimpleName()); 
    cmd = Commands.getInstance(new Integer(3)); 
    if(cmd instanceof CommandThree) 
     System.out.println("Command using key 3 is: "+cmd.getClass().getSimpleName()); 
    } 
    catch(Exception e) 
    { 
    System.out.println("Command instantiation failed"); 
    } 
} 

运行(假设有定义的命令CommandTwo和CommandThree以及所表现出的CommandOne)会是这样的结果证明:

命令使用密钥1是:CommandOne使用密钥2是

命令:使用密钥3 CommandTwo

命令是:CommandThree


版权声明:本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。
喜欢 (0)