如何实现每个对象只执行一次的例程

来源:互联网 发布:mysql mongodb 编辑:程序博客网 时间:2024/06/06 03:47
    对象是数据和行为的有机结合体,每一个对象实例保存的数据都是自身特有的信息,而其中的一些数据是需要经过计算或处理才能得到的,有时候这种计算的量还不小。因此对于那些在整个对象生命周期都不会改变的数据而言,每次为了获取它们而重复执行的计算就是一个大大的浪费。一个改进的方案是:在对象创建时执行相应的动作以预先计算好那些稳定的数据并保存在内部,以后需要的话就直接返回这些预先算好的数据。这听起来是不错,但有没有问题呢?

    让我们假设有一类名为COMPANY的对象,它们都有代表历史交易信息的stock_history属性,对应的数据非常庞大并被保存在数据库中,这些信息只有从数据库载入到内存后才能使用。而实际情况是,该类对象的大部分使用者可能仅仅利用它们做些简单的操作,并不关心甚至压根儿就不知道还可以取得历史交易信息,但他们仍然不得不(在对象创建时)承担相应的计算负担,糊里糊涂地做了冤大头。所以为了最广大人民群众的根本利益,要站在讲政治的高度坚决贯彻“不使用就不掏钱”和“别重复肮脏活儿”的原则,想人民之所想,急人民之所急,为群众送去一种特殊的例程,它的功能体只会在某个对象首次调用时执行,其后相同对象进行的所有调用都不重复执行计算而直接返回第一次计算得出的结果。按照严肃的科学黑话来说,这就是典型的“按需计算”(computing on demand)。

    下面以Lua语言为例,设计了一个简洁的once per object机制。

ONCE_PER_OBJECT = {}
ONCE_PER_OBJECT.__index = ONCE_PER_OBJECT

-- 创建一个对每个对象只执行一次的例程,target是针对的对象,func是只执行一次的函数。
-- 不要惊慌,Lua里没有真正的面向对象,但函数可是一等公民。
function ONCE_PER_OBJECT.new(target,func)
   local o = {}
   o.target = target
   o.computed = false
   o.routine = func
   o.result = 0
   setmetatable(o,ONCE_PER_OBJECT)
   return o
end

-- 按需执行once per object例程,并返回相应的值
function ONCE_PER_OBJECT:value(...)
   if self.computed then
      print("So easy, data has been computed!")
      return self.result
   else
      self.result = self.routine(self.target,...)
      self.computed = true
      return self.result
   end
end

COMPANY = {}
COMPANY.__index = COMPANY

--真正执行从数据库载入历史信息的函数
local function load_stock_history(company)
   print("Loading "..company.name..
         " company's data from database, fucking dirty work!")
   return "I am from database!"
end

-- 给出公司名字,创建一个COMPANY对象
function COMPANY.new(name)
   local o = {}
   o.name = name
   o.stock_history = ONCE_PER_OBJECT.new(o,load_stock_history)
   setmetatable(o,COMPANY)
   return o
end

-- 创建一个叫Microsoft的COMPANY对象
ms = COMPANY.new("Microsoft")
-- 第一次读取ms的历史信息
print(ms.stock_history:value())
-- 第二次读取
print(ms.stock_history:value())
-- 第三次读取
print(ms.stock_history:value())

    执行上面的代码,输出如下:

Loading Microsoft company's data from database, fucking dirty work!
I am from database!
So easy, data has been computed!
I am from database!
So easy, data has been computed!
I am from database!

    很显然,只有第一次读取stock_history才真的去找数据库了,随后均是轻松地返回已计算好的值。

    本文所演示的机制并不复杂,关键是计算(本例表现为函数)要能象对象一样传来传去,这对诸如Lua一类的函数是一等值(first-class value)的动态语言来说根本没有问题,而对于那些静态面向对象语言就需要有良好的delegate或者agent机制的支持。在这方面做得最好的当属Eiffel语言,它的agent机制为语言引入了强大的函数式特性,而本文的思路正是来自于Eiffel语言手册中《Agents,iterators and introspection》一章。
原创粉丝点击