Как реализовать шаблон Singleton (синтаксис) - PullRequest
9 голосов
/ 22 апреля 2010

У меня есть кеш данных, который обновляется из внешнего источника, и я хочу ограничить свой доступ к его кешу (только для чтения) внутри моего приложения. Я не хочу обновлять источник данных каждый раз, когда мне нужен доступ к нему (т. Е. При создании экземпляра и извлекать все необходимые данные, так как существует довольно много данных, которые обновляются).

type MySingleton = 

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

        static member GetInstance() = 
            instance

Полагаю, это одна из проблем, связанных с реализацией проекта и попыткой выучить язык одновременно. Я знаю, что логика должна быть

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

но отсутствие нуля бросает меня в тупик. Я думаю, что я могу использовать тип опции и т. Д., Но это бросает меня за цикл

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*

эта логика неверна согласно компилятору ...

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

я должен вместо этого использовать операторы IF? Есть ли предпочтительный шаблон для этого синтаксиса в f #?

Ответы [ 4 ]

14 голосов
/ 22 апреля 2010

Как .NET 4.0, так и F # имеют Lazy, поэтому я думаю, что вы хотите

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

(где 42 может быть new WhateverType() или любой дорогой инициализацией).

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

(Комментарий: Сейчас 2010 год, и редко приходится явно иметь дело с примитивами синхронизации; языки и библиотеки инкапсулируют все распространенные шаблоны.)

9 голосов
/ 18 октября 2010

Извините, что реанимировал старый вопрос, просто хотел отметить, что некоторые могут попытаться выставить Instance в публичном свойстве, и в этом случае может пригодиться следующий фрагмент кода:

// private constructor, just as any OO singleton
type public MyType private() =
  inherit SomeParent()

  static let mutable instance = lazy(new MyType())

  // Get the instance of the type
  static member Instance with get() = instance.Value
7 голосов
/ 29 сентября 2010

Вопрос был , как реализовать шаблон Singleton , а не , как реализовать шаблон Lazy-Load . Синглтон может быть реализован потокобезопасным несколькими способами, например:

// 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    

Редактировать
Добавлен упрощенный пример F # 2.0 с закрытым неявным ctor, а в примере с явным ctor теперь используется отдельный закрытый тип в качестве корневого элемента синхронизации. Спасибо kvb за подсказки.

Редактировать 2 Добавлен синтаксис автоматического свойства F # 3.0.

4 голосов
/ 22 апреля 2010

Тип Lazy, о котором говорил Брайан, - хорошее место для начала.Это позволяет вам гарантировать, что вычисление будет выполнено, когда необходимо значение, и гарантирует безопасность потока, что означает, что вычисление будет выполнено только один раз (хотя в некоторых случаях вы также можете использовать PublicationOnly option чтобы указать, что несколько потоков могут начать инициализировать кеш, и будет использоваться только первый результат).

Однако вам, вероятно, также понадобится механизм для пометки кеша как недействительного (например, через некоторое время)и принудительная повторная инициализация кеша.Обратите внимание, что на самом деле это не шаблон Singleton .В любом случае, вы все равно можете сделать это потокобезопасным способом, используя Lazy, но вам нужно будет структурировать код следующим образом:

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()

Конечно, вы можете превратить этот код в Cache класс, который принимает только функцию инициализации и инкапсулирует остальную часть кода в повторно используемый фрагмент (например, если вам необходимо кэшировать несколько значений).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...