簡體   English   中英

我有一個基類。 如何為正確的派生類調用正確的方法?

[英]I have a base class. How do I call the right method for the right derived class?

顯然試圖在這里簡化問題。 我有一個基類和一些派生類:

public class Mammal { }

public class Cat : Mammal { } 

public class Dog : Mammal { }

和實用類:

public static class AnotherClass
{
    public static void GiveFood(Cat cat) {}
    public static void GiveFood(Dog dog) {}
}

在其他地方是一個方法,Feed,它需要一個哺乳動物,從那里我想在AnotherClass上調用正確的重載:

public void Feed(Mammal mammal) {
    // if mammal is a cat, call the AnotherClass.GiveFood overload for cat,
    // if it's a dog, call the AnotherClass.GiveFood for dog, etc.
}

一種方法是做類似的事情:

public void Feed(Mammal mammal) {
    if (mammal is dog) 
        AnotherClass.GiveFood((Dog)mammal);
    if (mammal is Cat) 
        AnotherClass.GiveFood((Cat)mammal);
}

......但實際上我有很多來自哺乳動物的動物。 有沒有更好的方法在Feed()中做我想做的事情? 有沒有什么方法可以避免Feed()最終成為一個巨大的丑陋方法填充這些“如果x是y然后調用z” - 陳述?

我通常不喜歡使用dynamic ,但這是我認為合適的情況之一:

public void Feed(Mammal mammal) {
  Anotherclass.GiveFood((dynamic)mammal);
}

這將在運行時解決正確的重載,而無需事先知道類型。

嚴格地說,這可能不是最快的方法,但正如你所指出的,替代方案可能是維護和/或難以閱讀的真正痛苦。 在這種情況下,動態調度是優雅的,並將自動合並您將來添加的任何重載。

正如Chris Sinclair指出的那樣,如果找不到匹配的GiveFood()重載,你還可以添加一個catchall方法來檢測任何無效調用,並提供比你收到的運行時錯誤更友好的異常:

public static class AnotherClass
{
  public static void GiveFood(Cat cat) {}
  public static void GiveFood(Dog dog) {}

  public static void GiveFood(Mammal mammal)
  {
    throw new AnimalNotRecognizedException("I don't know how to feed a " + mammal.GetType().Name + ".");
  }
}

我認為處理食物是動物的責任,而不是飼養者。 否則你會遇到你現在遇到的問題:

public void Feed(Mammal mammal) {
    if (mammal is Duck) 
    {
        ((Duck)mammal).PryOpenBeak();
        ((Duck)mammal).InsertFeedingTube();
        ((Duck)mammal).PourDownFood();
    }
}

等等,雖然鴨子不是哺乳動物。

無論如何,你的Mammal類應該有一個抽象的方法Feed(Food food) ,動物本身將不得不弄清楚如何處理食物。 這樣,當后來添加一種新的哺乳動物時,您不必使用這種新哺乳動物的喂養邏輯來更新喂食器。

IFoodXEater的評論:然后動物可以實現包含Feed(IFoodX)方法的正確IFoodXEater接口,然后接收器可以查看它,盡管那時你回到了方形一:

if (mammal is IFishEater)
{
    ((IFishEater)mammal).Feed(new Fish());
}

如果您不介意創建類型映射的工作,您可以偽造雙重調度,如下所示:

[編輯]這個新的改進版本更好地處理子類。 如果你有一個派生自另一個哺乳動物類的類(例如下面例子中派生自Dog Pug ),那么你不需要為Pug類明確添加一個饋線 - 它會自動調用它的基類Dog的饋線。

但是,如果需要,您可以為派生類創建特定的進紙器,如下面的Manx類所示。

使用dynamic雖然容易得多! 我只是想展示一下如果你沒有使用dynamic

using System;
using System.Collections.Generic;

namespace Demo
{
    public class Mammal {}

    public class Cat: Mammal {}
    public class Pig: Mammal {}
    public class Dog: Mammal {}

    public class Pug:  Dog {}
    public class Manx: Cat {}

    public static class Feeder
    {
        static readonly Dictionary<Type, Action<Mammal>> map = createMap();

        static Dictionary<Type, Action<Mammal>> createMap()
        {
            return new Dictionary<Type, Action<Mammal>>
            {
                {typeof(Cat),  mammal => GiveFood((Cat)  mammal)},
                {typeof(Dog),  mammal => GiveFood((Dog)  mammal)},
                {typeof(Manx), mammal => GiveFood((Manx) mammal)}
            };
        }

        public static void GiveFood(Mammal mammal)
        {
            for (
                var currentType = mammal.GetType(); 
                typeof(Mammal).IsAssignableFrom(currentType);
                currentType = currentType.BaseType)
            {
                if (map.ContainsKey(currentType))
                {
                    map[currentType](mammal);
                    return;
                }
            }

            DefaultGiveFood(mammal);
        }

        public static void DefaultGiveFood(Mammal mammal)
        {
            Console.WriteLine("Feeding an unknown mammal.");
        }

        public static void GiveFood(Cat cat)
        {
            Console.WriteLine("Feeding the cat.");
        }

        public static void GiveFood(Manx cat)
        {
            Console.WriteLine("Feeding the Manx cat.");
        }

        public static void GiveFood(Dog dog)
        {
            Console.WriteLine("Feeding the dog.");
        }
    }

    class Program
    {
        void test()
        {
            feed(new Cat());
            feed(new Manx());
            feed(new Dog());
            feed(new Pug());
            feed(new Pig());
            feed(new Mammal());
        }

        void feed(Mammal mammal)
        {
            Feeder.GiveFood(mammal);
        }

        static void Main()
        {
            new Program().test();
        }
    }
}

我的建議:

第1步 :創建一個接口IMammal

<!-- language: c# -->
public interface IMammal
{
    void Feed();
}

第2步 :(可選)實現Base類BaseMammal

public class BaseMammal : IMammal
{
    public void Feed()
    {
        Trace.Write("basic mammal feeding");
        //a basic implementation of feeding, common to all or most mammals
    }
}

第3步 :實現繼承的類

public class Cat : BaseMammal
{
    public void Feed()
    {
        Trace.Write("cat feeding");
        BePicky();//some custom cat like functionality
        base.Feed(); //and afterwards its still just a mammal after all
    }
}

public class Gruffalo : BaseMammal
{
    public void Feed()
    {
        Trace.Write("Gruffalo feeding");
        WeirdWayOfEating();//the base implementation is not appropriate
    }
}

第4步 :使用! (包括隨機示例)

List<IMammal> pets = new List<IMammal>()
    {
        new Cat(catValues),
        new Gruffalo(gruffaloValues)
    };

foreach(var pet in pets)
{
    pet.Feed();
}

每只動物都將通過自己的實施方式喂養。 瞧,你的復雜代碼現在很簡單。 我還建議你閱讀“Head First Design Patterns”,它解釋了這個和許多其他概念。 http://www.amazon.co.uk/Head-First-Design-Patterns-Freeman/dp/0596007124

如果不止一只動物分享喂養行為,我建議使用策略模式將喂養行為封裝在界面中並具體實現每組動物的每種行為

你將使用組合而不是繼承

檢查頭部的第一個設計模式我認為這將是一個很好的實現在你的情況下

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM