如何使用键建立一个Java工厂
我想构建一个命令解析器,它接收一个数据块并将该数据解析为特定命令的一个实例。这基本上是一个工厂,工厂返回的实例基于一个密钥。如何使用键建立一个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
以上是 如何使用键建立一个Java工厂 的全部内容, 来源链接: utcz.com/qa/261932.html