【问题标题】:Non-Interface methods [closed]非接口方法[关闭]
【发布时间】:2017-02-15 19:13:31
【问题描述】:

我一直在阅读有关接口编程而不是实现的内容。我没有正确理解的一个领域是如何处理非接口方法。例如一个接口 IAnimal 和一个实现它的 Cat 类。我的示例是用 C# 编写的,但我认为它也应该适用于其他语言。

public interface IAnimal
{
    void Eat();

}

    public class Cat : IAnimal
    {

    public Cat()

    public void Eat()
    { 
         //Do something
    }


    public string Meow()
    {
        return "meow";
    }

}

从我读到的内容看来,我应该尝试使用接口而不是 cat 实现,例如,

Main()
{
    IAnimal cat = new Cat();
}

但这让我无法访问我的 meow 方法,因为它不是 IAnimal 接口的一部分。我应该创建另一个实现 IAnimals 的接口 ICat 并让 Cat 实现它吗?这是否意味着所有方法都应该是接口或抽象类的实现?还是我在这里做错了什么。

感谢您的帮助。

【问题讨论】:

  • 您能否在界面中添加一个更通用的方法,可能是string speak().?然后无论哪个类实现它都可以有自己的消息。
  • 完全不会说话的类鱼怎么办?
  • 然后它可以返回一个空字符串。或者是没有声音的指示器。
  • 或者会说话的动物可以实现ITalkingAnimal
  • 所以从 cmets 看来,您似乎是在提倡将所有方法包含在相关接口中,而不是像示例中那样留下任何方法?

标签: c# oop interface abstract-class


【解决方案1】:

您要做的是拥有另一个接口来表示会说话的动物,并且要么继承自 IAnimal,要么将其添加为第二个接口。有会说话的动物的类实现了第二个接口。

具有继承的接口。

public interface IAnimal
{
    void Eat();
}

public interface ISpeakingAnimal : IAnimal
{
    string Speak();
}

public class Cat : ISpeakingAnimal 
{
    public Cat()

    public void Eat()
    { 
         //Do something
    }


    public string Speak()
    {
        return "meow";
    }
}

public class Fish : IAnimal 
{
    public Fish()

    public void Eat()
    { 
         //Do something
    }
}

带有第二个装饰器接口

public interface IAnimal
{
    void Eat();
}

public interface ISpeakable
{
    string Speak();
}

public class Cat : IAnimal, ISpeakable
{
    public Cat()

    public void Eat()
    { 
         //Do something
    }

    public string Speak()
    {
        return "meow";
    }
}

public class Fish : IAnimal 
{
    public Fish()

    public void Eat()
    { 
         //Do something
    }
}

如果您需要的方法不是Speak(),而是Meow(),您可以使用显式接口实现仅通过该接口公开Speak() 方法。

public class Cat : ISpeakingAnimal 
{
    public Cat()

    public void Eat()
    { 
         //Do something
    }


    string ISpeakingAnimal.Speak()
    {
        return Meow();
    }

    public string Meow()
    {
        return "meow";
    }
}

【讨论】:

  • 是否有关于何时实现多个接口与链接接口的一般经验法则?
  • 取决于您打算如何使用它。对于您在问题中的示例,您将使用 interitance。如果你有像IEnumerable<string> MakeThemSpeak(IEnumerable<...> obj) 这样的代码,你可能会使用ISpeakable,所以如果你有可以说话但不是动物的东西可以使用它(例如,也许你也有IRobot)。
  • 好的,然后确认您是说所有方法都应该从某个接口实现?
  • 你遵循一个有意义的模式,有些地方没有意义。例如,一个有属性但没有方法的 DTO 对象没有接口是没有意义的。
【解决方案2】:

接口的意义在于定义实现该接口的类的通用行为。您注意到这样定义 cat 是正确的:

    IAnimal cat = new Cat();

使您无法访问 Cat 类中不在 IAnimal 中的方法。那么为什么鼓励以这种方式实现事物呢?

答案很简单:它使以后更改代码变得非常容易。例如,如果我们有一个实现 IAnimal 的 Dog 类,如下所示:

    public class Dog : IAnimal
    {
        // some methods
    }

然后我们可以很容易地将我们的 Cat 类替换为 Dog 类,而无需更改任何其他代码。换句话说,我们可以替换:

    IAnimal cat = new Cat();

    IAnimal dog = new Dog();

无需更改整个程序中的任何其他代码(变量名除外)。这是因为根据 IAnimal 定义 Cat 和 Dog 会强制它们仅使用 IAnimal 中的方法,尽管它们在 Cat 和 Dog 中的实现方式可能不同。

当然,如果您只想使用特定于 Cat 或 Dog 的东西,则必须明确定义该类,正如 @Erick 在他的回答中提到的那样,如下所示:

    Cat cat = new Cat();

通常,您应该尝试在接口中定义尽可能多的常见行为,仅在绝对必要时才显式转换为特定类,例如 Cat 或 Dog。这使您的代码更加通用和多变。

【讨论】:

    【解决方案3】:

    如果您需要访问该方法,则需要进行显式强制转换。

    在这种情况下,让你的 Meow() 方法对其他可能实现它的类更通用会更有趣:

    public interface IAnimal
    {
        void Eat();
        void Speak();
    }
    
    public class Cat : IAnimal
    {  
        public void Eat() {  }
    
        public string Speak()
        {
            return "meow";
        }   
    }
    
    public class Dog : IAnimal
    {  
        public void Eat() {  }
    
        public string Speak()
        {
            return "au";
        }   
    }
    

    【讨论】:

      【解决方案4】:

      我在这个主题上的两分钱是你确实需要依赖抽象(即接口)而不是实现。

      顺便说一句,这不是太过分了吗?无需为对象模型中的任何类定义接口。通常,如果您需要接受某些满足给定合同的对象,您可以定义接口。

      例如,我不会定义 IAnimalICat 接口。可能我会定义一个抽象类Animal 和一个具体类Cat

      如果出于某种原因我需要在某些 API 中接受可以的生物,我会定义这样的接口:

      public interface IFeedable
      {
            void Feed(Food food);
      }
      

      如果一个生物会说话

      public interface ITalkative
      {
           void Talk(Food food);
      }
      

      除非没有动物独有的功能/属性/行为,否则我会按原样保留这些界面。

      public abstract class Animal : ITalkative, IFeedable
      {
           public Animal(AudioPlayer audioPlayer)
           {
                AudioPlayer = audioPlayer;
           }
      
           private AudioPlayer AudioPlayer { get; }
      
           public abstract void Feed(Food food);
      
           public void Talk()
           {
                 // Probably you would want to load an animal sound library
                 // here, and later pass the audio player with the sound library
                 // already loaded
                 OnTalk(AudioPlayer.LoadLibrary("animals"));
           }
      
           protected abstract void OnTalk(AudioLibrary audioLibrary);
      }
      
      public sealed class Cat : Animal
      {
            public Cat(AudioPlayer audioPlayer) : base(audioPlayer) 
            {
            }
      
            public override void Feed(Food food)
            {
                 if(food is Vegetable)
                 {
                      throw new NotSupportedException("MeeEEEEooW (=O ò.ó)=O!!");
                 }
                 else if(food is Meat)
                 {
                     // Proceed to eat this meat!
                 }
            }
      
            protected override void OnTalk(AudioLibrary audioLibrary)
            {
                  audioLibrary.Play("sweet-cat");
            }
      }
      

      如果你需要在某个地方制作一个物体来说话:

      ITalkative talkative = some as ITalkative;
      
      if(talkative != null)
      {
          talkative.Talk();
      }
      

      或者如果你需要喂对象:

      IFeedable feedable = some as IFeedable;
      
      if(feedable != null)
      {
          feedable.Feed(new Vegetable());
      }
      

      如您所见,您并没有为所有事物定义接口,而只是为您需要在某些 API 中处理的那些事情定义接口,您并不关心谁可以执行某些操作和/或拥有某些数据,但是您只关心对象可以分别做或暴露某些行为和数据。

      【讨论】:

        猜你喜欢
        • 2014-02-04
        • 2016-07-16
        • 1970-01-01
        • 2019-01-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-25
        相关资源
        最近更新 更多