我有一个我正在编写的平台,它为开发人员提供了一组默认模块,我希望他们可以扩展这些模块以在运行时提供更多自定义功能。以下代码段是默认模块层次结构:
package com.test.core;
public abstract class Module {
public abstract void performAction();
}
package com.test.fun;
public class AModule extends Module {
@Override
public void performAction() {
// Perform action A
}
}
package com.test.fun;
public class BModule extends Module {
@Override
public void performAction() {
// Perform action B
}
}
package com.test.fun;
public class CModule extends Module {
@Override
public void performAction() {
// Perform action C
}
}
有一个模块引擎将在应用程序实例的开头创建模块,并将这些模块存储在HashMap中,其中[key = name;值=模块。由于我的后端和应用程序的性质,我必须使用名称作为密钥并识别模块。
public Module createModule(String name) throws Exception {
Module module = null;
Class moduleClass = null;
// Find class by name, default to using <name>Module as the class name
// might throw exception
String className = name = "com.test.fun." + name + "Module";
moduleClass = Class.forName(className);
if (moduleClass == null) {
// quit, nothing to do
} else {
// create the module
module = (QPComponent) moduleClass.getConstructor().newInstance();
}
return module;
}
假设AModule是二进制文件并且我无法更新其实现,我想在模块中添加更多行为,并将它们键为“A”,如下所示。
package com.test.custom;
public class ExtendedModuleA extends AModule {
@Override
public void performAction() {
super.performAction();
// Do some more to add behaviour to AModule
}
}
我如何修改体系结构以使开发人员能够为密钥“A”注册模块的自定义实现,以便在应用程序启动时,应用程序将获取自定义ExtendedModuleA版本而不是默认的AModule版本?
我想的一种方式,看起来并不漂亮如下:
public class ModuleRegistry {
// Assume singleton
HashMap<String, Class<Module>> registry;
public ModuleRegistry() {
}
private void init() {
registry = new HashMap<String, Class<Module>>();
registry.put("A", ExtendedModuleA.class);
// no extension for B
registry.put("C", CModuleExtra.class);
// developers add more entries to "register" more extended modules here for the future
}
public Class<Module> getExtendedModuleClass(String name) {
return registry.get(name);
}
}
是否有设计模式或工具库可以帮助我解决这类问题?我目前只考虑这个解决方案,因为我记得像Spring或Dagger或Android的清单这样的东西,你必须注册你的类,以便它们被系统使用或拾取。 我已经在我的应用程序中使用了Dagger,但我的模块本身需要ObjectGraphs,因此可能有鸡和鸡蛋捕获22的情况。
答案 0 :(得分:1)
您需要检查Decorator Pattern,它允许您在运行时为类添加动态行为。这更适用于组合而不是继承,比如为类创建包装器。这是一个启动示例,只是为了理解主要想法:
abstract class Beverage {
protected final BigDecimal price;
public Beverage(BigDecimal price) {
this.price = price;
}
public BigDecimal getPrice() {
return this.price;
}
}
class Coffee extends Beverage {
public Coffee(BigDecimal price) {
super(price);
}
}
class BeverageWithSugar extends Beverage {
private Beverage beverage;
private static final BigDecimal sugarPrice = new BigDecimal("0.15");
public BeverageWithSugar(Beverage beverage) {
super(sugarPrice);
this.beverage = beverage;
}
@Override
public BigDecimal getPrice() {
//here we add the behavior dynamically
return this.beverage.getPrice().add(sugarPrice);
}
}
class BeverageWithChocolate extends Beverage {
private Beverage beverage;
private static final BigDecimal chocolatePrice = new BigDecimal("1.25");
public BeverageWithChocolate(Beverage beverage) {
super(chocolatePrice);
this.beverage = beverage;
}
@Override
public BigDecimal getPrice() {
//here we add the behavior dynamically
return this.beverage.getPrice().add(chocolatePrice);
}
}
public class BeverageStore {
public static void main(String[] args) {
Coffee coffee = new Coffee(new BigDecimal("0.5"));
//adding chocolate to our coffee
Beverage coffeeWithChocolate = new BeverageWithChocolate(coffee);
//adding sugar to our coffee
Beverage coffeeWithSugar = new BeverageWithSugar(coffee);
//adding both chocolate and sugar to our coffee
Beverage greatCoffee = new BeverageWithChocolate(new BeverageWithSugar(coffee));
System.out.println("Coffee price: " + coffee.getPrice());
System.out.println("Coffee with chocolate price: " + coffeeWithChocolate.getPrice());
System.out.println("Coffee with sugar price: " + coffeeWithSugar.getPrice());
System.out.println("Coffee with chocolate and sugar price: " + greatCoffee.getPrice());
}
}
打印:
Coffee price: 0.5
Coffee with chocolate price: 1.75
Coffee with sugar price: 0.65
Coffee with chocolate and sugar price: 1.90
要检查在Java框架中如何使用装饰器模式和其他模式,请参阅Examples of GoF Design Patterns in Java's core libraries
答案 1 :(得分:0)
您可能希望动态加载模块(而不是在init函数中)。
你可以在AModule类中提供最终方法
public final String getRegistryKey() {
return "A";
}
public final String getRegistryKey() {
return "A";
}
并在ModuleRegistry中实现一个注册方法(我假设它是一个单例,其他开发人员可以在运行时访问它)。
所以谁曾经使用过你的课程,可以在需要的时候在运行时注册或删除他们的模块。public void register(Module m) {
// check key exist and throw exception if you need.
registry.put(m.getRegistryKey(), m.getClass());
}
public void remove(Module m) {
hashMap.remove(m.getRegistryKey());
}