我在从OOP思维转向功能性思维方面遇到了一些麻烦。我目前的问题是我有一个不可变的,持久的数据结构,可以用它(比方说)来构建URL-s:
class UrlBuilder {
public UrlBuilder withHost(String domain) {
return new UrlBuilder(/*...*/);
}
public UrlBuilder withPort(Int port) {
return new UrlBuilder(/*...*/);
}
// ...
public String build() {
// ...
}
}
懒惰地评估字符串的build()
方法非常昂贵,所以我想缓存结果。
在OOP中这没问题,因为我可以这样做:
class UrlBuilder {
private String url;
// ...
public String build() {
if (null == this.url) {
this.url = doExpensiveEvaluation();
}
return this.url;
}
}
如果我需要线程安全,我会使用双重检查锁定并完成它。但据我所知,这是反对功能范式,因为它引入了副作用(修改了对象的内部状态)。
我知道在Scala中有lazy
关键字,它完全符合我的需要:实现所谓的 by-need 懒惰。但是我怎样才能用OOP语言做同样的事情呢?我实际上非常好奇他们如何在Scala中实现它。
我试图将结果缓存到我的UrlBuilder
的消费者的责任,但这在消费者方面引起了同样的问题:
class Consumer {
private UrlBuilder urlBuilder;
private String url;
// ...
public String getUrl() {
if (null == this.url) {
this.url = urlBuilder.build(); // same as before!
}
return this.url;
}
}
因此我在标题中提出了问题。
编辑:要明确:我问的是除了Scala以外的OOP语言实现。它可能是Java或C#,但我也想知道如何在JavaScript之类的东西中做到这一点。正如我所提到的,我可以使用锁定,但我一直在寻找一种纯功能解决方案,而不必使用锁定。
我的印象是功能编程是开箱即用的线程安全,因此锁定感觉就像一个丑陋的OOP解决方案。但当然我也会接受一个证明这是不可能的答案。 Ben Reich的The comment bellow几乎说明了一切:如果Scala开发人员在没有锁定的情况下无法做到这一点,那么我可能会死于尝试。
答案 0 :(得分:1)
我们在谈论java不是吗?为什么不同步呢?
class LazyClass
{
Integer someValue = null;
public synchronized Integer someReallyExpensiveMethod() {
if (someValue == null)
{
someValue = 1 + 2 + 3; // .. + 32 + .. this takes a long time
}
return someValue;
}
}
答案 1 :(得分:0)
这个怎么样:
object UrlBuilder{
def empty = new InnerBuilder("")
class InnerBuilder(...){
def withHost(host: String) = new InnerBuilder(...)
def withPort(port: Int) = new InnerBuilder(...)
def build(): String = ...
}
这样你就没有任何可变的状态
}
并使用它:
UrlBuilder.empty
.withHost(...)
.withPort(...)
.build()
答案 2 :(得分:0)
我已经找到了Rich Hickey对这个问题in this article的最佳答案。它涉及所谓的瞬态数据结构的Closure实现。它们本质上是对持久数据结构的可变副本进行操作,但是对于外部世界透明地执行(在后台使用锁定)。
除了描述数据结构如何工作之外,文章基本上表明只要无法观察到突变就可以进行突变。
事实证明这是某种哲学问题。文章的引用总结了这个非常好的:
如果一棵树落在树林里,它会发出声音吗?
如果纯函数改变某些本地数据以产生不可变的返回值,那可以吗?
- Rich Hickey,Clojure