最简单的方法是RemoteIdGetter
返回一个由 1. 请求的键和 2. 自身的新实例组成的对,并缓存了请求的键。针对RemoteIdGetter
. 实例的自动线程可以使用 monad 来完成。
在 Scala 中没有特别需要它的原因,因为您可以通过在类中更改缓存映射来实现相同的目的。
有点有趣的是,您可以编写一个RemoteIdGetter
从在线获取密钥的普通函数,然后编写一个通用缓存函数来包装任何昂贵的计算。这与 memoization 的概念非常相似,只是您保留了一些有关何时要丢弃缓存结果的元数据。没有真正的理由RemoteIdGetter
应该自己做缓存,当它可以被卸载到其他一些专门用于缓存的通用函数时。
通用缓存器
因此,假设您有一个功能可以做一些昂贵的事情。在这种情况下,为了简单起见,我只是让它接收用户输入。
def get_id():
try:
return int(input("Enter an id: "))
except ValueError:
return get_id()
这要求用户输入一个整数。如果用户没有输入一个,它只是再次询问。我们不想一直用整数打扰用户,所以我们想缓存用户输入的值以供进一步使用。我们可以在函数中缓存东西get_id
,但这不是理想的情况,因为我们想要分离关注点。
所以我们要做的是创建一个通用的缓存对象,它可以缓存任何值。我们希望能够做这样的事情:
cached_id = Cacher(get_id)
cached_id.get()
然后获取缓存的 ID 或向用户询问号码,具体取决于号码是否被缓存/最近更新。
该Cacher
对象需要保存数据和数据的过期时间。在这种 ID 情况下,这将转换为缓存器中的两个字段。您可能希望在现实世界的缓存对象中使用地图/字典,但我试图保持简单。
class Cacher:
value = None
expires = 0
如果值未缓存,则使用要调用的函数初始化缓存对象,所以这只是
def __init__(self, function):
self.external = function
然后有趣的是get()
方法,它最简单的形式看起来像
# Get a value and in case it is retrieved, make it good for 30 seconds
def get(self, ttl=30):
if not self.value or datetime.now() > self.expires:
# Get a new value from the external source
self.value = self.external()
# and update the new expiration time to ttl seconds from now
self.expires = datetime.now() + timedelta(seconds=ttl)
return self.value
然后,当您cached_id.get()
第一次调用时,您必须输入一个整数,但 30 秒内的任何连续调用都将检索最后输入的整数。
这个可变的 Python 对象当然可以在 Scala 中实现为演员,我只是想展示其原理。