ゲッター/セッター メソッドを備えたインターフェイスで構成されたオブジェクト モデルがあります。これらのオブジェクトの実装は動的プロキシを使用して作成され、フィールドの値が (JavaBean 命名規則を使用して) 暗黙的に Map に格納されます。
これらのインターフェースにメソッドを追加して、ビジネス ロジックを提供したいと考えています (単なる POJO のコレクションではなく、実際のオブジェクト モデルのように)。
私が最初に考えたのは、各インターフェイスを実装するが、ビジネス メソッドの実装のみを提供する抽象クラスを作成することでした。次に、これらの実装を InvocationHandler の Map と組み合わせて使用して、インターフェイスの完全な実装を提供します。
何かのようなもの:
interface ModelObject extends BaseModel {
void setFoo(String foo);
String getFoo();
void doSomething();
}
public abstract class ModelObjectImpl implements ModelObject {
@Override
public void doSomething()
{
// Do something
}
}
public class ModelObjectInvocationHander implements InvocationHandler {
Map<String, Object> fieldValues; // holds values for implied fields for getter setter
ModelObject modelObject; // holds reference to object implementing business methods
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Get implied field name for method name by removing "get"/"set" and lower casing next letter
String fieldName = getBeanNameForMethod(method.getName());
if (fieldValues.containsKey(fieldName)) {
return fieldValues.get(fieldName);
}
// Not a getter/setter so must be a business method. Delegate to implementation class
return method.invoke(modelObject, args);
}
}
抽象クラスのインスタンスを作成できないことを除けば、このような (しかし明らかにより複雑な) 方法が機能します。BusinessObjectImpl を非抽象化し、呼び出されることのない getter/setter メソッドの何もしない実装を追加することもできますが、それはコードを醜くし、メンテナンスの問題を引き起こすだけです。また、BusinessObjectImpl を実際には BusinessObject インターフェイスを実装しないようにすることもできますが、実装とインターフェイスの間の適切なバインディングが壊れ、インターフェイスと「実装」が同期しなくなるとエラーが発生します。
これらのビジネス メソッドを呼び出すために使用できる Java リフレクションの巧妙なトリックはありますか?
更新: 既に実装されている Java 動的プロキシ フレームワークとJavassistを組み合わせて、抽象実装クラスのプロキシを作成しました。これにより、必要に応じてビジネス メソッドが追加されるまで、既存のモデル インターフェイスをまったく変更する必要がなくなります。オブジェクトに動作を追加する機能が用意されました。真のオブジェクト指向コードを今すぐ書き始めるかどうかは、開発者次第です。
public class ModelObjectInvocationHandler implements InvocationHandler
{
public ModelObjectInvocationHandler(Class<ModelImplementation<? extends BaseModel>> implementationClass)
{
if (implementationClass != null)
{
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(implementationClass);
try
{
modelObject = (ModelObject) factory.create(new Class<?>[0], new Object[0]);
}
catch (Exception e)
{
// Exception handling
}
}
}
Map<String, Object> fieldValues; // holds values for implied fields for getter setter
ModelObject modelObject; // holds reference to object implementing business methods
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
// Get implied field name for method name by removing "get"/"set" and lower casing next letter
String fieldName = getBeanNameForMethod(method.getName());
if (fieldValues.containsKey(fieldName))
{
return fieldValues.get(fieldName);
}
// Not a getter/setter so must be a business method. Delegate to implementation class
if (modelObject != null)
{
return method.invoke(modelObject, args);
}
return null;
}
}
実行時に、実装クラスをスキャンしてMap<Class<? extends BaseModel>, Class<ModelImplementation>>
. インターフェイスの動的プロキシを作成するとき、マップでその実装クラスを見つけて、InvocationHandler
. Bean 名として一致しないメソッドは、実装クラスのプロキシに委譲されます。もちろん、モデル インターフェイス内のクラス階層と多重継承を考慮する必要があるため、それよりも少し複雑ですが、理論は正しいです。