web-dev-qa-db-ja.com

静的メソッド継承の正しい代替手段は何ですか?

静的メソッドの継承はC#ではサポートされていないことを理解しています。また、開発者がこの機能の必要性を主張する多くの議論(ここを含む)を読んでおり、典型的な対応は「静的メンバーの継承が必要な場合、設計に欠陥があります」です。

わかりました、OOPは静的継承について考えることさえ望まないので、それに対する私の明らかな必要性は私の設計のエラーを指していると結論付けなければなりません。しかし、私は立ち往生しています。これを解決するための助けをいただければ幸いです。ここに課題があります...

いくつかの複雑な初期化コードをカプセル化する抽象基本クラス(フルーツと呼びましょう)を作成します。一部のコードは仮想メソッド呼び出しに依存するため、このコードをコンストラクターに配置することはできません。

フルーツは他の具象クラス(Apple、Orange)に継承され、各クラスはインスタンスを作成および初期化するために標準ファクトリメソッドCreateInstance()を公開する必要があります。

静的メンバーの継承が可能であれば、ファクトリメソッドを基本クラスに配置し、派生クラスへの仮想メソッド呼び出しを使用して、具体的なインスタンスを初期化する必要がある型を取得します。クライアントコードはApple.CreateInstance()を呼び出して、完全に初期化されたAppleインスタンスを取得します。

しかし、明らかにこれは不可能です。だから誰かが同じ機能に対応するために私のデザインをどのように変更する必要があるか説明してください。

78
Tim Coulter

一つのアイデア:

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() { ... }
}

そして、次のように呼び出します:

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

追加のファクトリクラスは必要ありません。

57
Matt Hamsmith

ファクトリメソッドを型から外し、独自の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();
     }
}

しかし、私がこれを見ていると、Createメソッドを独自のFactoryクラスに移動する必要はありません(懸念の分離が望ましいと思いますが)、それをFruitクラスに入れることができます:

public abstract class Fruit
{

   public abstract string Define();

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

}

そして、それが機能するかどうかを確認するには:

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

            Console.ReadLine ();
        }        
    }
16

Createメソッドでファクトリクラス(テンプレート)を作成してみませんか?

FruitFactory<Banana>.Create();
4

私はこのようなことをします

 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()
4
Bob

.NET BCLの WebRequest クラスとその派生型は、この種の設計を比較的うまく実装できる良い例を示しています。

WebRequestクラスには、HttpWebRequestFtpWebReuestなど、いくつかのサブクラスがあります。現在、このWebRequest基本クラスもファクトリータイプであり、静的な Create メソッドを公開しています(ファクトリーパターンの要求に応じて、インスタンスコンストラクターは非表示になっています)。

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

このCreateメソッドは、WebRequestクラスの特定の実装を返し、URI(またはURI文字列)を使用して、作成して返すオブジェクトのタイプを決定します。

これにより、次の使用パターンの最終結果が得られます。

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 Frameworkクリエーターの好まれる方法のようです。

3
Noldorin

まず、仮想化できる静的イニシャライザがないからといって、オーバーロードされる可能性のある「標準」メンバーメソッドを持たないというわけではありません。第二に、コンストラクターから仮想メソッドを呼び出すことができ、期待どおりに動作するため、ここでは問題ありません。第三に、ジェネリックを使用してタイプセーフファクトリを作成できます。
ここに、コンストラクターによって呼び出されるファクトリー+メンバーInitialize()メソッドを使用するコードを示します(保護されているため、誰かがオブジェクトを作成した後に再び呼び出されることを心配する必要はありません)。


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());
    }
}
3
Marcin Deptuła

最良の方法は、フルーツクラスで仮想/抽象のInitializeメソッドを作成し、それを呼び出す必要があることと、外部の「フルーツファクトリ」クラスを作成してインスタンスを作成することです。


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;
    }
}
0
Lee