Ruby元编程之杂记2

一个重复代码的问,题虽然重复就是一切.但是你知道的。 重复1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class DS
 def initialize
 def get_mouse_info(workstation_id)
 def get_mouse_price(workstation_id)
 def get_cpu_info(workstation_id)
 def get_cpu_price(workstation_id)
 def get_display_info(workstation_id)
 def get_display_price(workstation_id)
end

ds = DS.new
ds.get_mouse_info(42)
ds.get_mouse_price(42)
ds.get_cpu_info(42)
ds.get_cpu_price(42)
ds.get_display_info(42)
ds.get_display_price(42)

这样的代码,会发现人生的重点都是“command+c”与“command+v”,在无乐趣可言。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Computer
 def initialize(computer_id,data_source)
   @id = computer_id
   @data_source = data_source
 end

 def mouse
  info  = @data_source.get_mouse_info(@id)
  price = @data_source.get_mouse_price(@id)
  result = "Mouse: #{info} ($#{price})"
  return " * #{result} " if price >=100
  result
 end

 def cpu
  info  = @data_source.get_cpu_info(@id)
  price = @data_source.get_cpu_price(@id)
  result = "cpu: #{info} ($#{price})"
  return " * #{result} " if price >=100
  result
 end

 def keyboard
  info  = @data_source.get_keyboard_info(@id)
  price = @data_source.get_keyboard_price(@id)
  result = "keyboard: #{info} ($#{price})"
  return " * #{result} " if price >=100
  result
 end
  ...
end

dynamic_call.rb(动态调用方法)

class MyClass
  def my_method(my_arg)
    my_arg * 2
  end
end

obj = Myclass.new
obj.my_method(3)
obj.send(:my_method,3)

dynamic_defintion(动态定方法)

class MyClass
  define_method :my_method do |my_arg|
    my_arg * 3
  end
end

obj = MyClass.new
obj.my_method(2)

重构Computer类 第一步:添加动态派发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Computer
  def initialize(computer_id,data_source)
    @id = computer_id
    @data_source = data_source
  end

  def mouse
    component :mouse
  end

  def cpu
    component :cpu
  end

  def keyboard
    component :keyboard
  end

  def component
    info  = @data_source.send "get_#{name}_info", @id
    price = @data_source.send "get_#{name}_price", @id
    result= "#{name.to_s.capitalize}: #{info} ($#{price})"
    return " * #{result}" if price >=100
    result
  end

end

my_computer = Computer.new(42,DS.new)
my_computer.cpu => * Cpu: 2.16 Ghz ($220)

第二步:动态创建方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Computer
  def initialize(computer_id,data_source)
    @id = computer_id
    @data_source = data_source
  end

  def self.define_component(name)
    define_method(name){
      info  = @data_source.send "get_#{name}_info", @id
      price = @data_source.send "get_#{name}_price", @id
      result= "#{name.to_s.capitalize}: #{info} ($#{price})"
      return " * #{result}" if price >=100
      result
    }
  end
  define_component :mouse
  define_component :cpu
  define_component :keyboard
end

第三步:用内省(Introspection)方式缩减代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Computer
  def initialize(computer_id,data_source)
    @id = computer_id
    @data_source = data_source
    data_source.methods.grep(/^get_(*)_info$/){
      Computer.define_component $1
    }
  end

  def self.define_component(name)
    define_method(name){
      info  = @data_source.send "get_#{name}_info", @id
      price = @data_source.send "get_#{name}_price", @id
      result= "#{name.to_s.capitalize}: #{info} ($#{price})"
      return " * #{result}" if price >=100
      result
    }
  end
end

重构Computer类(再一次)

用method_missing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Computer
  def initialize(computer_id,data_source)
    @id = computer_id
    @data_source = data_source
  end

  def method_missing(name,*args)
    super if @ data_source.respond_to?("get_#{name}_info")
    info  = @data_source.send "get_#{name}_info", @id
    price = @data_source.send "get_#{name}_price", @id
    result= "#{name.to_s.capitalize}: #{info} ($#{price})"
    return " * #{result}" if price >=100
    result
  end

end

my_computer = Computer.new(42,DS.new)
my_computer.cpu => * Cpu: 2.16 Ghz ($220)

覆写respond_to?()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Computer
  def initialize(computer_id,data_source)
    @id = computer_id
    @data_source = data_source
  end

  def method_missing(name,*args)
    super if respond_to?(name)
    info  = @data_source.send "get_#{name}_info", @id
    price = @data_source.send "get_#{name}_price", @id
    result= "#{name.to_s.capitalize}: #{info} ($#{price})"
    return " * #{result}" if price >=100
    result
  end

  def respond_to?(method)
    @data_source.respond_to?("get_#{method}_info")|| super
  end
end

加入instance_methods方法,解决display的内部方法冲突

Object.instance_methods.grep /d/ #=>[:dup,:display,define_singleton_method]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Computer
   instance_mehods.each do |m|
    undef_method m unless m.to_s =~ /^__|method_missing|respond_to?/
  end

  def initialize(computer_id,data_source)
    @id = computer_id
    @data_source = data_source
  end

  def method_missing(name,*args)
    super if respond_to?(name)
    info  = @data_source.send "get_#{name}_info", @id
    price = @data_source.send "get_#{name}_price", @id
    result= "#{name.to_s.capitalize}: #{info} ($#{price})"
    return " * #{result}" if price >=100
    result
  end

  def respond_to?(method)
    @data_source.respond_to?("get_#{method}_info")|| super
  end
end

想起了以前写的业务结算的代码,回头补上代码。

原文地址:http://dlj.bz/r1ibS5

Comments