1

我正在尝试在 OCaml 中概念化 Singleton 设计模式(qua Java),并且曾经看到实例提到函子或模块,我在 GoF 工作的概念证明中都没有使用它们。基本上,我想使用 OCaml 重新创建以下功能:

public class Singleton
{
 private static Singleton UniqueInstance; 
 private Singleton(){}
 public static Singleton getInstance()
 {
  if(UniqueInstance==null)UniqueInstance=new Singleton();
  return UniqueInstance;
 }
}

如果没有模块或函子,这可能吗?

4

2 回答 2

4

你能解释一下为什么模块解决方案不适合你吗?模块确实是执行其他语言中的单例操作的自然方式。

一般来说,GOF 风格的模式在 OCaml 中基本上是不相关的。事实上,对象在 OCaml 中基本上是不相关的。具有 OO 背景的人通常认为他们应该从使用 OCaml 中的 OO 功能开始,但这是一个错误。大多数用其他语言的对象解决的问题都是用 OCaml 中的参数多态性、代数数据类型和模块来解决的。函子是一个稍微高级一点的话题,而对象也很遥远。在您熟练掌握该语言的其余部分之前,您不应该接触对象系统。

于 2009-02-12T03:53:50.540 回答
3

Ocaml 中的对象不能有静态/全局方法。你可以试试,立即对象。警告,我认为您仍然可以执行 obj.copy 来获取对象的副本,并且每次对其进行评估时,它们都会返回一个新对象(因此您不能将参数传递给对象,但是您可以通过一些封装和全局布尔值,但现在您正在处理模块)。当然,如果你只有这样的例子,

let p = object 
   val mutable x = 0
   method get_x = x
   method move d = x <- x + d
end

p将被评估一次,并且您每次都可以访问它。当然, p#copy 会为你搞砸。模块是通往这里的路。Ocaml 的 OO 功能不像其他语言那样“强大”。这当然不是项目的目标,它的模块系统非常强大。

模块(全局)单例。你真的不需要写任何东西来构建它们。它们隐含在语言中。例如,如何记录到文件:

---logging.ml
(* default channel to log *)
let log_channel = ref stdout

(* set the log to a channel *)
let set_log_chan chan = log_channel := chan

(* timestamp of log -- kinda heavy weight, but just for kicks *)
let get_log_prequel () = 
    let time = Unix.localtime (Unix.time ()) in
    (* format the time data into "y/m/d - h:m:s" format *)
    Printf.sprintf  "%d/%02d/%02d - %02d:%02d:%02d"
                    (time.Unix.tm_year+1900) time.Unix.tm_mon time.Unix.tm_mday
                    time.Unix.tm_hour time.Unix.tm_min time.Unix.tm_sec

(* log a string to the channel *)
let log_string str =
    output_string (!log_channel) ((get_log_prequel ())^":\t"^str^"\n")

---logging.mli
set_log_chan : in_channel -> unit
log_string : string -> unit

我想你明白这里的意思。单例是隐式的,对吗?确实没有像在对象中那样的实例化——但这有点像你在单例中想要的。您只需开始在其他文件中使用它,例如Logging.log_string "parsed file "^file_name^" successfully.",等,任何地方,并且您始终使用相同的频道。

使用函子,您可以组合模块以增加多样性。就像指定一个模块/函数来生成输出的前传等等。

于 2009-02-11T20:10:27.377 回答