我遇到了设计问题。
我正在用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
然后使用我的宝石的人必须拨打电话:
@client = MyClient.new(tier: :local)
@client.the_klass.get
这似乎不正确
我可以使用模块返回相应的类,但后来我遇到了如何为我的gem提供单个公共接口的问题。我不能用.new。
实例化一个模块我的感觉是,这是一个常见的OO问题,我还没有碰到它。也有可能答案是盯着我,我还没有找到它。
非常感谢任何帮助。
答案 0 :(得分:2)
一种常见的模式是将服务传递到客户端,例如:
class MyClient
attr_reader :service
def initialize(service)
@service = service
end
def some_method
service.some_method
end
end
用以下方法创建:
client = MyRestClient.new(LocalClass.new)
# or
client = MyRestClient.new(ZkClass.new)
您可以将这两个移动到类方法中:
class MyClient
self.local
new(LocalClass.new)
end
self.dev
new(ZkClass.new)
end
end
而是致电:
client = MyRestClient.local
# or
client = MyRestClient.dev
答案 1 :(得分:1)
您可以使用method_missing
从客户端委派给实际的班级。
def method_missing(m, *args, &block)
@the_class.send(m, *args, &block)
end
因此,只要在您的课程中调用一个不存在的方法(例如您的示例中为get
),就会在@the_class
上调用它。
定义相应的respond_to_missing?
btw:
def respond_to_missing?(m, include_private = false)
@the_class.respond_to?(m)
end
答案 2 :(得分:1)
您描述的用例看起来像是一个经典的工厂方法用例。
这个的常见解决方案是创建一个返回相关类实例的方法( not new
):
class MyClient
def self.create_client(opts={})
if opts[:tier] == :local
LocalClass.new
else
ZkClass.new
end
end
end
现在您的用法是:
test = MyClient.create(tier: :local)
=> #<LocalClass:0x007fe4d881ed58>
test.class
=> LocalClass