我有一个用例,其中有一个由外部供应商提供的数据库接口,假设如下所示:
interface Database{
public Value get(Key key);
public void put(Key key, Value value)
}
供应商提供了此接口的多个实现,例如actualdatabaseimpl、mockdatabaseimpl。我的用户希望使用数据库接口,但在调用某些api之前,他们希望执行一些额外的工作,例如在进行调用之前调用客户端速率限制器。因此,与其让每个消费者都做额外的检查ratelimmiter的限制的工作,我想创建一个修饰类,它将抽象出ratelimmit部分,并且消费者可以在不知道ratelimmiter逻辑的情况下与db交互。例如
class RateLimitedDatabase implements Database{
private Database db;
public RateLimitedDatabase(Database db) {this.db = db;}
public Value get(Key key) {
Ratelimiter.waitOrNoop();
return db.get(key);
}
public void put(Key key, Value value) {
Ratelimiter.waitOrNoop();
return db.put(key, value);
}
}
只要数据库接口没有引入新的方法,这就可以正常工作,但是一旦它们开始添加我并不真正关心的api,比如delete/getdbinfo/deletedb等问题就开始出现了。
每当有新方法的DB版本发布时,我的RateLimitedDatabase版本就会崩溃。一种选择是在修饰类中实现新方法,以调查生成失败的根本原因,但这对开发人员来说只是额外的痛苦。有没有其他方法来处理这种情况,因为在使用具有不断变化/扩展接口的decorator模式时,这似乎是一个常见的问题?
注意:我也可以考虑构建一个基于反射的解决方案,但对于这个特定的问题来说,这似乎是一个过度的设计。
最佳答案
如果这是可行的(您需要修改您的所有客户端代码),您可以提取vendor.Database
接口的“镜像”并将其称为例如mirror.Database
;然后只将您需要的方法从vendor.Database
接口复制到mirror.Database
(具有相同的签名)。
编辑客户端代码以使用mirror.Database
接口,并让RateLimitedDatabase
实现这个mirror.Database
接口。因为所有的方法签名都是相同的,所以将客户机代码切换到镜像接口应该是无痛的。RateLimitedDatabase
将委托给vendor.Database
实施。
(我认为我所描述的或多或少是桥接模式(使用接口来“屏蔽”底层更改),https://en.wikipedia.org/wiki/Bridge_pattern)