Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 何时使用接口而不是抽象类,反之亦然?_Oop_Inheritance_Interface_Abstract Class - Fatal编程技术网

Oop 何时使用接口而不是抽象类,反之亦然?

Oop 何时使用接口而不是抽象类,反之亦然?,oop,inheritance,interface,abstract-class,Oop,Inheritance,Interface,Abstract Class,这可能是一个通用的OOP问题。我想根据接口和抽象类的用法对它们进行一般性比较 什么时候想使用接口,什么时候想使用抽象类?如果你想提供一些基本的实现,那么就使用抽象类。在java中,你可以从一个(抽象)类继承来“提供”功能,你可以实现许多接口来“确保”功能抽象类可以具有共享状态或功能。接口只是提供状态或功能的承诺。一个好的抽象类将减少必须重写的代码量,因为它的功能或状态可以共享。接口没有要共享的定义信息这可能是一个非常困难的调用 我可以给出一个指针:一个对象可以实现很多接口,而对象只能继承一个基类

这可能是一个通用的OOP问题。我想根据接口和抽象类的用法对它们进行一般性比较


什么时候想使用接口,什么时候想使用抽象类?

如果你想提供一些基本的实现,那么就使用抽象类。

在java中,你可以从一个(抽象)类继承来“提供”功能,你可以实现许多接口来“确保”功能

抽象类可以具有共享状态或功能。接口只是提供状态或功能的承诺。一个好的抽象类将减少必须重写的代码量,因为它的功能或状态可以共享。接口没有要共享的定义信息

这可能是一个非常困难的调用


我可以给出一个指针:一个对象可以实现很多接口,而对象只能继承一个基类(在现代的OO语言中,像C++一样,我知道C++有多重继承性,但这不是皱眉吗?)

< P>抽象类可以实现。 接口没有实现,它只是定义了一种契约


也可能存在一些与语言相关的差异:例如,C#没有多重继承,但可以在一个类中实现多个接口。

答案因语言而异。例如,在Java中,一个类可以实现(继承)多个接口,但只能继承一个抽象类。因此,接口为您提供了更大的灵活性。但是,在C++中,这不是真的。

< P>纯基于继承,你将使用一个抽象的概念,在这里你定义了清晰的子代,抽象的关系(即,动物-CAT)和/或需要继承虚拟或非公共属性,特别是共享状态(接口不能支持)。
您应该尽可能地支持组合(通过依赖项注入)而不是继承,并且注意,作为契约的接口支持单元测试、关注点分离和(语言变化)多重继承,这是抽象所不能做到的。

我写了一篇关于这方面的文章:

总结:

当我们谈论抽象类时,我们定义的是对象类型的特征;指定对象是什么


当我们谈论一个接口并定义我们承诺提供的功能时,我们谈论的是建立一个关于对象可以做什么的契约。

就我个人而言,我几乎从来都不需要编写抽象类

大多数时候我看到抽象类被(mis)使用,这是因为抽象类的作者使用的是“模板方法”模式

“模板方法”的问题在于,它几乎总是有点可重入性,“派生”类不仅知道它正在实现的基类的“抽象”方法,还知道基类的公共方法,尽管大多数时候它不需要调用它们

(过于简化)例如:

因此,在这里,这个类的作者编写了一个通用算法,并打算让人们通过提供自己的“钩子”——在本例中是一个“比较”方法来“专门化”它

因此,预期用途如下:

class NameSorter : QuickSorter
{
    public bool compare(object lhs, object rhs)
    {
        // etc.
    }
}
问题在于,您不适当地将两个概念结合在一起:

  • 比较两个项目的一种方法(哪个项目应该先进行)
  • 对项目进行排序的方法(即快速排序与合并排序等)
  • 在上面的代码中,理论上,“compare”方法的作者可以重新调用超类“Sort”方法。。。即使在实践中,他们也永远不会想要或需要这样做

    为这种不必要的耦合付出的代价是很难更改超类,而且在大多数OO语言中,在运行时不可能更改它

    替代方法是使用“策略”设计模式:

    interface IComparator
    {
        bool compare(object lhs, object rhs);
    }
    
    class QuickSorter
    {
        private readonly IComparator comparator;
        public QuickSorter(IComparator comparator)
        {
            this.comparator = comparator;
        }
    
        public void Sort(object[] items)
        {
            // usual code but call comparator.Compare();
        }
    }
    
    class NameComparator : IComparator
    {
        bool compare(object lhs, object rhs)
        {
            // same code as before;
        }
    }
    
    现在请注意:我们只有接口,以及这些接口的具体实现。在实践中,您不需要任何其他东西来进行高级OO设计

    为了“隐藏”我们已经通过使用“快速排序”类和“NameComparator”实现了“名称排序”,我们仍然可以在某个地方编写一个工厂方法:

    ISorter CreateNameSorter()
    {
        return new QuickSorter(new NameComparator());
    }
    
    任何时候你有一个抽象类,你可以这样做。。。即使在基类和派生类之间存在自然的重新进入关系,使它们显式通常也是值得的


    最后一个想法:我们在上面所做的就是使用“快速排序”函数和“名称比较”函数“组合”一个“名称排序”函数。。。在函数式编程语言中,这种编程风格变得更加自然,代码更少。

    如果你头脑中有了清晰的概念,什么时候做一件非常简单的事情

    抽象类可以派生,而接口可以实现。这两者之间有些不同。派生抽象类时,派生类和基类之间的关系是“是”关系。e、 例如,狗是动物,羊是动物,这意味着派生类从基类继承了一些属性

    而对于接口的实现,关系是“可以”。e、 例如,狗可以是间谍狗。狗可以是马戏团的狗。一只狗可以是一只赛狗。这意味着你要用某些方法来获取某些东西


    我希望我能说清楚。

    一个有趣的地方是,当您需要向一组(相关或不相关的)对象添加额外的功能时,接口比抽象类更好。如果您不能给它们一个基本抽象类(例如,它们是
    密封的
    或已经有一个父类),您可以给它们一个虚拟(空)接口,然后简单地为该接口编写扩展方法<
    ISorter CreateNameSorter()
    {
        return new QuickSorter(new NameComparator());
    }
    
    public abstract class BaseAnimal
    {
        public int NumberOfLegs { get; set; }
    
        protected BaseAnimal(int numberOfLegs)
        {
            NumberOfLegs = numberOfLegs;
        }
    }
    
    public class Dog : BaseAnimal
    {
        public Dog() : base(4) { }
    }
    
    public class Human : BaseAnimal 
    {
        public Human() : base(2) { }
    }
    
    public static int CountAllLegs(List<BaseAnimal> animals)
    {
        int legCount = 0;
        foreach (BaseAnimal animal in animals)
        {
            legCount += animal.NumberOfLegs;
        }
        return legCount;
    }
    
    public interface IMakeSound
    {
        void MakeSound();
    }
    
    public class Car : IMakeSound
    {
        public void MakeSound() => Console.WriteLine("Vroom!");
    }
    
    public class Vuvuzela : IMakeSound
    {
        public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");        
    }
    
    List<IMakeSound> soundMakers = new List<ImakeSound>();
    soundMakers.Add(new Car());
    soundMakers.Add(new Vuvuzela());
    soundMakers.Add(new Car());
    soundMakers.Add(new Vuvuzela());
    soundMakers.Add(new Vuvuzela());
    
    foreach (IMakeSound soundMaker in soundMakers)
    {
        soundMaker.MakeSound();
    }
    
    public interface IMakeSound
    {
        void MakeSound();
    }
    
    public abstract class BaseAnimal : IMakeSound
    {
        public int NumberOfLegs { get; set; }
    
        protected BaseAnimal(int numberOfLegs)
        {
            NumberOfLegs = numberOfLegs;
        }
    
        public abstract void MakeSound();
    }
    
    public class Cat : BaseAnimal
    {
        public Cat() : base(4) { }
    
        public override void MakeSound() => Console.WriteLine("Meow!");
    }
    
    public class Human : BaseAnimal 
    {
        public Human() : base(2) { }
    
        public override void MakeSound() => Console.WriteLine("Hello, world!");
    }