静态方法继承的正确替代方法是什么?

我知道 C # 不支持静态方法继承。我还阅读了一些讨论(包括这里) ,其中开发人员声称需要这个功能,对此的典型反应是“如果您需要静态成员继承,那么您的设计中存在一个缺陷”。

好的,考虑到 OOP 甚至不希望我考虑静态继承,我必须得出结论,我对它的明显需求指向了我的设计中的一个错误。但是,我卡住了。如果你能帮我解决这个问题,我会很感激的。挑战来了。

我想创建一个抽象的基类(我们称之为 Fruit) ,它封装了一些复杂的初始化代码。此代码不能放置在构造函数中,因为其中一些代码将依赖于虚方法调用。

Fruit 将被其他具体类(Apple、 Orange)继承,每个类都必须公开一个标准工厂方法 CreateInstance ()来创建和初始化一个实例。

如果静态成员继承是可行的,我将把工厂方法放在基类中,并使用对派生类的虚方法调用来获取必须初始化具体实例的类型。客户机代码将简单地调用 Apple。CreateInstance ()获取完全初始化的 Apple 实例。

但显然这是不可能的,所以有人可以解释如何我的设计需要改变,以适应相同的功能。

50129 次浏览

I'd say the best thing to do is to create a virtual/abstract Initialise method on the fruit class which must be called and then create an external 'fruit factory' class to create instances:


public class Fruit
{
//other members...
public abstract void Initialise();
}


public class FruitFactory()
{
public Fruit CreateInstance()
{
Fruit f = //decide which fruit to create
f.Initialise();


return f;
}
}

Why not create a factory class (templated) with a create method?

FruitFactory<Banana>.Create();

Move the factory method out of the type, and put it in its own Factory class.

public abstract class Fruit
{
protected Fruit() {}


public abstract string Define();


}


public class Apple : Fruit
{
public Apple() {}


public override string Define()
{
return "Apple";
}
}


public class Orange : Fruit
{
public Orange() {}


public override string Define()
{
return "Orange";
}
}


public static class FruitFactory<T>
{
public static T CreateFruit<T>() where T : Fruit, new()
{
return new T();
}
}

But, as I'm looking at this, there is no need to move the Create method to its own Factory class (although I think that it is preferrable -separation of concerns-), you can put it in the Fruit class:

public abstract class Fruit
{


public abstract string Define();


public static T CreateFruit<T>() where T : Fruit, new()
{
return new T();
}


}

And, to see if it works:

    class Program
{
static void Main( string[] args )
{
Console.WriteLine (Fruit.CreateFruit<Apple> ().Define ());
Console.WriteLine (Fruit.CreateFruit<Orange> ().Define ());


Console.ReadLine ();
}
}

I would do something like this

 public abstract class Fruit() {
public abstract void Initialize();
}


public class Apple() : Fruit {
public override void Initialize() {


}
}


public class FruitFactory<T> where T : Fruit, new {
public static <T> CreateInstance<T>() {
T fruit = new T();
fruit.Initialize();
return fruit;
}
}




var fruit = FruitFactory<Apple>.CreateInstance()

The WebRequest class and its derivative types in the .NET BCL represent a good example of how this sort of design can be implemented relatively well.

The WebRequest class has several sub-classes, including HttpWebRequest and FtpWebReuest. Now, this WebRequest base class is also a factory type, and exposes a static Create method (the instance constructors are hidden, as required by the factory pattern).

public static WebRequest Create(string requestUriString)
public static WebRequest Create(Uri requestUri)

This Create method returns a specific implementation of the WebRequest class, and uses the URI (or URI string) to determine the type of object to create and return.

This has the end result of the following usage pattern:

var httpRequest = (HttpWebRequest)WebRequest.Create("http://stackoverflow.com/");
// or equivalently
var httpRequest = (HttpWebRequest)HttpWebWebRequest.Create("http://stackoverflow.com/");


var ftpRequest = (FtpWebRequest)WebRequest.Create("ftp://stackoverflow.com/");
// or equivalently
var ftpRequest = (FtpWebRequest)FtpWebWebRequest.Create("ftp://stackoverflow.com/");

I personally think this is a good way to approach the issue, and it does indeed seem to be the preffered method of the .NET Framework creators.

First of all, not having static initializers that can be virtual doesn't mean you can't have "standard" member methods, that could be overloaded. Second of all, you can call your virtual methods from constructors, and they will work as expected, so there's no problem here. Third of all, You can use generics to have type-safe factory.
Here's some code, that uses factory + member Initialize() method that is called by constructor (and it's protected, so you don't have to worry, that someone will call it again after creating an object):


abstract class Fruit
{
public Fruit()
{
Initialize();
}


protected virtual void Initialize()
{
Console.WriteLine("Fruit.Initialize");
}
}


class Apple : Fruit
{
public Apple()
: base()
{ }


protected override void Initialize()
{
base.Initialize();
Console.WriteLine("Apple.Initialize");
}


public override string ToString()
{
return "Apple";
}
}


class Orange : Fruit
{
public Orange()
: base()
{ }


protected override void Initialize()
{
base.Initialize();
Console.WriteLine("Orange.Initialize");
}


public override string ToString()
{
return "Orange";
}
}


class FruitFactory
{
public static T CreateFruit<T>() where T : Fruit, new()
{
return new T();
}
}


public class Program
{


static void Main()
{
Apple apple = FruitFactory.CreateFruit<Apple>();
Console.WriteLine(apple.ToString());


Orange orange = new Orange();
Console.WriteLine(orange.ToString());


Fruit appleFruit = FruitFactory.CreateFruit<Apple>();
Console.WriteLine(appleFruit.ToString());
}
}

One idea:

public abstract class Fruit<T>
where T : Fruit<T>, new()
{
public static T CreateInstance()
{
T newFruit = new T();
newFruit.Initialize();  // Calls Apple.Initialize
return newFruit;
}


protected abstract void Initialize();
}


public class Apple : Fruit<Apple>
{
protected override void Initialize() { ... }
}

And call like so:

Apple myAppleVar = Fruit<Apple>.CreateInstance();

No extra factory classes needed.

All these ideas compensate for a glaring lack of language with stilted language constructs.

If I want an apple, I don't call a method on the fruit. Why should I even learn the family tree of the class? And I don't want to repeat myself or copy constructors through the whole hierarchy like a madman. Computers were invented to save me from such nonsense.

Two languages that have noticed and are now doing it right are PHP and Swift. Both supported static inheritance and covariance.

And old holy OOP books with a supposedly pure doctrine you can burn, because everything that allows me elegant code and compiles is good.