【发布时间】:2014-07-10 14:54:28
【问题描述】:
我有一个设计问题。
我正在用 ruby 编写一个 REST 客户端。由于我无法控制的原因,它必须扩展另一个使用我的网络 zookeeper 实例进行服务查找的 gem。我的客户端采用用户提供的层级,并根据该值向 zookeeper 注册表查询适当的服务 url。
问题是我还需要能够针对本地运行的被测服务版本运行我的客户端。当服务在本地运行时,显然不涉及 zookeeper,所以我只需要能够对 localhost 资源 url 进行 GET 请求。
当用户实例化我的 gem 时,他们会调用如下代码:
client = MyRestClient.new(tier: :dev)
或在本地模式下
client = MyRestClient.new(tier: :local)
我想避免有条件地破解 MyRestClient 中的构造函数(以及 MyRestClient 中的所有 GET 方法)来更改基于 :local 与 :requests_via_the_zk_gem 的请求。
我正在寻找一种优雅而干净的方式来处理 Ruby 中的这种情况。
一种想法是创建两个客户端类,一个用于:local,另一个用于:not_local。但是我不知道如何提供一个可以返回正确客户端对象的 gem 接口。
如果MyClient 的构造函数看起来像这样:
class MyClient
attr_reader :the_klass
def initialize(opts={})
if opts[:tier] == :local
@the_klass = LocalClass.new
else
@the_klass = ZkClass.new
end
@the_klass
end
end
然后我会得到类似的结果:
test = MyClient.new(tier: :local)
=> #<MyClient:0x007fe4d881ed58 @the_klass=#<LocalClass:0x007fe4d883afd0>>
test.class
=> MyClient
test.the_klass.class
=> LocalClass
那些使用我的 gem 的人必须拨打如下电话:
@client = MyClient.new(tier: :local)
@client.the_klass.get
这似乎不对
我可以使用一个模块来返回适当的类,但随后我面临如何为我的 gem 提供单个公共接口的问题。我无法使用 .new 实例化模块。
我的感觉是这是一个常见的 OO 问题,但我还没有遇到它。也有可能答案正盯着我看,而我还没有找到。
非常感谢您的帮助。
【问题讨论】:
-
你要找的东西叫Factory Pattern
-
不使用
Class,我认为你可以使用Module,使用if opts[:tier] == :local then include LocalClientModule else include ZkModule end。
标签: ruby