Принудительный шаблон синглтона в подклассах - PullRequest
8 голосов
/ 25 февраля 2009

Я хотел бы заставить подклассы реализовать шаблон синглтона.

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

Затем я подумал о наличии интерфейса со статическим свойством, но это также не имеет смысла (для интерфейсов также требуется экземпляр).

Это что-то, что возможно, или я должен оставить этот ход мыслей и реализовать абстрактную фабрику?

Ответы [ 6 ]

7 голосов
/ 27 февраля 2009

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

Может иметь смысл создавать экземпляр только одного объекта этого класса большую часть времени, но в этом случае просто просто создает экземпляр объекта один раз . Не похоже, что вы случайно создадите десятки объектов, не заметив этого. Кроме того, как вы можете сказать, что наличие двух экземпляров будет НИКОГДА полезным? Я могу вспомнить несколько случаев даже сейчас.

Модульное тестирование: вы можете захотеть, чтобы каждый тест создавал экземпляр этого объекта, а затем снова разрушал его. А поскольку у большинства людей есть более одного модульного теста, вам нужно будет выполнить его несколько раз.

Или, возможно, в какой-то момент вы решите иметь несколько идентичных / похожих уровней в вашей игре, что означает создание нескольких экземпляров.

Синглтон дает вам две вещи:

  • Гарантия того, что будет создан только один экземпляр объекта, и
  • Глобальный доступ к этому экземпляру

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

Но вам не нужна гарантия того, что будет создано не более одного экземпляра. Конец света, если я дважды создаю объект? Будет ли сбой приложения? Если это так, вам нужна эта гарантия. Но в вашем случае ничего плохого не произойдет. Человек, создающий объект, просто использует больше памяти, чем необходимо. Но у него может быть причина.

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

4 голосов
/ 25 февраля 2009

Попробуйте использовать контейнер IOC. Большинство хороших IOC-контейнеров позволяют использовать шаблон синглтона без необходимости его самостоятельной реализации (т. Е. Spring Framework) - мне это нравится больше, чем принудительное использование статического метода GetInstance ()

Кроме того, это не совсем возможно в Java, хотя это будет работать в C ++ с шаблонами.

1 голос
/ 26 февраля 2009

Почему? Если кто-то хочет использовать несколько экземпляров подкласса вашего класса, у него может быть вполне веская причина.

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

0 голосов
/ 22 сентября 2016

Хотя это не заставит пользователя иметь одноэлементный подкласс, вы можете заставить пользователя создавать только один экземпляр класса (или его подклассы), как показано ниже. Это вызовет ошибку, если будет создан второй экземпляр любого подкласса.

public abstract class SuperClass {
private static SuperClass superClassInst = null;

public SuperClass () {
    if(superClassInst == null) {
        superClassInst = this;
    }
    else {
        throw new Error("You can only create one instance of this SuperClass or its sub-classes");
    }
}

public final static SuperClass getInstance() {
    return superClassInst;
}

public abstract int Method1();
public abstract void Method2();
}
0 голосов
/ 26 февраля 2009

Я думаю, вам будет лучше с заводским образцом, если честно. Или используйте инструмент IoC, как рекомендует Брайан Дилли. В мире c # есть нагрузки, вот самые популярные: Castle / windsor, StructureMap, Unity, Ninject.

Кроме того, я подумал, что было бы забавно попытаться решить вашу проблему! Посмотрите на это:

//abstract, no one can create me
public abstract class Room
{
    protected static List<Room> createdRooms = new List<Room>();
    private static List<Type> createdTypes = new List<Type>();

    //bass class ctor will throw an exception if the type is already created
    protected Room(Type RoomCreated)
    {
        //confirm this type has not been created already
        if (createdTypes.Exists(x => x == RoomCreated))
            throw new Exception("Can't create another type of " + RoomCreated.Name);
        createdTypes.Add(RoomCreated);
    }

    //returns a room if a room of its type is already created
    protected static T GetAlreadyCreatedRoom<T>() where T : Room
    {
        return createdRooms.Find(x => x.GetType() == typeof (T)) as T;
    }
}

public class WickedRoom : Room
{
    //private ctor, no-one can create me, but me!
    private WickedRoom()
        : base(typeof(WickedRoom)) //forced to call down to the abstract ctor
    {

    }

    public static WickedRoom GetWickedRoom()
    {
        WickedRoom result = GetAlreadyCreatedRoom<WickedRoom>();

        if (result == null)
        {
            //create a room, and store
            result = new WickedRoom();
            createdRooms.Add(result);
        }

        return result;
    }
}

public class NaughtyRoom :Room
{
    //allows direct creation but forced to call down anyway
    public NaughtyRoom() : base(typeof(NaughtyRoom))
    {

    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        //Can't do this as wont compile
        //WickedRoom room = new WickedRoom();

        //have to use the factory method:
        WickedRoom room1 = WickedRoom.GetWickedRoom();
        WickedRoom room2 = WickedRoom.GetWickedRoom();

        //actually the same room
        Debug.Assert(room1 == room2);

        NaughtyRoom room3 = new NaughtyRoom(); //Allowed, just this once!
        NaughtyRoom room4 = new NaughtyRoom(); //exception, can't create another
    }
}

WickedRoom - это класс, который правильно реализует систему. Любой клиентский код получит одноэлементный класс WickedRoom. NaughtyRoom не реализует систему должным образом, но даже этот класс не может быть создан дважды. Второе создание приводит к исключению.

0 голосов
/ 26 февраля 2009

Я бы определил запечатанный класс, который получает свою функциональность от делегатов, переданных в конструктор, что-то вроде этого:

public sealed class Shape {
  private readonly Func<int> areaFunction;

  public Shape(Func<int> areaFunction) { this.areaFunction = areaFunction; }

  public int Area { get { return areaFunction(); } }
}

Этот пример не имеет большого смысла, он просто иллюстрирует шаблон. Такой шаблон нельзя использовать везде, но иногда это помогает.

Кроме того, его можно расширить, чтобы выставить конечное число статических полей:

public sealed class Shape {
  private readonly Func<int> areaFunction;

  private Shape(Func<int> areaFunction) { this.areaFunction = areaFunction; }

  public int Area { get { return areaFunction(); } }

  public static readonly Shape Rectangle = new Shape(() => 2 * 3);
  public static readonly Shape Circle = new Shape(() => Math.Pi * 3 * 3);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...