强制子类中的单例模式

时间:2009-02-25 20:45:40

标签: c# design-patterns

我想强制子类实现单例模式。

我最初想过在父类中有一个抽象的静态属性,但是虽然接近但是没有意义(抽象需要和实例)。

接下来,我想到了一个带有静态属性的接口,但这也没有意义(接口也需要一个实例)。

这是可能的,还是我应该放弃这一思路并实施抽象工厂?

6 个答案:

答案 0 :(得分:7)

请重新考虑。你不想在这里使用单身人士。您正在为从您的课程派生的用户提供一些功能。没关系。但是你也在规定必须始终使用的一种特定方式,而且绝对没有理由。那不好。

在大多数情况下只对这个类的一个对象进行实例化是有意义的,但在这种情况下,只需要实例化一次对象。这并不是说你很可能在没有注意的情况下意外地实例化了十几个对象。 而且,你怎么知道有两个实例从不有用?我现在甚至可以想到几个案例。

单元测试:您可能希望每个测试都实例化此对象,然后再将其拆除。由于大多数人都有多个单元测试,因此您需要多次实例化它。

或者您可能在某个时候决定在游戏中拥有多个相同/相似的关卡,这意味着要创建多个实例。

单身人士给你两件事:

  • 保证不会实例化对象的一个​​实例,并且
  • 对该实例的全局访问

如果你不需要这两件事,那就有更好的选择。 您当然不需要全局访问。 (全局变量很糟糕,通常是设计不良的症状,特别是在游戏状态等可变数据中)

但是您不需要保证不会实例化多个实例。 如果我实例化对象两次,它是世界末日吗?应用程序会崩溃吗?如果是这样,您需要保证。 但在你的情况下,不会发生任何不好的事情。实例化对象的人仅使用比必要更多的内存。但他可能有理由。

简单地在类文档中添加这是一个非常庞大且昂贵的类,并且您不应该经常实例化它。问题解决了。您不会删除以后可能会变得有用的灵活性,您不会无缘无故地授予对数据的全局访问权限。因为您可以控制谁可以看到该对象,所以您不需要将其淹没在将成为多线程应用程序瓶颈的锁中。您没有分散在整个代码中的隐藏依赖项,这使得测试更加困难并且难以重用。

答案 1 :(得分:4)

尝试使用IOC容器。大多数优秀的IOC容器都可以使用单例模式而无需自己实现它(即:spring框架) - 我比强制使用静态GetInstance()方法更喜欢它。

此外,它在Java中并不可行,但它可以在带有模板的C ++中工作。

答案 2 :(得分:1)

为什么呢?如果有人想要使用你的类的子类的多个实例,他们可能有完全正确的理由。

如果你想做一些只为你的类子类化的每个类做一次的事情(为什么,我不知道,但你可能有理由),在基类中使用一个Dictionary。

答案 3 :(得分:0)

我会定义一个密封的类,它从传递给构造函数的委托中获取其功能,如下所示:

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);
}

答案 4 :(得分:0)

我认为你的工厂模式会更好,老实说。或者像Brian Dilley推荐的那样使用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没有正确实现系统,但即使这个类也无法实例化两次。第二次实例化会导致异常。

答案 5 :(得分:0)

虽然这不会强制用户拥有单例子类,但您可以强制用户只创建一个类(或其子类)的实例,如下所示。如果创建了任何子类的第二个实例,这将抛出错误。

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