Моностат против Синглтона

Каковы сценарии, когда для поддержки глобального объекта можно использовать шаблон Monostate вместо одноэлементного порядка?

Редактировать: я знаю, что такое шаблоны Singleton и Monostate. Также реализовали Синглтон в нескольких сценариях. Просто хочу знать сценарии (примеры случаев), в которых должен быть реализован шаблон MonoState.

Например, Мне нужно поддерживать список столбцов на экране в моем приложении Windows Forms. Я мог бы использовать словарь Singleton в этом случае. Однако я храню список в статической глобальной переменной и хотел предоставить индексаторы (поскольку мне нужно динамически добавлять новую запись в список, если ключа нет), где я мог бы указать ScreenDetails.ScreenName в качестве ключа и получить ScreenDetails.ColumnsTable. Поскольку индексаторы не могут работать со статическим классом, я изменил шаблон на Monostate.

Поэтому я хотел бы знать, какие другие сценарии могут заставить пользователя использовать Monostate вместо Singletons.

6 ответов

Решение

В его основе Monostate - просто синтаксический сахар вокруг Синглтона. Monostate становится интересным, когда вы начинаете создавать подклассы, потому что подклассы могут украшать общее состояние с другим поведением.

Простой - хотя и несколько надуманный и не очень эффективный:) - пример:

public class GlobalTable implements Iterable<Key> {

  /** Shared state -- private */    
  private static final Map<Key, Value> MAP = new LinkedHashMap<Key, Value>();

  /** Public final accessor */    
  public final Value get(Key key) {
    return MAP.get(key);
  }

  /** Public final accessor */    
  public final boolean put(Key key, Value value) {
    return MAP.put(key);
  }

  /** Protected final accessor -- subclasses can use this to access
      the internal shared state */    
  protected final Set<Key> keySet() {
    return MAP.keySet();
  }

  /** Virtual -- subclasses can override for different behavior */    
  public Iterator<Key> iterator() {
    return Collections.unmodifiableSet(MAP.keySet()).iterator();
  }
}

А что если нам нужен индексированный доступ?

public class IndexedGlobalTable extends GlobalTable {

  public List<Key> getKeysAsList() {
    return Collections.unmodifiableList(new ArrayList<Key>(keySet()));
  }

  public Key getKeyAt(int index) {
    return getKeysAsList().get(index);
  }

  public Value getValueAt(int index) {
    return get(getKeyAt(index));
  }
}

Как насчет отсортированных ключей?

public class SortedGlobalTable extends GlobalTable {

  @Override
  public Iterator <Key> iterator() {
    return Collections
      .unmodifiableSortedSet(new TreeSet<Key>(keySet())).iterator();
  }

}

Каждый раз, когда вам нужно одно или другое представление данных, вы просто создаете экземпляр соответствующего подкласса.

Конечно, вопрос о том, является ли глобальные данные действительно хорошей идеей, это другой вопрос, но, по крайней мере, Monostate дает вам больше гибкости в том, как вы их используете.

monostate и singleton - это два лица одной медали (глобальное состояние):

  • monostate вызывает поведение (только одно значение для всех экземпляров класса)
  • синглтон вызывает структурное ограничение (только один случай)

использование синглтона не прозрачно

то есть:

Singleton singleton = Singleton.getInstance();

использование моностата прозрачно

то есть:

MonoState m1 = new MonoState();
MonoState m2 = new MonoState(); // same state of m1 

Вот что Роберт С. Мартин должен сказать по этому поводу: Синглтон против Моностата (pdf)

SINGLETON лучше всего использовать, когда у вас есть существующий класс, который вы хотите ограничить с помощью деривации, и вы не возражаете, что всем придется вызывать метод instance() для получения доступа. Monostate лучше всего использовать, когда вы хотите, чтобы особая природа класса была прозрачной для пользователей, или когда вы хотите использовать полиморфные производные одного объекта.

Кто-то должен просто отметить, что синглтоны и моностаты - чрезвычайно опасные паттерны. Их часто используют ленивые программисты, которые не хотят думать о времени жизни объекта, который они хотят превратить в синглтон. Они усложняют тестирование и создают жесткие системы, которые тесно связаны.

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

Много было написано об этом:

Разница между этими двумя паттернами заключается в поведении и структуре. Шаблон SINGLETON обеспечивает структуру сингулярности. Это предотвращает создание более одного экземпляра. Принимая во внимание, что MONOSTATE обеспечивает поведение единственного числа без наложения структурных ограничений.

Преимущества SINGLETON

  • Применимо к любому классу. Вы можете изменить любой класс на SINGLETON, просто сделав его конструкторы приватными и добавив соответствующие статические функции и переменные.
  • Может быть создан путем деривации. Для данного класса вы можете создать подкласс SINGLETON.
  • Ленивая оценка. Если SINGLETON никогда не используется, он никогда не создается.

Стоимость SINGLETON

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

  • Не наследуется. Класс, производный от SINGLETON, не является синглтоном. Если это должен быть SINGLETON, к нему необходимо добавить статическую функцию и переменную.

  • Эффективность. Каждый вызов Instance вызывает оператор if. Для большинства этих вызовов оператор if бесполезен.

  • Непрозрачные. Пользователи SINGLETON знают, что они используют SINGLETON, потому что они должны вызывать метод Instance.

Преимущества МОНОСТАТА

  • Прозрачность. Пользователи MONOSTATE ведут себя не так, как пользователи обычного объекта. Пользователям не нужно знать, что объект является MONOSTATE.

  • Порождаемость. Производные моностата являются моностатами. Действительно, все производные моностата являются частью одного и того же моностата. Все они имеют одни и те же статические переменные.

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

  • Четко определенные создание и разрушение. Переменные MONOSTATE, будучи статичными, имеют четко определенные времена создания и уничтожения.

Расходы на МОНОСТАТ

  • Нет конверсии. Обычный класс не может быть преобразован в класс MONOSTATE с помощью деривации.

  • Эффективность. МОНОСТАТ может пройти через множество творений и разрушений, потому что это реальный объект. Эти операции часто являются дорогостоящими.

  • Присутствие. Переменные MONOSTATE занимают место, даже если MONOSTATE никогда не используется.

Гибкая разработка программного обеспечения, принципы, шаблоны и практики Роберт К. Мартин

Я думаю, что у большинства людей неправильное понимание шаблона моносостояния.

В эти дни я прочитал страницы о теории категорий. В теории категорий одноэлементное множество определяется как множество, состоящее только из одного элемента. Таким образом, у одноэлементного набора есть много хороших функций, например, вы можете создать элемент одноэлементного набора в любое время, и этот элемент всегда будет одним и тем же. На самом деле, это то, что должно означать моногосударство. Вы можете создать экземпляр моносостояния в любое время, и экземпляр всегда будет одним и тем же.

Однако действительно ли элемент тот же самый для нашего обычного использования моносостояния? Как мы знаем, элемент определяется своим интерфейсом, поэтому интерфейс одного и того же элемента всегда должен делать одно и то же и возвращать один и тот же результат. Таким образом, хотя у моносостояния нет нестатического члена, они не совпадают. У него вообще нет хороших качеств.

Что такое моногосударство? На самом деле, хороший пример. Он использует глобальную уникальную кучу памяти, но определяет моносостояние, и все экземпляры имеют одинаковое поведение. Вы можете случайным образом создать экземпляр std::allocatorи использовать его.

Более того. Определяет ли обычный одноэлементный шаблон одноэлементный набор? Фактически, сам класс singleton не является набором singleton, но указатель ссылки набора singleton является набором singleton.)

обычное использование моносостояния и синглтона, о котором я говорил в этом ответе:

      class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance;
        return instance;
    }
private:
    ~Singleton() = default;
};

class MonoState {
public:
    void setValue(int v) {
        _v = v;
    }
    int getValue() {
        return _v;
        // getValue may return different result at different time.
    }

private:
    static inline int _v;
};
Другие вопросы по тегам