Rozumiem, że dziedziczenie metod statycznych nie jest obsługiwane w języku C #. Przeczytałem również kilka dyskusji (w tym tutaj), w których programiści twierdzą, że potrzebują tej funkcjonalności, na co typową odpowiedzią jest „jeśli potrzebujesz dziedziczenia statycznego elementu członkowskiego, w Twoim projekcie jest błąd”.
OK, biorąc pod uwagę, że OOP nie chce, żebym nawet myślał o dziedziczeniu statycznym, muszę stwierdzić, że moja widoczna potrzeba tego wskazuje na błąd w moim projekcie. Ale utknąłem. Naprawdę byłbym wdzięczny za pomoc w rozwiązaniu tego problemu. Oto wyzwanie ...
Chcę utworzyć abstrakcyjną klasę bazową (nazwijmy ją Fruit), która zawiera jakiś złożony kod inicjujący. Tego kodu nie można umieścić w konstruktorze, ponieważ część z nich będzie polegać na wywołaniach metod wirtualnych.
Owoce zostaną odziedziczone przez inne konkretne klasy (Apple, Orange), z których każda musi udostępniać standardową fabryczną metodę CreateInstance (), aby utworzyć i zainicjować instancję.
Gdyby dziedziczenie elementów statycznych było możliwe, umieściłbym metodę fabryczną w klasie bazowej i użyłbym wywołania metody wirtualnej do klasy pochodnej, aby uzyskać typ, z którego należy zainicjować konkretne wystąpienie. Kod klienta po prostu wywołałby Apple.CreateInstance () w celu uzyskania w pełni zainicjowanej instancji Apple.
Ale najwyraźniej nie jest to możliwe, więc czy ktoś może wyjaśnić, jak powinien zmienić się mój projekt, aby uwzględnić tę samą funkcjonalność.
źródło
Odpowiedzi:
Jeden pomysł:
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() { ... } }
I zadzwoń tak:
Nie są potrzebne żadne dodatkowe klasy fabryczne.
źródło
Apple
konstruktora mniejszym niż publicznym, ponieważwhere T : Fruit<T>, new()
dyktuje, żeT
musi mieć konstruktora publicznego. @Matt Hamsmith - Twój kod nie kompiluje się, dopóki nie usunieszprotected Apple() { }
. Przetestowałem to już w VS.Przenieś metodę fabryki poza typ i umieść ją w jej własnej klasie Factory.
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(); } }
Ale, kiedy się temu przyjrzę, nie ma potrzeby przenoszenia metody Create do jej własnej klasy Factory (chociaż myślę, że jest to preferowane - oddzielenie obaw -), możesz umieścić ją w klasie Fruit:
public abstract class Fruit { public abstract string Define(); public static T CreateFruit<T>() where T : Fruit, new() { return new T(); } }
Aby sprawdzić, czy to działa:
class Program { static void Main( string[] args ) { Console.WriteLine (Fruit.CreateFruit<Apple> ().Define ()); Console.WriteLine (Fruit.CreateFruit<Orange> ().Define ()); Console.ReadLine (); } }
źródło
Dlaczego nie utworzyć klasy fabrycznej (na podstawie szablonu) za pomocą metody tworzenia?
źródło
Zrobiłbym coś takiego
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()
źródło
WebRequest
Klasa i jej rodzaje instrumentów pochodnych w .NET BCL stanowią dobry przykład tego, jak tego rodzaju konstrukcji może być realizowane stosunkowo dobrze.WebRequest
Klasa ma kilka podklas, w tymHttpWebRequest
iFtpWebReuest
. Teraz taWebRequest
klasa bazowa jest również typem fabrycznym i uwidaczniaCreate
metodę statyczną (konstruktory instancji są ukryte, zgodnie z wymaganiami wzorca fabryki).public static WebRequest Create(string requestUriString) public static WebRequest Create(Uri requestUri)
Ta
Create
metoda zwraca określoną implementacjęWebRequest
klasy i używa identyfikatora URI (lub ciągu identyfikatora URI) do określenia typu obiektu do utworzenia i zwrócenia.Daje to końcowy wynik następującego wzorca użycia:
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/");
Osobiście uważam, że jest to dobry sposób podejścia do problemu i rzeczywiście wydaje się, że jest to preferowana metoda twórców .NET Framework.
źródło
Po pierwsze, brak statycznych inicjatorów, które mogą być wirtualne, nie oznacza, że nie można mieć „standardowych” metod składowych, które mogłyby zostać przeciążone. Po drugie, możesz wywołać swoje metody wirtualne z konstruktorów i będą one działać zgodnie z oczekiwaniami, więc nie ma tutaj problemu. Po trzecie, możesz użyć typów generycznych, aby mieć fabrykę bezpieczną dla typów.
Oto kod wykorzystujący metodę factory + member Initialize (), która jest wywoływana przez konstruktora (i jest chroniona, więc nie musisz się martwić, że ktoś wywoła go ponownie po utworzeniu obiektu):
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()); } }
źródło
Powiedziałbym, że najlepiej jest stworzyć wirtualną / abstrakcyjną metodę Initialise na klasie owoców, którą należy wywołać, a następnie utworzyć zewnętrzną klasę „fabryki owoców” w celu utworzenia instancji:
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; } }
źródło