我正在尝试构建一个工厂对象,但是在使用Java编写一个很好的方法时遇到了麻烦.
我正在编写的应用程序用于处理各种格式的文件,因此有一个CodecInterface适用于所有用于读写文件的类.我们假设它定义了以下方法.这些文件中的每一个都具有唯一的人指定ID字符串,用于id'编码器\解码器.
String read();
void write(String data);
String getID();
Run Code Online (Sandbox Code Playgroud)
工厂类将有一个create方法,用于创建这些编解码器类的实例.我想方法签名看起来像这样.
static CodecInterface CodecFactory.create(String filename, String codecid, String args);
Run Code Online (Sandbox Code Playgroud)
filename是要读/写的文件的名称,codecid是指示要使用的编解码器的唯一ID.args参数是传递给正在生成的解码器/编码器对象的参数字符串.返回的应该是所请求的编解码器对象的实例.
我见过的所有Factory示例通常在create方法中都有一个switch语句,它创建一个依赖于ID的对象实例.我想避免这样做,因为它似乎不是'正确'的方式,它也意味着除非你修改create方法,否则列表或多或少是固定的.理想情况下,我想使用像字典(由编解码器ID索引)的东西,其中包含可用于创建我想要的编解码器类的实例的东西(我将称之为神秘类ClassReference).再次使用一些准java代码,这是我正在考虑的创建方法的主体.
static Dictionary<String, ClassReference>;
static CodecInterface CodecFactory.create(String filename, String codecid, String args);
{
ClassReference classreference;
classreference = codeclibrary(codecid);
return classreference.instanceOf(args);
}
Run Code Online (Sandbox Code Playgroud)
ID的字典很容易,但我无法弄清楚ClassReference应该是什么.类引用应该允许我创建所需类的实例,如上例所示.
从在线浏览,类方法和instanceOf似乎朝着正确的方向前进,但我没有发现任何将两者结合在一起的东西.作为一个额外的复杂性,正在创建的对象的构造函数将具有参数.
任何关于我应该看的内容的提示都将不胜感激.
提前致谢.
解
谢谢大家的意见.我最终从你的所有建议中得到了点点滴滴,并提出了以下似乎按我的意愿工作的内容.
请注意,我已经省略了很多sanity\error检查代码以显示重要位.
import java.lang.reflect.Constructor;
import java.util.HashMap;
public class CodecFactory
{
private static HashMap<String, Class<? extends CodecInterface>> codecs;
static
{
codecs = new HashMap<String, Class<? extends CodecInterface>>();
//Register built-in codecs here
register("codecA", CodecA.class);
register("codecB", CodecB.class);
register("codecC", CodecC.class);
}
public static void register(String id, Class<? extends CodecInterface> codec)
{
Class<? extends CodecInterface> existing;
existing = codecs.get(id);
if(existing == null)
{
codecs.put(id, codec);
}
else
{
//Duplicate ID error handling
}
}
public static CodecInterface create(String codecid, String filename, String mode, String arguments)
{
Class<? extends CodecInterface> codecclass;
CodecInterface codec;
Constructor constructor;
codec = null;
codecclass = codecs.get(codecid);
if(codecclass != null)
{
try
{
constructor = codecclass.getDeclaredConstructor(String.class, String.class, String.class, String.class);
codec = (CodecInterface)(constructor.newInstance(codecid, filename, mode, arguments));
}
catch(Exception e)
{
//Error handling for constructor/instantiation
}
}
return codec;
}
}
Run Code Online (Sandbox Code Playgroud)
Jas*_*n C 10
有很多选择.例如,您可以创建一个基础工厂类,该类也具有静态方法来管理已注册的工厂(此处输入未测试的代码,抱歉错误):
public abstract class CodecFactory {
private final String name;
public CodecFactory (String name) {
this.name = name;
}
public final String getName () {
return name;
}
// Subclasses must implement this.
public abstract Codec newInstance (String filename, String args);
// --- Static factory stuff ---
private static final Map<String,CodecFactory> factories = new HashMap<String,CodecFactory>();
public static void registerFactory (CodecFactory f) {
factories.put(f.getName(), f);
}
public static Codec newInstance (String filename, String codec, String args) {
CodecFactory factory = factories.get(codec);
if (factory != null)
return factory.newInstance(filename, args);
else
throw new IllegalArgumentException("No such codec.");
}
}
Run Code Online (Sandbox Code Playgroud)
然后:
public class QuantumCodecFactory extends CodecFactory {
public QuantumCodecFactory {
super("quantum");
}
@Override public Codec newInstance (String filename, String args) {
return new QuantumCodec(filename, args);
}
}
Run Code Online (Sandbox Code Playgroud)
当然,这意味着您必须:
CodecFactory.registerFactory(new QuantumCodecFactory());
Run Code Online (Sandbox Code Playgroud)
那么用法是:
Codec codec = CodecFactory.newInstance(filename, "quantum", args);
Run Code Online (Sandbox Code Playgroud)
另一种选择是使用反射并维护a Map<String,Class<? extends CodecInterface>>,Class.newInstance()用于实例化.这很方便实现,因为它在Java之上工作,Java Class已经支持用于实例化对象的工厂样式模型.注意事项,如上所述必须显式注册类,并且(不像上面)你不能在编译时隐式强制执行构造函数参数类型(尽管你至少可以在某些方法后面抽象而不是Class.newInstance()直接从客户端代码调用).
例如:
public final class CodecFactory {
private static final Map<String,Class<? extends Codec>> classes = new HashMap<String,Class<? extends Codec>>();
public static void registerClass (String name, Class<? extends Codec> clz) {
classes.put(name, clz);
}
public static Codec newInstance (String filename, String codec, String args) {
Class<? extends Codec> clz = classes.get(codec);
if (clz != null)
return clz.getDeclaredConstructor(String.class, String.class).newInstance(filename, args);
else
throw new IllegalArgumentException("No such codec.");
}
}
Run Code Online (Sandbox Code Playgroud)
Codec预计每个人都会有一个构造函数(String filename, String args).然后,注册是:
CodecFactory.registerClass("quantum", QuantumCodec.class);
Run Code Online (Sandbox Code Playgroud)
用法与上述相同:
Codec codec = CodecFactory.newInstance(filename, "quantum", args);
Run Code Online (Sandbox Code Playgroud)
您甚至可以省略地图并使用Class.forName()- 这并没有给您提供编解码器名称的灵活性,但它本质上允许类加载器为您完成所有工作,并且您不需要在前面显式注册类型时间
编辑:Re:下面的评论中的问题.您可以想出一个系统,它结合上面两个示例来创建一个可重用的,基于反射的通用工厂派生CodecFactory,这仍然使您能够创建其他更专业的工厂,例如:
public class GenericCodecFactory extends CodecFactory {
private final String name;
private final Class<? extends Codec> clz;
public GenericCodecFactory (String name, String clzname) {
this.name = name;
this.clz = Class.forName(clzname);
}
public GenericCodecFactory (String name, Class<? extends Codec> clz) {
this.name = name;
this.clz = clz;
}
// parameter type checking provided via calls to this method, reflection
// is abstracted behind it.
@Override public Codec newInstance (String filename, String args) {
return clz.getDeclaredConstructor(String.class, String.class).newInstance(filename, args);
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以用它来做任何事情:
// you can use specialized factories
ClassFactory.registerFactory(new QuantumCodecFactory());
// you can use the generic factory that requires a class at compile-time
ClassFactory.registerFactory(new GenericCodecFactory("awesome", AwesomeCodec.class));
// you can use the generic factory that doesn't need to have class present at compile-time
ClassFactory.registerFactory(new GenericCodecFactory("ninja", "com.mystuff.codecs.NinjaCodec"));
Run Code Online (Sandbox Code Playgroud)
如您所见,有很多可能性.使用Class.forName()基于反射的工厂是好的,因为该类并不需要出现在编译时; 所以你可以在类路径上放入编解码器类,比如在运行时配置文件中指定一个类名列表(然后你可以有静态ClassFactory.registerFactoriesListedInFile(String confgFilename)或其他东西),或扫描一个"插件"目录.你甚至可以用更简单的字符串构造类名,如果你对它感到满意,例如:
public class GenericPackageCodecFactory extends GenericCodecFactory {
public GenericPackageCodecFactory (String name) {
super(name, "com.mystuff." + name + ".Codec");
}
}
Run Code Online (Sandbox Code Playgroud)
如果找不到ClassFactory编解码器名称,您甚至可以使用类似的东西作为后备,以避免必须显式注册类型.
顺便说一句,反射不断涌现的原因在于它非常灵活,Class界面本质上是一个无所不包的类工厂,因此它经常与特定工厂架构试图完成的工作相媲美.
另一种选择是使用我上面提到的第二个例子(带有Map<String,Class>),但是使用registerFactory一个String类的名称而不是a Class,类似于我刚才提到的通用实现.这可能是避免创建CodecFactorys 实例所需的最少代码量.
我不可能举例说明你可以在这里做的每一件事情,所以这里有你可用的工具的部分清单,你应该根据自己的需要使用它们.记住:工厂是一个概念; 您可以使用所需的工具以清洁的方式实现该概念,以满足您的要求.
Class<?>和Class.forName)Class.forName可以触发它).另外:如果你不需要,不要疯狂地实施所有这些可能性; 考虑您的要求,并决定您在这里完成的最少工作量.例如,如果你需要可扩展的插件,单独的JSPF可能足以满足你的所有要求,而你不必做任何这项工作(我实际上没有检查过,所以我不确定).如果您不需要这种插件"扫描"行为,那么像上面的示例这样的简单实现就可以解决问题.
尝试这样的事情:
public class CodecFactory {
final private static Map<String, Class<? extends CodecInterface>> codecLibrary;
static {
codecLibrary = new HashMap<String, Class<? extends CodecInterface>>();
codecLibrary.put("codec1", Codec1.class);
//...
}
static CodecInterface create(String filename, String codecid, String args) throws InstantiationException, IllegalAccessException {
Class<? extends CodecInterface> clazz;
clazz = codecLibrary.get(codecid);
CodecInterface codec = clazz.newInstance();
codec.setArgs(args);
codec.setFilename(filename);
return codec;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3461 次 |
| 最近记录: |