What's the correct alternative to static method inheritance?
我知道C中不支持静态方法继承。我还阅读了许多讨论(包括这里),其中开发人员声称需要此功能,对此,典型的回答是"如果需要静态成员继承,那么您的设计中就存在缺陷"。
好吧,考虑到OOP甚至不想让我考虑静态继承,我必须得出结论,我对它的明显需求指出了我的设计中的一个错误。但是,我被卡住了。我真的很感谢你帮我解决这个问题。这是挑战…
我想创建一个抽象的基类(我们称之为水果),它封装了一些复杂的初始化代码。此代码不能放置在构造函数中,因为其中一些代码将依赖于虚拟方法调用。
水果将由其他具体类(Apple、Orange)继承,每个类必须公开标准的工厂方法createInstance()以创建和初始化实例。
如果静态成员继承是可行的,我会将工厂方法放在基类中,并使用对派生类的虚拟方法调用来获取必须从中初始化具体实例的类型。客户端代码将简单地调用apple.createInstance()以获取完全初始化的apple实例。
但显然这是不可能的,所以有人能解释一下我的设计需要如何改变以适应相同的功能吗?
一个想法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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() { ... } } |
像这样叫:
1 | Apple myAppleVar = Fruit<Apple>.CreateInstance(); |
不需要额外的工厂课程。
将工厂方法移出类型,并将其放入自己的工厂类中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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(); } } |
但是,正如我所看到的,不需要将create方法移动到它自己的工厂类(尽管我认为它更可取——分离关注点),您可以将它放在fruit类中:
1 2 3 4 5 6 7 8 9 10 11 |
而且,为了看看它是否有效:
1 2 3 4 5 6 7 8 9 10 | class Program { static void Main( string[] args ) { Console.WriteLine (Fruit.CreateFruit<Apple> ().Define ()); Console.WriteLine (Fruit.CreateFruit<Orange> ().Define ()); Console.ReadLine (); } } |
我想做这样的事
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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() |
为什么不使用创建方法创建工厂类(模板化)?
1 | FruitFactory<Banana>.Create(); |
首先,没有可以是虚拟的静态初始值设定项并不意味着您不能有可以重载的"标准"成员方法。其次,您可以从构造函数调用虚拟方法,它们将按预期工作,所以这里没有问题。第三,您可以使用泛型来拥有类型安全工厂。下面是一些代码,它使用了由构造函数调用的factory+member initialize()方法(它是受保护的,因此您不必担心,在创建对象后会有人再次调用它):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 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()); } } |
.NET BCL中的
1 2 | public static WebRequest Create(string requestUriString) public static WebRequest Create(Uri requestUri) |
此
这是以下使用模式的最终结果:
1 2 3 4 5 6 7 | 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/"); |
我个人认为这是解决这个问题的一个好方法,而且它似乎确实是.NET框架创建者首选的方法。
我想说,最好的做法是在必须调用的Fruit类上创建一个虚拟/抽象初始化方法,然后创建一个外部的"Fruit Factory"类来创建实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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; } } |