Ruby ruby 如何实现这个 aop 效果?

chenge · 2012年11月11日 · 最后由 chenge 回复于 2016年07月04日 · 7786 次阅读

这个是 spring python 的例子,看上去还不错。

import springpython.aop as saop

class SampleService:
    def method(self, data):
        return "You sent me '%s'" % data

    def m2(self, data):
        return "You sent me 2'%s'" % data

class WrappingSvc(saop.MethodInterceptor):
    def invoke(self, invocation):
        return "Wrapped:" + invocation.proceed() + ":Wrapped"


service = saop.ProxyFactoryObject(target = SampleService(), interceptors =  [WrappingSvc()])

print service.method("something")
print service.m2("something")

Wrapped:You sent me 'something':Wrapped Wrapped:You sent me 2'something':Wrapped

奇怪,为什么没人给答案呢?嫌简单懒得回答?

这个怎么看着不像 Python ...

Python 的风格难道不该是像下面这样


def m1():
    return "m1"

def m2():
    return "m2"

def wrapped(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return "<wrapped>{}</wrapped>".format(func(*args, **kwargs))

print m1()
print m2()

m1 = wrapped(m1)
m2 = wrapped(m2)

print m1()
print m2()

@fsword 第一,楼主提到的 springpyhton 不是每个人都熟悉,第二,今天双 11,被你厂的黑猫商城搞的大家都很忙,实在没空管这些一看看不明白的帖子。

这个算不算

class SampleService
  def m1(data)
    return "You sent me '#{data}'"
  end
  def m2(data)
    return "You sent me 2'#{data}'"
  end
end

class Wrapping
  def initialize(target)
    @target = target
  end

  def method_missing(m, *args)
    return "Wrapped:#{@target.send(m, *args)}:Wrapped"
  end
end

wrap = Wrapping.new(SampleService.new)
p wrap.m1("something")
p wrap.m2("something")

#4 楼 @cwheart 基本接近了,思路很巧妙,这个对单个 aspect 够用。 那个 python 似乎可以串接多个 aspect,这个估计用的不多。

这个怎么看起来有点像 tornado

#5 楼 @chenge aspect 是神马?写个串接多个的例子

这不是 decorator 嘛?-__,-

https://github.com/fredwu/ruby_decorators

class Hi < RubyDecorator
  def call(this, *args, &blk)
    this.call(*args, &blk).sub('hello', 'hi')
  end
end

class Batman < RubyDecorator
  def call(this, *args, &blk)
    this.call(*args, &blk).sub('world', 'batman')
  end
end

class Catwoman < RubyDecorator
  def initialize(*args)
    @args = args.any? ? args : ['catwoman']
  end

  def call(this, *args, &blk)
    this.call(*args, &blk).sub('world', @args.join(' '))
  end
end

class World
  extend RubyDecorators

  def initialize
    @greeting = 'hello world'
  end

  def hello_world
    @greeting
  end

  +Batman
  def hello_batman
    @greeting
  end

  +Hi
  +Catwoman
  def hello_catwoman
    @greeting
  end

  +Catwoman.new('super', 'catwoman')
  def hello_super_catwoman
    @greeting
  end
end

world = World.new

world.hello_world          # => "hello world"
world.hello_batman         # => "hello batman"
world.hello_catwoman       # => "hi catwoman"
world.hello_super_catwoman # => "hello super catwoman"

aspect 是一种新的编程范型,已经有多年历史了,大型程序比较有用。主要用途是横切,比如常见的 log,安全,缓存,事务等可以写到 aspect 里面。spring 框架就是靠这个成名。

rubu 要写多个串接的估计也不容易。

#9 楼 @chenge 就是 spring 的 AOP 呗,负责任的告诉你,实现起来很容易,很多种方式

#10 楼 @cwheart show 一下代码,如果真的很容易,那恭喜你,可以抢 spring 的饭碗了。

#11 楼 @chenge 抢饭碗不敢

class SampleService
  def m1(data)
    return "You sent me '#{data}'"
  end
  def m2(data)
    return "You sent me 2'#{data}'"
  end
end

class Wrapping
  def initialize(target, wrappers = [])
    @target = target
    @wrappers = wrappers
  end

  def method_missing(m, *args)
    return "#{@wrappers.map(&:perform_befor).join(':')}:#{@target.send(m, *args)}:#{@wrappers.map(&:perform_after).join(':')}"
  end
end

class WrapperDemo1
  def perform_befor
    "demo1 befor"
  end

  def perform_after
    "demo1 after"
  end
end

class WrapperDemo2
  def perform_befor
    "demo2 befor"
  end

  def perform_after
    "demo2 after"
  end
end

wrap = Wrapping.new(SampleService.new, [WrapperDemo1.new, WrapperDemo2.new])
p wrap.m1("something")
p wrap.m2("something")

#12 楼 @cwheart 效果不错。你的 ruby 水平很好。一点建议,那个长语句可重构下,可读性会好些。

#13 楼 @chenge 这个是基础,谈不上水平,只能说动态语言比较灵活

#12 楼 @cwheart 最好让 Wrapping 继承 BasicObject :)

class Wrapping < BasicObject

#12 楼 @cwheart 代码还有点问题,顺序应该是:

before1 before2 method after2 after1

不过,不严格的情况下,似乎是可以的。

#16 楼 @chenge 1.9.x 下 BasicObject 可做 blank class 用。1.8.x 需要自己处理一下,或使用 gem "blankslate"。

Ruby 的文档已经说的详细了:) http://ruby-doc.org/core-1.9.3/BasicObject.html

#18 楼 @skandhas 这个不是很重要吧。

#19 楼 @chenge 是重要的。blank class 的作用主要是防止名字空间被污染。 以此例来说,Wrapping 默认是从 Object 继承,这样会导致 Wrapping.new 创建的对象拥有来自 Object 及 Kernel 的方法。作为一个代理对象,这有可能会与被代理的对象有方法的冲突。 所以,在实现类似的代理类时,通常的做法是让它继承一个 blank class。

比如还是以此例稍微修改一下,然后你看看结果

class SampleService
  def m1(data)
    return "You sent me '#{data}'"
  end
  def m2(data)
    return "You sent me 2'#{data}'"
  end
  def method(data)
     return "You sent me 3'#{data}'"
  end
end

...省略......

p wrap.m1("something")
p wrap.m2("something")
p wrap.method("something")

#20 楼 @skandhas 但是这样的话,就不能使用 kernel 了,这个要权衡吧。

#21 楼 @chenge 是有使用场合的,主要是用在写代理类上。其他大多数的场合还是默认继承 Object

我写了这么多年代码还没发现过这样做的必要...

#3 楼 @xds2000 其实我是想说,什么 springpython 真是侮辱 python 了,python 的 decorate 甚至比 ruby 都好用,作者居然迁移 java 框架,就象已经修建了铁路,却非要找几匹马来拉火车一样,简直是上等的吐槽贴啊

#11 楼 @chenge 在 ruby(和其它动态语言)社区,spring 这种东西本来就没有饭碗

有这样用 Python 的?

看不懂啊,这不就是一个简单 的 mixin 功能吗? 哦 其实是个 decorator

class SampleService
  def meth(data)
    "You sent me #{data}" 
  end

  def m2(data)
    "You sent me 2#{data}"
  end
end

class Wrapper
  def invoke(&blk)
    "Wrapped: " + blk.call + " :Wrapped" if block_given?
  end
end

module Decorator
  def initialize(service, wrap)
    @service = service
    @wrap = wrap
  end

  def method_missing(meth, *args)
    if @service.respond_to?(meth)
       @wrap.invoke { @service.send(meth, *args) } 
    else
      super
    end
  end

  def respond_to?(meth)
    @service.respond_to?(meth)
  end
end

class Service
  include Decorator
end

service = Service.new(SampleService.new, Wrapper.new)

puts service.meth("something")
puts service.m2("something")

#26 楼 @xuluan 装饰模式确实更好,可以级联多个。

#24 楼 @fsword 能否给出 python 代码,大家学习下。

#28 楼 @chenge python 我只是票友,不过 #2 楼 @bhuztez 已经给出答案了。另外(如果没看过的话),我推荐 ruby 设计模式 这本书

看这个包的命名应该是代理模式 用 C++ 都可以实现这种模式……

#23 楼 @luikore blank class 在写 builder-style 的 DSL 时,经常会用到。比如 gem“builder”和 DHH 的“jbuilder”

#30 楼 @wuwx 严格说应是代理模式。两个模式的图不一样,装饰更难理解一些。

每次看这种 python/ruby 写 java 就抓鸡心疼

本来静态语言当宝的各种设计模式要解决的问题,在动态语言的鸭子和匿名闭包等特性下都不是问题…

class World
  extend RubyDecorators

  def initialize
    @greeting = 'hello world'
  end

  def hello_world
    @greeting
  end

  +Batman
  def hello_batman
    @greeting
  end

  +Hi
  +Catwoman
  def hello_catwoman
    @greeting
  end

  +Catwoman.new('super', 'catwoman')
  def hello_super_catwoman
    @greeting
  end
end

这里的+Hi, + 号是什么意思?

#36 楼 @donbe 直接去看源码啊,很简单的

class RubyDecorator
  def self.+@
    RubyDecorators::Stack.all << self
  end

  def +@
    RubyDecorators::Stack.all << self
  end
end

#36 楼 @donbe 双飞燕那本书里也有类似例子

对这种问题无爱呀。只会让我更混乱....

还是用 Ruby 的方式解决 Ruby 自己擅长的问题吧。何必要和其他类比??

chenge Ruby 学习汇集,请推荐内容 提及了此话题。 07月04日 11:35
需要 登录 后方可回复, 如果你还没有账号请 注册新账号