• Effective C# Item19:定义并实现接口优于继承类型


        这个话题不仅仅是针对.NET的,在其他面向对象语言的环境中,例如Java,都会有接口和抽象类,对于究竟是选择接口还是抽象类,已经有了太多的讨论,包括设计模式中都有了一条设计原则:组合优于继承,虽然这条原则和我们要讨论话题没有太大联系,但是可以看出在这方面如何做出选择,并没有一个万能的解决方案,一般都要见招拆招,具体问题具体分析。

       抽象类一般是作为一个类层次结构的顶端,它一般有两个用途:1)定义逻辑骨架;2)抽取共通方法。它为一组相关的类型提供了一个公用的抽象;而接口是一种按合约进行设计的方式,一个实现了接口的类型,必须要实现接口中定义的方法。

       继承一种“is a”的关系,而接口实现则是一种“behave as”的关系,抽象类描述了对象是什么,而接口描述了对象的行为方式。

        在接口中,不能包含实现,也不能包含任何具体的数据成员,接口是在声明一种合约,所有实现接口的类型都要负责履行其中的约定。

        抽象类可以为派生类型提供一些具体的实现,我们可以指定数据成员、具体的方法、虚方法的实现、属性、事件以及索引器。它还可以实现一些具体的方法,因此可以为子类提供一些共通的可重用代码。抽象类可以为任何具体的行为提供一个实现,在这方面,接口是不可以的。

        在抽象基类和接口之间做选择,实际上是一个如何随着时间的推移而更好的支持抽象的问题。接口的特点是比较稳定:我们将一组功能封装在接口中,作为其他类型的实现合约;而基类则可以随着时间的推移进行扩展,这些扩展会成为每一个子类的一部分。

        C#中,我们可以混合使用抽象类和接口,可以继承一个类,然后实现多个接口。

        面向对象中的一个设计原则是面向接口编程,因此无论对于接口,还是抽象类,在传递参数的过程中,使用抽象类型或者接口类型,会比直接使用派生类型或者实现接口的类型要好,在扩展的空间。

        当我们在传递参数的过程中,使用类作为传递方式,那么我们实际上是将整个类的接口暴露给外界,通过使用接口,我们可以选择只提供哪些期望给用户的方法和属性,用来实现接口的类属于实现细节,它会随着时间的推移而发生改变。(关于这一点,我个人感觉主要是由于.NET中单继承、多接口的机制造成的,如果是单继承、单接口的机制,那么即使是在传值过程中使用接口,也会将接口的方法暴露给外界)。

       我们来看下面的代码。

    代码
    1 public interface InterfaceA
    2 {
    3 bool MethodA();
    4 }
    5
    6 public interface InterfaceB
    7 {
    8 bool MethodB();
    9 }
    10
    11 public abstract class BaseClass
    12 {
    13 public abstract void AbstractMethodA();
    14 }
    15
    16 public class DerivedClass : BaseClass, InterfaceA, InterfaceB
    17 {
    18 public override void AbstractMethodA()
    19 {
    20 throw new NotImplementedException();
    21 }
    22
    23 #region InterfaceA Members
    24
    25 public bool MethodA()
    26 {
    27 throw new NotImplementedException();
    28 }
    29
    30 #endregion
    31
    32 #region InterfaceB Members
    33
    34 public bool MethodB()
    35 {
    36 throw new NotImplementedException();
    37 }
    38
    39 #endregion
    40 }
       上述代码定义了两个接口、一个抽象类和一个继承自抽象类实现了两个接口的具体类。关于传值过程中使用类和使用接口的区别,我们可以看以下的代码。

    代码
    1 private static void TestWithInterface(InterfaceA interfaceA)
    2 {
    3 interfaceA.MethodA();
    4 }
    5
    6 private static void TestWithAbstractClass(BaseClass baseClass)
    7 {
    8 baseClass.AbstractMethodA();
    9 }
    10
    11 private static void TestWithClass(DerivedClass derivedClass)
    12 {
    13 derivedClass.MethodA();
    14 derivedClass.MethodB();
    15 derivedClass.AbstractMethodA();
    16 }
        我们定义了三个用于测试的方法,这三个方法在传值时,分别使用接口、抽象类和具体类,我们可以看到使用接口和抽象类传递参数,都是只能访问接口或者抽象类中定义好的方法,而使用具体类传递参数,它既可以访问接口中的方法,也可以访问抽象类中的方法。

       因此,如果我们需要向调用方只公开一部分功能,那么我们应该使用抽象类或者接口的方式进行传值。

       总结:基类描述并实现了一组相关类型间共通的行为,接口则描述了一组比较紧密关联的功能, 供其他不相关的具体类型来实现。这两者都有自己的适用范围,类定义了我们要创建的类型,接口则以功能分组的形式描述了那些类型的行为。如果理解好而这之间的差别,我们便可以创建更富表现力、更能应对变化的设计。应该使用类层次来定义相关的类型,然后让它们实现不同的接口,以便通过接口向外界提供功能。

  • 相关阅读:
    查看python关键字
    命令终端执行python
    Codeforces-462C. A Twisty Movement
    Codeforces-462A. A Compatible Pair
    Codeforces-446C. Pride
    Codeforces-Hello 2018C. Party Lemonade(贪心)
    Codeforces-33C. Wonderful Randomized Sum
    Codeforces-118D. Caesar's Legions(lazy dynamics)
    codeforces-73C. LionAge II
    Gym 101510C-Computer Science
  • 原文地址:https://www.cnblogs.com/wing011203/p/1648896.html
Copyright © 2020-2023  润新知