2010-04-22 16 views
9

Tengo un caché de datos que se actualiza desde una fuente externa, y quiero limitar mi acceso a su caché (solo lectura) dentro de mi aplicación. No quiero actualizar el origen de datos cada vez que necesito acceder a él (es decir, en la creación de instancias vaya y obtenga todos los datos que necesito, ya que hay bastante información actualizada).Cómo implementar Singleton Pattern (sintaxis)

type MySingleton = 

     [<DefaultValue>] 
     static val mutable private instance: MySingleton 

     static member GetInstance() = 
      instance 

Supongo que este es uno de los problemas sobre la implementación de un proyecto y tratar de aprender el idioma al mismo tiempo. Sé que la lógica debe ser

if instance is null 
    synchronize 
    if instance is null 
     instance = new MySingleton() 

pero la falta de nulo me está tirando por un bucle. Creo que puedo utilizar un tipo de opción, etc. pero me está tirando para un bucle

type MySingleton = 

     [<DefaultValue>] 
     static val mutable private instance: MySingleton option 

     static member GetInstance() = 
      match instance with 
       | Some(i) -> i 
       | None -> 
          *MySingleton.instance = new MySingleton() 
          MySingleton.instance* 

que la lógica es incorrecta según el compilador ...

 if Helper.notExists MySingleton.instance then 
      MySingleton.instance <- Some(new MySingleton())   
     MySingleton.instance 

debo utilizar las instrucciones IF en lugar? ¿Hay un patrón preferido para esta sintaxis en f #?

+1

seria comentario en esta ocasión . Solo para asegurarse de que está pidiendo ayuda sobre lo correcto, un singleton es una clase especial diseñada que puede intentar crear tantas instancias como desee, pero después de la primera, cada vez que se le da la primera instancia. así que si trataste de crear una matriz de singletons, simplemente obtienes una matriz de ese mismo objeto. Después de leer su pregunta, es difícil saber si realmente quiere este comportamiento. – thecoshman

+0

Sí, lo hago; El singleton contendrá un gran caché de datos y proporcionará acceso a los datos cuando sea necesario sin que cada uso de la clase necesite actualizar/recuperar los datos (lo cual es un proceso largo). – akaphenom

Respuesta

4

El tipo Lazy como Brian mencionó es una buen lugar para comenzar. Le permite garantizar que se ejecutará un cálculo cuando se necesita el valor y garantiza la seguridad del hilo, lo que significa que el cálculo se ejecutará solo una vez (aunque, en algunos casos, también puede usar PublicationOnly option para especificar que varios hilos pueden comenzar a inicializar el caché y solo se usará el primer resultado).

Sin embargo, es probable que también necesite un mecanismo para marcar la memoria caché como no válida (por ejemplo, después de un tiempo específico) y forzar la reinicialización de la memoria caché. Tenga en cuenta que esto no es realmente un Singleton patrón. De todos modos, todavía se puede hacer esto de una manera segura usando hilo Lazy, pero tendrá que estructurar el código como el siguiente:

module Cache = 
    // returns a lazy value that initializes the cache when 
    // accessed for the first time (safely) 
    let private createCacheInitialization() = 
    lazy(// some code to calculate cache 
      cache) 
    // current cache represented as lazy value 
    let mutable private currentCache = createCacheInitialization() 

    // Returns the current cache 
    let GetCache() = currentCache.Value 
    // Reset - cache will be re-initialized next time it is accessed 
    // (this doesn't actually initialize a cache - just creates a lazy value) 
    let Reset() = currentCache <- createCacheInitialization() 

Por supuesto, se podría convertir el código en una clase Cache que toma sólo el función de inicialización y encapsula el resto del código en una pieza reutilizable (si necesita almacenar en caché varios valores, por ejemplo).

14

Tanto .NET 4.0 y F # Lazy tienen, por lo que creo que quieres

module MySingleton = 
    let private x = Lazy.Create(fun() -> 42) 
    let GetInstance() = x.Value 

(donde 42 podría haber una new WhateverType() o lo que sea la inicialización es caro).

http://msdn.microsoft.com/en-us/library/dd997286.aspx

(Comentario: Es 2010, y conseguir raro tener que tratar explícitamente con las primitivas de sincronización; lenguas y las bibliotecas se engloba todo los patrones comunes).

+0

¿Hay alguna diferencia entre 'Lazy.Create (fun() -> 42)' y 'lazy (42)' o solo tienen una sintaxis diferente para la misma cosa? Si '42' fuera una llamada de constructor, ¿se retrasaría en ambas versiones? –

+0

Lo mismo; perezosa (expr) significa Lazy.Create (diversión() -> expr) – Brian

+1

@ Brian - ¿el desarrollo básico de F # no es básicamente un singleton? En serio, lo asignas una vez y luego lees de ese valor una y otra vez. No estoy seguro de entender la diferencia entre esto y el patrón singleton. –

6

La pregunta era cómo implementar el patrón Singleton, no cómo implementar el patrón-Lazy Load. Un singleton se puede implementar de forma segura de varias maneras, p. Ej.:

// Standard approach in F# 2.0: using an implicit constructor. 
type Singleton private() = 
    static let instance = new Singleton() 
    static member Instance = instance 

// Abbreviated approach in F# 3.0: using an implicit constructor with auto property. 
type Singleton private() = 
    static member val Instance = Singleton() 

// Alternative example: When you have to use an explicit ctor, 
// and also want to check instanciation upon each access of the property. 

/// This type is intended for private use within Singleton only. 
type private SyncRoot = class end 

type Singleton = 
    [<DefaultValue>] 
    static val mutable private instance: Singleton 

    private new() = { } 

    static member Instance = 
     lock typeof<SyncRoot> (fun() -> 
      if box Singleton.instance = null then 
       Singleton.instance <- Singleton()) 
     Singleton.instance  

Editar
añadido un simplificada F # 2.0 ejemplo, con Héctor implícita privada, y el ejemplo con Héctor explícita ahora utiliza un tipo privado por separado como root sincronización. Gracias a kvb por las pistas.

Editar 2 Añadida la sintaxis de propiedad de auto F # 3.0.

+2

1. Puede hacer que un constructor predeterminado sea privado (como en, 'escriba PersonSingleton private() = ...'), en cuyo caso puede usar 'static let' dentro del tipo. 2. No bloquee en una instancia de tipo; porque el tipo es público, el otro código también podría bloquearse, provocando un interbloqueo. – kvb

+0

Gracias por nunca borrar esta respuesta. ¡El modificador de acceso privado para un constructor implícito era exactamente la pieza que me faltaba! – JDB

+0

¿Cuál es la razón por la que encasilla la instancia Singleton aquí? – Snake

9

Lo sentimos para reanimar una vieja pregunta, sólo quería señalar que algunos podrían tratar de exponer Instance en una propiedad pública, en cuyo caso el siguiente fragmento de código podría ser útil:

type MyType() = 
    inherit SomeParent() 

    static let mutable instance = lazy(new MyType()) 
    static member Instance with get() = instance.Value 
+2

Y, el patrón singleton debería tener un constructor privado, entonces: 'type public MyType private() =' –