我有一个用例,其中我有一个由外部供应商提供的数据库接口,让我们说它看起来如下:
interface Database{
public Value get(Key key);
public void put(Key key, Value value)
}
供应商提供此接口的多种实现,例如ActualDatabaseImpl,MockDatabaseImpl。我的使用者想使用DataBase接口,但是在调用某些API之前,他们想执行一些其他工作,例如在进行调用之前先调用客户端速率限制器。因此,我不必创建每个修饰的类,而不是每个使用者都要检查rateLimiter的限制来做额外的工作,而该类将抽象出速率限制部分,并且使用者可以与DB进行交互,而无需了解RateLimiter的逻辑。例如
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);
}
}
只要Database接口不引入新方法,此方法就可以正常工作,但是只要它们开始添加我并不真正在意的API,例如删除/ getDBInfo / deleteDB等问题开始出现。
每当发布具有较新方法的DB新版本时,我对RateLimitedDatabase的构建都会中断。一个选项是在装饰类中实施新方法,以调查构建失败的根本原因,但这对开发人员而言是额外的痛苦。还有其他方法可以处理此类情况,因为在将Decorator模式与不断变化/扩展的接口配合使用时,这似乎是一个常见问题?
注意:我也可以考虑构建一个基于反射的解决方案,但是对于这个特定问题,这似乎是过分/过度设计。
答案 0 :(得分:1)
如果可行(您需要修改所有客户端代码),则可以提取vendor.Database
接口的“镜像”,并将其命名为。 mirror.Database
;并仅将您需要的方法从vendor.Database
接口复制到mirror.Database
(具有相同的签名)。
编辑客户端代码以使用mirror.Database
接口,并让RateLimitedDatabase
实现此mirror.Database
接口。由于所有方法签名都相同,因此将客户端代码切换到镜像接口应该很容易。 RateLimitedDatabase
将委派给一个vendor.Database
实现的课程。
(我认为我所描述的或多或少是桥接模式(使用接口“屏蔽”潜在变化)https://en.wikipedia.org/wiki/Bridge_pattern)
答案 1 :(得分:0)
面向方面的编程可以解决此问题。 大多数框架都会为您的界面生成一个动态代理,因此它总是同步的。