Interface / Abstract Class Coding Standard

后端 未结 5 786
礼貌的吻别
礼貌的吻别 2021-02-09 06:01

I spotted a proposed C# coding-standard which stated \"Try to offer an interface with all abstract classes\". Does someone know the rationale for this?

相关标签:
5条回答
  • 2021-02-09 06:09

    I think it's premature to say whether an interface is needed or not in a general sense. So, I think we should not put "Try to offer an interface with all abstract classes" as a coding standard unless that coding standard includes more details on when this rule applies.

    If I am not going to use the interface at all, am I still required to define an interface just to fulfill the coding standard?

    0 讨论(0)
  • 2021-02-09 06:18

    Without looking at the original article, I would guess that the original author is suggesting it for testability and allow easy mocking of the class with tools like MoQ, RhinoMocks etc.

    0 讨论(0)
  • 2021-02-09 06:21

    I always understood Interface-Driven Design (IDD) to involve the following steps in creating a concrete class (in its purest form, for non-trivial classes):

    1. Create an interface to describe the properties and behaviours your objects must exhibit, but not how those should function.
    2. Create an abstract base class as the primary implementation of the interface. Implement any functionality required by the interface, but which is unlikely to differ between concrete implementations. Also provide appropriate default (virtual) implementations for members which are unlikely (but possible) to change. You can also provide appropriate constructors (something not possible at the interface level). Mark all other interface members as abstract.
    3. Create your concrete class from the abstract class, overriding a subset of the members originally defined by the interface.

    The above process, while long-winded, ensures maximum adherence to the contract you initially lay down, while minimising redundant code in alternative implementations.

    This is why I would generally pair an abstract class with an interface.

    0 讨论(0)
  • 2021-02-09 06:30

    The .NET Framework Design Guidelines have some interesting things to say about interfaces and abstract classes.

    In particular, they note that interfaces have the major drawback of being less flexible than classes when it comes to the evolution of an API. Once you ship an interface, its members are fixed forever, and any additions will break compatibility with the existing types that implement that interface. Whereas, shipping a class offers much more flexibility. Members can be added at any time, even after the initial version has shipped, as long as they are not abstract. Any existing derived classes can continue to work unchanged. The System.IO.Stream abstract class provided in the Framework is given as an example. It initially shipped without support for timing out pending I/O operations, but version 2.0 was able to add members that supported this feature, even from existing subclasses.

    Thus, having a corresponding interface for each abstract base class provides few additional benefits. The interface cannot be publically exposed, or you're left back at square one in terms of versioning. And if you only expose the abstract base class, there's little gained by having the interface in the first place.

    Additionally, the point is often made in favor of interfaces that they allow separating contract from implementation. Krzysztof Cwalina argues that this claim is specious: it incorrectly assumes you cannot separate contracts from implementation using classes. By writing abstract classes that reside in a separate assembly from their concrete implementations, it's easy to achieve the same virtues of separation. He writes:

    I often hear people saying that interfaces specify contracts. I believe this is a dangerous myth. Interfaces, by themselves, do not specify much beyond the syntax required to use an object. The interface-as-contract myth causes people to do the wrong thing when trying to separate contracts from implementation, which is a great engineering practice. Interfaces separate syntax from implementation, which is not that useful, and the myth provides a false sense of doing the right engineering. In reality, the contract is semantics, and these can actually be nicely expressed with some implementation.

    In general, the guideline provided is DO favor defining classes over interfaces. Again, Krzysztof comments:

    Over the course of the three versions of the .NET Framework, I have talked about this guideline with quite a few developers on our team. Many of them, including those who initially disagreed with the guideline, have said that they regret having shipped some API as an interface. I have not heard of even one case in which somebody regretted that they shipped a class.

    A second guideline argues that one DO use abstract classes instead of interfaces to decouple the contract from implementations. The point here is that correctly-designed abstract classes still allow for the same degree of decoupling between contract and implementation as interfaces. Brian Pepin's personal perspective is thus:

    One thing I've started doing is to actually bake as much contract into my abstract class as possible. For example, I might want to have four overloads to a method where each overload offers an increasingly complex set of parameters. The best way to do this is to provide a nonvirtual implementation of these methods on the abstract class, and have the implementations all route to a protected abstract method that provides the actual implementation. By doing this, you can write all the boring argument-checking logic once. Developers who want to implement your class will thank you.

    Perhaps one would do best by revisiting the oft-touted "rule" that a derived class indicates an IS-A relationship with the base class, whereas a class implementing an interface has a CAN-DO relationship with that interface. To make the claim one should always code both an interface and an abstract base class, independent of specific reasons to do so, seems to miss the point.

    0 讨论(0)
  • 2021-02-09 06:35

    Test-driven development (TDD) is one key reason why you would want to do this. If you have a class that depends directly on your abstract class you cannot test it without writing a subclass that can be instantiated in your unit tests. If, however, your dependent class only depends on an interface then you can provide an 'instance' of this easily using a mocking framework such as Rhino Mocks, NMock, etc.

    Ultimately I think it's going to be down to how you ship your product. We only ever ship binaries and customers never extend our work. Internally we have interfaces for pretty much everything so classes can be isolated completely for unit testing. This offers huge benefits for refactoring and regression testing!

    EDIT: updated with example

    Consider following code in a unit test:

    // doesn't work - can't instantiate BaseClass directly
    var target = new ClassForTesting(new BaseClass());      
    
    // where we only rely on interface can easily generate mock in our tests
    var targetWithInterface = new ClassForTestingWithInterface(MockRepository.GenerateStub<ISomeInterface>());
    

    where the abstract class version is:

    // dependent class using an abstract class
    public abstract class BaseClass
    {
         public abstract void SomeMethod();
    }
    
    public class ClassForTesting
    {
        public BaseClass SomeMember { get; private set; }
    
        public ClassForTesting(BaseClass baseClass)
        {
            if (baseClass == null) throw new ArgumentNullException("baseClass");
            SomeMember = baseClass;
        }
    }
    

    and the same stuff but using interface is:

    public interface ISomeInterface
    {
        void SomeMethod();
    }
    
    public abstract class BaseClassWithInterface : ISomeInterface
    {
        public abstract void SomeMethod();
    }
    
    public class ClassForTestingWithInterface
    {
        public ISomeInterface SomeMember { get; private set; }
    
        public ClassForTestingWithInterface(ISomeInterface baseClass) {...}
    }
    
    0 讨论(0)
提交回复
热议问题